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

Commit 3595b025 authored by David Gutierrez's avatar David Gutierrez Committed by Android (Google) Code Review
Browse files

Merge "Automatic timezone support on non-telephony devices"

parents b7855ee1 9ebb2fce
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -426,6 +426,8 @@
    <string name="date_time_auto">Set time automatically</string>
    <!-- Date & time setting screen setting switch title: whether the time zone should be determined automatically [CHAR LIMIT=100]  -->
    <string name="zone_auto_title">Set automatically</string>
    <!-- Date & time setting screen setting switch summary for non-telephony devices [CHAR LIMIT=100] -->
    <string name="auto_zone_requires_location_summary">Location will be used for setting the time zone when this toggle is on</string>
    <!-- Date & time setting screen setting option summary text for the automatic 24 hour setting checkbox [CHAR LIMIT=100] -->
    <string name="date_time_24hour_auto">Use locale default</string>
    <!-- Date & time setting screen setting check box title -->
@@ -2990,6 +2992,16 @@
    <!-- [CHAR LIMIT=60] Date&Time settings screen, toggle button title -->
    <string name="location_time_zone_detection_toggle_title">Use location</string>
    <!-- [CHAR LIMIT=50] Date&Time settings screen, title of the dialog when AutoTimeZone is degraded -->
    <string name="location_time_zone_detection_status_title">Cannot set the time zone automatically</string>
    <!-- Date&Time settings screen, summary of the dialog when AutoTimeZone is degraded by settings-->
    <string name="location_time_zone_detection_status_summary_degraded_by_settings" />
    <!-- [CHAR LIMIT=60] Date&Time settings screen, summary of the dialog when AutoTimeZone is blocked by settings -->
    <string name="location_time_zone_detection_status_summary_blocked_by_settings">Location or Location Services are off</string>
    <!-- Date&Time settings screen, summary of the dialog when AutoTimeZone is blocked by the environment-->
    <string name="location_time_zone_detection_status_summary_blocked_by_environment" />
    <!-- Date&Time settings screen, summary of the dialog when AutoTimeZone is temporarily unavailable-->
    <string name="location_time_zone_detection_status_summary_temporarily_unavailable" />
    <!-- [CHAR LIMIT=60] Date&Time settings screen, title of the dialog shown when user tries to
         enable GeoTZ when Location toggle is off. -->
    <string name="location_time_zone_detection_location_is_off_dialog_title">Device location needed</string>
@@ -2999,6 +3011,8 @@
    <!-- [CHAR LIMIT=30] Date&Time settings screen, button on a dialog shown when user tries to
    enable GeoTZ, but Location toggle is off, which leads to Location settings page. -->
    <string name="location_time_zone_detection_location_is_off_dialog_ok_button">Location settings</string>
    <!-- [CHAR LIMIT=30] Date&Time settings screen, button on a dialog shown when the LTZP needs to be fixed -->
    <string name="location_time_zone_provider_fix_dialog_ok_button">Fix this</string>
    <!-- [CHAR LIMIT=30] Date&Time settings screen, button on a dialog shown when user tries to
     enable GeoTZ, but Location toggle is off, which closes the dialog. -->
    <string name="location_time_zone_detection_location_is_off_dialog_cancel_button">Cancel</string>
+5 −0
Original line number Diff line number Diff line
@@ -47,6 +47,11 @@
            android:title="@string/zone_auto_title"
            settings:userRestriction="no_config_date_time"/>

        <com.android.settingslib.widget.BannerMessagePreference
            android:key="location_time_zone_detection_status"
            android:title="@string/location_time_zone_detection_status_title"
            settings:controller="com.android.settings.datetime.LocationProviderStatusPreferenceController"/>

        <!-- This preference gets removed if location-based time zone detection is not supported -->
        <SwitchPreference
            android:key="location_time_zone_detection"
+21 −0
Original line number Diff line number Diff line
@@ -29,8 +29,10 @@ import android.content.Context;

import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;

import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;

@@ -99,6 +101,25 @@ public class AutoTimeZonePreferenceController extends AbstractPreferenceControll
        return result;
    }

    @Override
    public CharSequence getSummary() {
        // If auto time zone cannot enable telephony fallback and is capable of location, then auto
        // time zone must use location.
        if (LocationProviderStatusPreferenceController.hasLocationTimeZoneNoTelephonyFallback(
                mTimeManager.getTimeZoneCapabilitiesAndConfig().getDetectorStatus())) {
            return mContext.getResources().getString(R.string.auto_zone_requires_location_summary);
        }
        // If the user has a dedicated toggle to control location use, the summary can
        // be empty because the use of location is explicit.
        return "";
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        refreshSummary(screen.findPreference(getPreferenceKey()));
    }

    @VisibleForTesting
    boolean isEnabled() {
        TimeZoneConfiguration config = getTimeZoneCapabilitiesAndConfig().getConfiguration();
+173 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.settings.datetime;

import android.app.time.DetectorStatusTypes;
import android.app.time.LocationTimeZoneAlgorithmStatus;
import android.app.time.TelephonyTimeZoneAlgorithmStatus;
import android.app.time.TimeManager;
import android.app.time.TimeZoneDetectorStatus;
import android.content.Context;
import android.location.LocationManager;
import android.service.timezone.TimeZoneProviderStatus;
import android.text.TextUtils;

import androidx.annotation.Nullable;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.location.LocationSettings;
import com.android.settingslib.widget.BannerMessagePreference;

import java.util.concurrent.Executor;

/**
 * The controller for the "location time zone detection" entry in the Location settings
 * screen.
 */
public class LocationProviderStatusPreferenceController
        extends BasePreferenceController implements TimeManager.TimeZoneDetectorListener {
    private final TimeManager mTimeManager;
    private final LocationManager mLocationManager;

    private BannerMessagePreference mPreference = null;

    public LocationProviderStatusPreferenceController(Context context, String preferenceKey) {
        super(context, preferenceKey);
        mTimeManager = context.getSystemService(TimeManager.class);
        mLocationManager = context.getSystemService(LocationManager.class);

        Executor mainExecutor = context.getMainExecutor();
        mTimeManager.addTimeZoneDetectorListener(mainExecutor, this);
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreference = screen.findPreference(getPreferenceKey());
        assert mPreference != null;
        mPreference
                .setPositiveButtonText(
                        R.string.location_time_zone_provider_fix_dialog_ok_button)
                .setPositiveButtonOnClickListener(v -> launchLocationSettings());
    }

    @Override
    public int getAvailabilityStatus() {
        // Checks that the summary is non-empty as most status strings are optional. If a status
        // string is empty, we ignore the status.
        if (!TextUtils.isEmpty(getSummary())) {
            return AVAILABLE_UNSEARCHABLE;
        }
        return CONDITIONALLY_UNAVAILABLE;
    }

    private void launchLocationSettings() {
        new SubSettingLauncher(mContext)
                .setDestination(LocationSettings.class.getName())
                .setSourceMetricsCategory(getMetricsCategory())
                .launch();
    }

    // Android has up to two location time zone providers (LTZPs) which can
    // (optionally) report their status along several dimensions. Typically there is
    // only one LTZP on a device, the primary. The UI here only reports status for one
    // LTZP. This UI logic prioritizes the primary if there is a "bad" status for both.
    @Nullable
    private TimeZoneProviderStatus getLtzpStatus() {
        LocationTimeZoneAlgorithmStatus status =
                mTimeManager.getTimeZoneCapabilitiesAndConfig().getDetectorStatus()
                        .getLocationTimeZoneAlgorithmStatus();
        TimeZoneProviderStatus primary = status.getPrimaryProviderReportedStatus();
        TimeZoneProviderStatus secondary = status.getSecondaryProviderReportedStatus();
        if (primary == null && secondary == null) {
            return null;
        }

        if (primary == null) {
            return secondary;
        } else if (secondary == null) {
            return primary;
        }

        if (status.getPrimaryProviderStatus()
                != LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN) {
            return secondary;
        }

        return primary;
    }

    @Override
    public void onChange() {
        if (mPreference != null) {
            mPreference.setVisible(getAvailabilityStatus() == AVAILABLE_UNSEARCHABLE);
            refreshSummary(mPreference);
        }
    }

    @Override
    public CharSequence getSummary() {
        boolean locationEnabled = mLocationManager.isLocationEnabled();
        final TimeZoneDetectorStatus detectorStatus =
                mTimeManager.getTimeZoneCapabilitiesAndConfig().getDetectorStatus();

        if (!locationEnabled && hasLocationTimeZoneNoTelephonyFallback(detectorStatus)) {
            return mContext.getResources().getString(
                    R.string.location_time_zone_detection_status_summary_blocked_by_settings);
        }

        TimeZoneProviderStatus ltzpStatus = getLtzpStatus();
        if (ltzpStatus == null) {
            return "";
        }

        int status = ltzpStatus.getLocationDetectionDependencyStatus();

        if (status == TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT) {
            return mContext.getResources().getString(
                    R.string.location_time_zone_detection_status_summary_blocked_by_environment);
        }
        if (status == TimeZoneProviderStatus.DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS) {
            return mContext.getResources().getString(
                    R.string.location_time_zone_detection_status_summary_degraded_by_settings);
        }
        if (status == TimeZoneProviderStatus.DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE) {
            return mContext.getResources().getString(
                    R.string.location_time_zone_detection_status_summary_temporarily_unavailable);
        }
        if (status == TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS) {
            return mContext.getResources().getString(
                    R.string.location_time_zone_detection_status_summary_blocked_by_settings);
        }

        return "";
    }

    /** package */
    static boolean hasLocationTimeZoneNoTelephonyFallback(TimeZoneDetectorStatus detectorStatus) {
        final LocationTimeZoneAlgorithmStatus locationStatus =
                detectorStatus.getLocationTimeZoneAlgorithmStatus();
        final TelephonyTimeZoneAlgorithmStatus telephonyStatus =
                detectorStatus.getTelephonyTimeZoneAlgorithmStatus();
        return telephonyStatus.getAlgorithmStatus()
                == DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED
                && locationStatus.getStatus()
                != DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED;
    }
}
+41 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.settings.datetime;

import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED;
import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING;
import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_RUNNING;
import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT;
@@ -36,10 +37,13 @@ import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
import android.app.time.TimeZoneDetectorStatus;
import android.content.Context;
import android.location.LocationManager;
import android.os.UserHandle;

import androidx.preference.Preference;

import com.android.settings.R;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,6 +62,8 @@ public class AutoTimeZonePreferenceControllerTest {
    private Preference mPreference;
    @Mock
    private TimeManager mTimeManager;
    @Mock
    private LocationManager mLocationManager;

    @Before
    public void setUp() {
@@ -67,6 +73,9 @@ public class AutoTimeZonePreferenceControllerTest {
        mPreference = new Preference(mContext);

        when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager);
        when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);

        when(mLocationManager.isLocationEnabled()).thenReturn(true);
    }

    @Test
@@ -221,10 +230,35 @@ public class AutoTimeZonePreferenceControllerTest {
        assertThat(controller.isEnabled()).isFalse();
    }

    @Test
    public void getSummary() {
        AutoTimeZonePreferenceController controller = new AutoTimeZonePreferenceController(
                mContext, mCallback, false /* fromSUW */);

        TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(
                /* autoSupported= */true, /* autoEnabled= */true, /* telephonySupported= */
                true);
        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
        when(mTimeManager.updateTimeZoneConfiguration(Mockito.any())).thenReturn(true);

        assertThat(controller.getSummary()).isEqualTo("");

        capabilitiesAndConfig = createCapabilitiesAndConfig(
                /* autoSupported= */true, /* autoEnabled= */true, /* telephonySupported= */
                false);
        when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
        when(mTimeManager.updateTimeZoneConfiguration(Mockito.any())).thenReturn(true);

        assertThat(controller.getSummary()).isEqualTo(
                mContext.getString(R.string.auto_zone_requires_location_summary));
    }

    private static TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig(
            boolean autoSupported, boolean autoEnabled) {
            boolean autoSupported, boolean autoEnabled, boolean telephonySupported) {
        TimeZoneDetectorStatus status = new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING,
                new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING),
                new TelephonyTimeZoneAlgorithmStatus(
                        telephonySupported ? DETECTION_ALGORITHM_STATUS_RUNNING
                                : DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED),
                new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING,
                        PROVIDER_STATUS_NOT_READY, null,
                        PROVIDER_STATUS_NOT_PRESENT, null));
@@ -242,4 +276,9 @@ public class AutoTimeZonePreferenceControllerTest {
                .build();
        return new TimeZoneCapabilitiesAndConfig(status, capabilities, config);
    }

    private static TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig(
            boolean autoSupported, boolean autoEnabled) {
        return createCapabilitiesAndConfig(autoSupported, autoEnabled, false);
    }
}
Loading