• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.settings.suggestions;
18 
19 import android.app.PendingIntent;
20 import android.car.drivingstate.CarUxRestrictions;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.os.Bundle;
24 import android.service.settings.suggestions.Suggestion;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 import androidx.loader.app.LoaderManager;
29 import androidx.loader.content.Loader;
30 import androidx.preference.Preference;
31 import androidx.preference.PreferenceGroup;
32 
33 import com.android.car.settings.common.FragmentController;
34 import com.android.car.settings.common.Logger;
35 import com.android.car.settings.common.PreferenceController;
36 import com.android.settingslib.suggestions.SuggestionController;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 
41 /**
42  * Injects {@link SuggestionPreference} instances loaded from the SuggestionService at the
43  * location in the hierarchy of the controller's placeholder preference. The placeholder should
44  * be a {@link PreferenceGroup} which sets the controller attribute to the fully qualified name
45  * of this class.
46  *
47  * <p>For example:
48  * <pre>{@code
49  * <PreferenceCategory
50  *     android:key="@string/pk_suggestions"
51  *     android:title="@string/suggestions_title"
52  *     settings:controller="com.android.settings.suggestions.SuggestionsPreferenceController"/>
53  * }</pre>
54  */
55 public class SuggestionsPreferenceController extends
56         PreferenceController<PreferenceGroup> implements
57         SuggestionController.ServiceConnectionListener,
58         LoaderManager.LoaderCallbacks<List<Suggestion>>, SuggestionPreference.Callback {
59 
60     private static final Logger LOG = new Logger(SuggestionsPreferenceController.class);
61 
62     // These values are hard coded until we receive the OK to plumb them through
63     // SettingsIntelligence. This is ok as right now only SUW uses this framework.
64     private static final ComponentName COMPONENT_NAME = new ComponentName(
65             "com.android.settings.intelligence",
66             "com.android.settings.intelligence.suggestions.SuggestionService");
67 
68     private final SuggestionController mSuggestionController;
69     private List<Suggestion> mSuggestionsList = new ArrayList<>();
70     private LoaderManager mLoaderManager;
71 
SuggestionsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)72     public SuggestionsPreferenceController(Context context, String preferenceKey,
73             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
74         super(context, preferenceKey, fragmentController, uxRestrictions);
75         mSuggestionController = new SuggestionController(context,
76                 COMPONENT_NAME, /* serviceConnectionListener= */ this);
77     }
78 
79     @Override
getPreferenceType()80     protected Class<PreferenceGroup> getPreferenceType() {
81         return PreferenceGroup.class;
82     }
83 
84     /**
85      * Sets the {@link LoaderManager} used to load suggestions.
86      */
setLoaderManager(LoaderManager loaderManager)87     public void setLoaderManager(LoaderManager loaderManager) {
88         mLoaderManager = loaderManager;
89     }
90 
91     /**
92      * Verifies that the controller was properly initialized with
93      * {@link #setLoaderManager(LoaderManager)}.
94      *
95      * @throws IllegalStateException if the loader manager is {@code null}
96      */
97     @Override
checkInitialized()98     protected void checkInitialized() {
99         LOG.v("checkInitialized");
100         if (mLoaderManager == null) {
101             throw new IllegalStateException(
102                     "SuggestionPreferenceController must be initialized by calling "
103                             + "setLoaderManager(LoaderManager)");
104         }
105     }
106 
107     /** Starts the suggestions controller. */
108     @Override
onStartInternal()109     protected void onStartInternal() {
110         LOG.v("onStartInternal");
111         mSuggestionController.start();
112     }
113 
114     /** Stops the suggestions controller. */
115     @Override
onStopInternal()116     protected void onStopInternal() {
117         LOG.v("onStopInternal");
118         mSuggestionController.stop();
119         cleanupLoader();
120     }
121 
122     @Override
onServiceConnected()123     public void onServiceConnected() {
124         LOG.v("onServiceConnected");
125         mLoaderManager.restartLoader(SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS, /* args= */
126                 null, /* callback= */ this);
127     }
128 
129     @Override
onServiceDisconnected()130     public void onServiceDisconnected() {
131         LOG.v("onServiceDisconnected");
132         cleanupLoader();
133     }
134 
135     @NonNull
136     @Override
onCreateLoader(int id, @Nullable Bundle args)137     public Loader<List<Suggestion>> onCreateLoader(int id, @Nullable Bundle args) {
138         LOG.v("onCreateLoader: " + id);
139         if (id == SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS) {
140             return new SettingsSuggestionsLoader(getContext(), mSuggestionController);
141         }
142         throw new IllegalArgumentException("This loader id is not supported " + id);
143     }
144 
145 
146     @Override
onLoadFinished(@onNull Loader<List<Suggestion>> loader, List<Suggestion> suggestions)147     public void onLoadFinished(@NonNull Loader<List<Suggestion>> loader,
148             List<Suggestion> suggestions) {
149         LOG.v("onLoadFinished");
150         if (suggestions == null) {
151             // Load started before the service was ready.
152             return;
153         }
154 
155         updateSuggestionPreferences(suggestions);
156         mSuggestionsList = new ArrayList<>(suggestions);
157     }
158 
updateSuggestionPreferences(List<Suggestion> suggestions)159     private void updateSuggestionPreferences(List<Suggestion> suggestions) {
160         // Remove suggestions that are not in the new list.
161         for (Suggestion oldSuggestion : mSuggestionsList) {
162             boolean isInNewSuggestionList = false;
163             for (Suggestion suggestion : suggestions) {
164                 if (oldSuggestion.getId().equals(suggestion.getId())) {
165                     isInNewSuggestionList = true;
166                     break;
167                 }
168             }
169             if (!isInNewSuggestionList) {
170                 getPreference().removePreference(
171                         getPreference().findPreference(getSuggestionPreferenceKey(oldSuggestion)));
172             }
173         }
174 
175         // Add suggestions that are not in the old list and update the existing suggestions.
176         for (Suggestion suggestion : suggestions) {
177             Preference curPref = getPreference().findPreference(
178                     getSuggestionPreferenceKey(suggestion));
179             if (curPref == null) {
180                 SuggestionPreference newSuggPref = new SuggestionPreference(getContext(),
181                         suggestion, /* callback= */ this);
182                 getPreference().addPreference(newSuggPref);
183             } else {
184                 ((SuggestionPreference) curPref).updateSuggestion(suggestion);
185             }
186         }
187 
188         refreshUi();
189     }
190 
191     @Override
onLoaderReset(@onNull Loader<List<Suggestion>> loader)192     public void onLoaderReset(@NonNull Loader<List<Suggestion>> loader) {
193         LOG.v("onLoaderReset");
194     }
195 
196     @Override
launchSuggestion(SuggestionPreference preference)197     public void launchSuggestion(SuggestionPreference preference) {
198         LOG.v("launchSuggestion");
199         Suggestion suggestion = preference.getSuggestion();
200         try {
201             if (suggestion.getPendingIntent() != null) {
202                 suggestion.getPendingIntent().send();
203                 mSuggestionController.launchSuggestion(suggestion);
204             } else {
205                 LOG.w("Suggestion with null pending intent " + suggestion.getId());
206             }
207         } catch (PendingIntent.CanceledException e) {
208             LOG.w("Failed to start suggestion " + suggestion.getId());
209         }
210     }
211 
212     @Override
dismissSuggestion(SuggestionPreference preference)213     public void dismissSuggestion(SuggestionPreference preference) {
214         LOG.v("dismissSuggestion");
215         Suggestion suggestion = preference.getSuggestion();
216         mSuggestionController.dismissSuggestions(suggestion);
217         mSuggestionsList.remove(suggestion);
218         getPreference().removePreference(preference);
219         refreshUi();
220     }
221 
222     @Override
updateState(PreferenceGroup preference)223     protected void updateState(PreferenceGroup preference) {
224         preference.setVisible(preference.getPreferenceCount() > 0);
225     }
226 
cleanupLoader()227     private void cleanupLoader() {
228         LOG.v("cleanupLoader");
229         mLoaderManager.destroyLoader(SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS);
230     }
231 
getSuggestionPreferenceKey(Suggestion suggestion)232     private String getSuggestionPreferenceKey(Suggestion suggestion) {
233         return SuggestionPreference.SUGGESTION_PREFERENCE_KEY + suggestion.getId();
234     }
235 }
236