| /* |
| * 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 static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK; |
| |
| import android.app.time.DetectorStatusTypes; |
| import android.app.time.LocationTimeZoneAlgorithmStatus; |
| import android.app.time.TelephonyTimeZoneAlgorithmStatus; |
| import android.app.time.TimeManager; |
| import android.app.time.TimeZoneCapabilities; |
| import android.app.time.TimeZoneCapabilitiesAndConfig; |
| import android.app.time.TimeZoneDetectorStatus; |
| import android.content.Context; |
| import android.service.timezone.TimeZoneProviderStatus; |
| import android.service.timezone.TimeZoneProviderStatus.DependencyStatus; |
| 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 BannerMessagePreference mPreference = null; |
| |
| public LocationProviderStatusPreferenceController(Context context, String preferenceKey) { |
| super(context, preferenceKey); |
| mTimeManager = context.getSystemService(TimeManager.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 getLtzpStatusToReport() { |
| LocationTimeZoneAlgorithmStatus status = |
| mTimeManager.getTimeZoneCapabilitiesAndConfig().getDetectorStatus() |
| .getLocationTimeZoneAlgorithmStatus(); |
| @Nullable TimeZoneProviderStatus primary = status.getPrimaryProviderReportedStatus(); |
| @Nullable TimeZoneProviderStatus secondary = status.getSecondaryProviderReportedStatus(); |
| if (primary != null && secondary != null) { |
| return pickWorstLtzpStatus(primary, secondary); |
| } else if (primary != null) { |
| return primary; |
| } else { |
| return secondary; |
| } |
| } |
| |
| private static TimeZoneProviderStatus pickWorstLtzpStatus( |
| TimeZoneProviderStatus primary, TimeZoneProviderStatus secondary) { |
| int primaryScore = scoreLtzpStatus(primary); |
| int secondaryScore = scoreLtzpStatus(secondary); |
| return primaryScore >= secondaryScore ? primary : secondary; |
| } |
| |
| private static int scoreLtzpStatus(TimeZoneProviderStatus providerStatus) { |
| @DependencyStatus int locationStatus = |
| providerStatus.getLocationDetectionDependencyStatus(); |
| if (locationStatus <= DEPENDENCY_STATUS_OK) { |
| return 0; |
| } |
| // The enum values currently correspond well to severity. |
| return providerStatus.getLocationDetectionDependencyStatus(); |
| } |
| |
| @Override |
| public void onChange() { |
| if (mPreference != null) { |
| mPreference.setVisible(getAvailabilityStatus() == AVAILABLE_UNSEARCHABLE); |
| refreshSummary(mPreference); |
| } |
| } |
| |
| @Override |
| public CharSequence getSummary() { |
| final TimeZoneCapabilitiesAndConfig timeZoneCapabilitiesAndConfig = |
| mTimeManager.getTimeZoneCapabilitiesAndConfig(); |
| final TimeZoneDetectorStatus detectorStatus = |
| timeZoneCapabilitiesAndConfig.getDetectorStatus(); |
| final TimeZoneCapabilities timeZoneCapabilities = |
| timeZoneCapabilitiesAndConfig.getCapabilities(); |
| |
| if (!timeZoneCapabilities.isUseLocationEnabled() |
| && hasLocationTimeZoneNoTelephonyFallback(detectorStatus)) { |
| return mContext.getString( |
| R.string.location_time_zone_detection_status_summary_blocked_by_settings); |
| } |
| |
| TimeZoneProviderStatus ltzpStatus = getLtzpStatusToReport(); |
| if (ltzpStatus == null) { |
| return ""; |
| } |
| |
| @DependencyStatus int locationStatus = ltzpStatus.getLocationDetectionDependencyStatus(); |
| |
| if (locationStatus == TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS) { |
| return mContext.getString( |
| R.string.location_time_zone_detection_status_summary_blocked_by_settings); |
| } |
| if (locationStatus == TimeZoneProviderStatus.DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS) { |
| return mContext.getString( |
| R.string.location_time_zone_detection_status_summary_degraded_by_settings); |
| } |
| if (locationStatus == TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT) { |
| return mContext.getString( |
| R.string.location_time_zone_detection_status_summary_blocked_by_environment); |
| } |
| if (locationStatus == TimeZoneProviderStatus.DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE) { |
| return mContext.getString( |
| R.string.location_time_zone_detection_status_summary_temporarily_unavailable); |
| } |
| |
| // LTZP-reported network connectivity and time zone resolution statuses are currently |
| // ignored. Partners can tweak this logic if they also want to report these to users. |
| |
| 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; |
| } |
| } |