/* * Copyright (C) 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.suggestions; import android.app.PendingIntent; import android.car.drivingstate.CarUxRestrictions; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; import android.service.settings.suggestions.Suggestion; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import com.android.car.settings.common.FragmentController; import com.android.car.settings.common.Logger; import com.android.car.settings.common.PreferenceController; import com.android.settingslib.suggestions.SuggestionController; import java.util.ArrayList; import java.util.List; /** * Injects {@link SuggestionPreference} instances loaded from the SuggestionService at the * location in the hierarchy of the controller's placeholder preference. The placeholder should * be a {@link PreferenceGroup} which sets the controller attribute to the fully qualified name * of this class. * *

For example: *

{@code
 * 
 * }
*/ public class SuggestionsPreferenceController extends PreferenceController implements SuggestionController.ServiceConnectionListener, LoaderManager.LoaderCallbacks>, SuggestionPreference.Callback { private static final Logger LOG = new Logger(SuggestionsPreferenceController.class); // These values are hard coded until we receive the OK to plumb them through // SettingsIntelligence. This is ok as right now only SUW uses this framework. private static final ComponentName COMPONENT_NAME = new ComponentName( "com.android.settings.intelligence", "com.android.settings.intelligence.suggestions.SuggestionService"); private final SuggestionController mSuggestionController; private List mSuggestionsList = new ArrayList<>(); private LoaderManager mLoaderManager; public SuggestionsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions) { super(context, preferenceKey, fragmentController, uxRestrictions); mSuggestionController = new SuggestionController(context, COMPONENT_NAME, /* serviceConnectionListener= */ this); } @Override protected Class getPreferenceType() { return PreferenceGroup.class; } /** * Sets the {@link LoaderManager} used to load suggestions. */ public void setLoaderManager(LoaderManager loaderManager) { mLoaderManager = loaderManager; } /** * Verifies that the controller was properly initialized with * {@link #setLoaderManager(LoaderManager)}. * * @throws IllegalStateException if the loader manager is {@code null} */ @Override protected void checkInitialized() { LOG.v("checkInitialized"); if (mLoaderManager == null) { throw new IllegalStateException( "SuggestionPreferenceController must be initialized by calling " + "setLoaderManager(LoaderManager)"); } } /** Starts the suggestions controller. */ @Override protected void onStartInternal() { LOG.v("onStartInternal"); mSuggestionController.start(); } /** Stops the suggestions controller. */ @Override protected void onStopInternal() { LOG.v("onStopInternal"); mSuggestionController.stop(); cleanupLoader(); } @Override public void onServiceConnected() { LOG.v("onServiceConnected"); mLoaderManager.restartLoader(SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS, /* args= */ null, /* callback= */ this); } @Override public void onServiceDisconnected() { LOG.v("onServiceDisconnected"); cleanupLoader(); } @NonNull @Override public Loader> onCreateLoader(int id, @Nullable Bundle args) { LOG.v("onCreateLoader: " + id); if (id == SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS) { return new SettingsSuggestionsLoader(getContext(), mSuggestionController); } throw new IllegalArgumentException("This loader id is not supported " + id); } @Override public void onLoadFinished(@NonNull Loader> loader, List suggestions) { LOG.v("onLoadFinished"); if (suggestions == null) { // Load started before the service was ready. return; } updateSuggestionPreferences(suggestions); mSuggestionsList = new ArrayList<>(suggestions); } private void updateSuggestionPreferences(List suggestions) { // Remove suggestions that are not in the new list. for (Suggestion oldSuggestion : mSuggestionsList) { boolean isInNewSuggestionList = false; for (Suggestion suggestion : suggestions) { if (oldSuggestion.getId().equals(suggestion.getId())) { isInNewSuggestionList = true; break; } } if (!isInNewSuggestionList) { getPreference().removePreference( getPreference().findPreference(getSuggestionPreferenceKey(oldSuggestion))); } } // Add suggestions that are not in the old list and update the existing suggestions. for (Suggestion suggestion : suggestions) { Preference curPref = getPreference().findPreference( getSuggestionPreferenceKey(suggestion)); if (curPref == null) { SuggestionPreference newSuggPref = new SuggestionPreference(getContext(), suggestion, /* callback= */ this); getPreference().addPreference(newSuggPref); } else { ((SuggestionPreference) curPref).updateSuggestion(suggestion); } } refreshUi(); } @Override public void onLoaderReset(@NonNull Loader> loader) { LOG.v("onLoaderReset"); } @Override public void launchSuggestion(SuggestionPreference preference) { LOG.v("launchSuggestion"); Suggestion suggestion = preference.getSuggestion(); try { if (suggestion.getPendingIntent() != null) { suggestion.getPendingIntent().send(); mSuggestionController.launchSuggestion(suggestion); } else { LOG.w("Suggestion with null pending intent " + suggestion.getId()); } } catch (PendingIntent.CanceledException e) { LOG.w("Failed to start suggestion " + suggestion.getId()); } } @Override public void dismissSuggestion(SuggestionPreference preference) { LOG.v("dismissSuggestion"); Suggestion suggestion = preference.getSuggestion(); mSuggestionController.dismissSuggestions(suggestion); mSuggestionsList.remove(suggestion); getPreference().removePreference(preference); refreshUi(); } @Override protected void updateState(PreferenceGroup preference) { preference.setVisible(preference.getPreferenceCount() > 0); } private void cleanupLoader() { LOG.v("cleanupLoader"); mLoaderManager.destroyLoader(SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS); } private String getSuggestionPreferenceKey(Suggestion suggestion) { return SuggestionPreference.SUGGESTION_PREFERENCE_KEY + suggestion.getId(); } }