/* * Copyright 2018 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.car.settings.common; import static com.android.car.settings.common.PreferenceXmlParser.PREF_AVAILABILITY_STATUS_HIDDEN; import static com.android.car.settings.common.PreferenceXmlParser.PREF_AVAILABILITY_STATUS_READ; import android.car.CarOccupantZoneManager; import android.car.drivingstate.CarUxRestrictions; import android.car.drivingstate.CarUxRestrictionsManager.OnUxRestrictionsChangedListener; import android.content.Context; import android.os.SystemClock; import android.widget.Toast; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import com.android.car.settings.CarSettingsApplication; import com.android.car.settings.R; import com.android.car.ui.preference.ClickableWhileDisabledPreference; import com.android.car.ui.preference.UxRestrictablePreference; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; /** * Controller which encapsulates the business logic associated with a {@link Preference}. All car * settings controllers should extend this class. * *
Controllers are responsible for populating and modifying the presentation of an associated * preference while responding to changes in system state. This is enabled via {@link * SettingsFragment} which registers controllers as observers on its lifecycle and dispatches * {@link CarUxRestrictions} change events to the controllers via the {@link * OnUxRestrictionsChangedListener} interface. * *
Controllers should be instantiated from XML. To do so, define a preference and include the * {@code controller} attribute in the preference tag and assign the fully qualified class name. * *
For example: *
{@code ** ** }
Subclasses must implement {@link #getPreferenceType()} to define the upper bound type on the * {@link Preference} that the controller is associated with. For example, a bound of {@link * androidx.preference.PreferenceGroup} indicates that the controller will utilize preference group * methods in its operation. {@link #setPreference(Preference)} will throw an {@link * IllegalArgumentException} if not passed a subclass of the upper bound type. * *
Subclasses may implement any or all of the following methods (see method Javadocs for more * information): * *
Note: this will not be called on {@link #UNSUPPORTED_ON_DEVICE} controllers. */ protected void onCreateInternal() { } /** * Subclasses may override this method to complete any operations needed each time the * controller is started e.g. registering broadcast receivers. * *
Note: this will not be called on {@link #UNSUPPORTED_ON_DEVICE} controllers. */ protected void onStartInternal() { } /** * Subclasses may override this method to complete any operations needed each time the * controller is resumed. Prefer to use {@link #onStartInternal()} unless absolutely necessary * as controllers may not be resumed in a multi-display scenario. * *
Note: this will not be called on {@link #UNSUPPORTED_ON_DEVICE} controllers. */ protected void onResumeInternal() { } /** * Subclasses may override this method to complete any operations needed each time the * controller is paused. Prefer to use {@link #onStartInternal()} unless absolutely necessary * as controllers may not be resumed in a multi-display scenario. * *
Note: this will not be called on {@link #UNSUPPORTED_ON_DEVICE} controllers. */ protected void onPauseInternal() { } /** * Subclasses may override this method to complete any operations needed each time the * controller is stopped e.g. unregistering broadcast receivers. * *
Note: this will not be called on {@link #UNSUPPORTED_ON_DEVICE} controllers. */ protected void onStopInternal() { } /** * Subclasses may override this method to complete any operations needed when the controller is * destroyed e.g. freeing up held resources. * *
Note: this will not be called on {@link #UNSUPPORTED_ON_DEVICE} controllers. */ protected void onDestroyInternal() { } /** * Subclasses may override this method to update the presentation of the preference for the * current system state (summary, switch state, etc). If the preference has dynamic content * (such as preferences added to a group), it may be updated here as well. * *
Important: Operations should be idempotent as this may be called multiple times. * *
Note: this will only be called when the following are true: *
* It is not expected that subclasses will override this functionality. If they do, it is
* important to respect the config flags being consulted here.
*
* @return true if {@code uxRestrictions} should be applied and false otherwise.
*/
protected boolean shouldApplyUxRestrictions(CarUxRestrictions uxRestrictions) {
return !isUxRestrictionsIgnored(mAlwaysIgnoreUxRestrictions,
mPreferencesIgnoringUxRestrictions)
&& CarUxRestrictionsHelper.isNoSetup(uxRestrictions)
&& getAvailabilityStatus() != AVAILABLE_FOR_VIEWING;
}
/**
* Updates the UxRestricted state and action for a preference. This will also update all child
* preferences with the same state and action when {@param preference} is a PreferenceGroup.
*
* @param preference the preference to update
* @param restrict whether or not the preference should be restricted
*/
protected void restrictPreference(Preference preference, boolean restrict) {
if (preference instanceof UxRestrictablePreference) {
UxRestrictablePreference restrictablePreference = (UxRestrictablePreference) preference;
restrictablePreference.setUxRestricted(restrict);
restrictablePreference.setOnClickWhileRestrictedListener(p ->
Toast.makeText(mContext, mRestrictedWhileDrivingMessage,
Toast.LENGTH_LONG).show());
}
if (preference instanceof PreferenceGroup) {
PreferenceGroup preferenceGroup = (PreferenceGroup) preference;
for (int i = 0; i < preferenceGroup.getPreferenceCount(); i++) {
restrictPreference(preferenceGroup.getPreference(i), restrict);
}
}
}
/**
* Updates the clickable while disabled state and action for a preference. This will also
* update all child preferences with the same state and action when {@param preference}
* is a PreferenceGroup. If the preference is only available for viewing for the zone,
* this won't apply since an action will have already been assigned.
*
* @param preference the preference to update
* @param clickable whether or not the preference should be clickable when disabled
* @param disabledClickAction the action that should be taken when clicked while disabled.
*/
protected void setClickableWhileDisabled(Preference preference, boolean clickable,
@Nullable Consumer