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 package com.android.car.settings.suggestions; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.app.PendingIntent; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.graphics.drawable.Drawable; 24 import android.os.Bundle; 25 import android.service.settings.suggestions.Suggestion; 26 import android.support.annotation.StringRes; 27 import android.support.v4.app.LoaderManager; 28 import android.support.v4.content.Loader; 29 30 import com.android.car.list.TypedPagedListAdapter; 31 import com.android.car.settings.R; 32 import com.android.car.settings.common.Logger; 33 import com.android.settingslib.suggestions.SuggestionController; 34 import com.android.settingslib.suggestions.SuggestionControllerMixin; 35 import com.android.settingslib.suggestions.SuggestionLoader; 36 import com.android.settingslib.utils.IconCache; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 41 42 /** 43 * Retrieves suggestions and prepares them for rendering. 44 * Modeled after {@link SuggestionControllerMixin}, differs by implementing support library version 45 * of LoaderManager and Loader. Does not implement use of LifeCycle. 46 */ 47 public class SettingsSuggestionsController implements 48 SuggestionController.ServiceConnectionListener, 49 LoaderManager.LoaderCallbacks<List<Suggestion>> { 50 private static final Logger LOG = new Logger(SettingsSuggestionsController.class); 51 private static final ComponentName mComponentName = new ComponentName( 52 "com.android.settings.intelligence", 53 "com.android.settings.intelligence.suggestions.SuggestionService"); 54 // These values are hard coded until we receive the OK to plumb them through 55 // SettingsIntelligence. This is ok as right now only SUW uses this framework. 56 @StringRes 57 private static final int PRIMARY_ACTION_ID = R.string.suggestion_primary_button; 58 @StringRes 59 private static final int SECONDARY_ACTION_ID = R.string.suggestion_secondary_button; 60 61 private final Context mContext; 62 private final LoaderManager mLoaderManager; 63 private final Listener mListener; 64 private final SuggestionController mSuggestionController; 65 private final IconCache mIconCache; 66 67 public SettingsSuggestionsController( 68 Context context, 69 LoaderManager loaderManager, 70 @NonNull Listener listener) { 71 mContext = context; 72 mLoaderManager = loaderManager; 73 mListener = listener; 74 mIconCache = new IconCache(context); 75 mSuggestionController = new SuggestionController( 76 mContext, 77 mComponentName, 78 /* listener= */ this); 79 } 80 81 @Override 82 public void onServiceConnected() { 83 LOG.v("onServiceConnected"); 84 mLoaderManager.restartLoader( 85 SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS, 86 /* args= */ null, 87 /* callback= */ this); 88 } 89 90 @Override 91 public void onServiceDisconnected() { 92 LOG.v("onServiceDisconnected"); 93 cleanupLoader(); 94 } 95 96 @NonNull 97 @Override 98 public Loader<List<Suggestion>> onCreateLoader(int id, @Nullable Bundle args) { 99 LOG.v("onCreateLoader: " + id); 100 if (id == SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS) { 101 return new SettingsSuggestionsLoader(mContext, mSuggestionController); 102 } 103 throw new IllegalArgumentException("This loader id is not supported " + id); 104 } 105 106 @Override 107 public void onLoadFinished( 108 @NonNull Loader<List<Suggestion>> loader, 109 List<Suggestion> suggestionList) { 110 LOG.v("onLoadFinished"); 111 if (suggestionList == null) { 112 return; 113 } 114 ArrayList<TypedPagedListAdapter.LineItem> items = new ArrayList<>(); 115 for (final Suggestion suggestion : suggestionList) { 116 LOG.v("Suggestion ID: " + suggestion.getId()); 117 Drawable itemIcon = mIconCache.getIcon(suggestion.getIcon()); 118 SuggestionLineItem suggestionLineItem = 119 new SuggestionLineItem( 120 suggestion.getTitle(), 121 suggestion.getSummary(), 122 itemIcon, 123 mContext.getString(PRIMARY_ACTION_ID), 124 mContext.getString(SECONDARY_ACTION_ID), 125 v -> { 126 try { 127 suggestion.getPendingIntent().send(); 128 launchSuggestion(suggestion); 129 } catch (PendingIntent.CanceledException e) { 130 LOG.w("Failed to start suggestion " + suggestion.getTitle()); 131 } 132 }, 133 adapterPosition -> { 134 dismissSuggestion(suggestion); 135 mListener.onSuggestionDismissed(adapterPosition); 136 137 }); 138 items.add(suggestionLineItem); 139 } 140 mListener.onSuggestionsLoaded(items); 141 } 142 143 @Override 144 public void onLoaderReset(@NonNull Loader<List<Suggestion>> loader) { 145 LOG.v("onLoaderReset"); 146 } 147 148 /** 149 * Start the suggestions controller. 150 */ 151 public void start() { 152 LOG.v("Start"); 153 mSuggestionController.start(); 154 } 155 156 /** 157 * Stop the suggestions controller. 158 */ 159 public void stop() { 160 LOG.v("Stop"); 161 mSuggestionController.stop(); 162 cleanupLoader(); 163 164 } 165 166 private void cleanupLoader() { 167 LOG.v("cleanupLoader"); 168 mLoaderManager.destroyLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS); 169 } 170 171 private void dismissSuggestion(Suggestion suggestion) { 172 LOG.v("dismissSuggestion"); 173 mSuggestionController.dismissSuggestions(suggestion); 174 } 175 176 private void launchSuggestion(Suggestion suggestion) { 177 LOG.v("launchSuggestion"); 178 mSuggestionController.launchSuggestion(suggestion); 179 } 180 181 /** 182 * Listener interface to notify of data state changes and actions. 183 */ 184 public interface Listener { 185 /** 186 * Invoked when deferred setup items have been loaded. 187 * 188 * @param suggestions List of deferred setup suggestions. 189 */ 190 void onSuggestionsLoaded(@NonNull ArrayList<TypedPagedListAdapter.LineItem> suggestions); 191 192 /*** 193 * Invoked when a suggestion is dismissed. 194 * 195 * @param adapterPosition the position of the suggestion item in it's adapter. 196 */ 197 void onSuggestionDismissed(int adapterPosition); 198 } 199 } 200