blob: 1a8659036d66b00fd8c9848da266c3b4750c82b2 [file] [log] [blame]
/*
* 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.
*
* <p>For example:
* <pre>{@code
* <PreferenceCategory
* android:key="@string/pk_suggestions"
* android:title="@string/suggestions_title"
* settings:controller="com.android.settings.suggestions.SuggestionsPreferenceController"/>
* }</pre>
*/
public class SuggestionsPreferenceController extends
PreferenceController<PreferenceGroup> implements
SuggestionController.ServiceConnectionListener,
LoaderManager.LoaderCallbacks<List<Suggestion>>, 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<Suggestion> 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<PreferenceGroup> 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<List<Suggestion>> 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<List<Suggestion>> loader,
List<Suggestion> 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<Suggestion> 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<List<Suggestion>> 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();
}
}