Add basic settings suggestion support
Bug: 74248946
Test: Tested locally
Change-Id: I16daa6171a11a1eb5ff8da0e61c78546984f9f34
diff --git a/Android.mk b/Android.mk
index 587f874..5cbef4d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,6 +53,7 @@
include packages/apps/Car/libs/car-stream-ui-lib/car-stream-ui-lib.mk
include packages/apps/Car/libs/car-list/car-list.mk
include packages/apps/Car/libs/car-apps-common/car-apps-common.mk
+ include packages/apps/Car/libs/car-settings-lib/car-settings-lib.mk
include packages/services/Car/car-support-lib/car-support.mk
include frameworks/opt/setupwizard/library/common-gingerbread.mk
include frameworks/base/packages/SettingsLib/common.mk
diff --git a/src/com/android/car/settings/common/Logger.java b/src/com/android/car/settings/common/Logger.java
new file mode 100644
index 0000000..841aa90
--- /dev/null
+++ b/src/com/android/car/settings/common/Logger.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common;
+
+import com.android.car.settingslib.log.LoggerBase;
+
+/**
+ * Implementation of {@link LoggerBase} for CarSettings.
+ */
+public final class Logger extends LoggerBase {
+ public Logger(Class<?> cls) {
+ super(cls);
+ }
+
+ @Override
+ protected String getTag() {
+ return "CarSettings";
+ }
+}
diff --git a/src/com/android/car/settings/home/HomepageFragment.java b/src/com/android/car/settings/home/HomepageFragment.java
index bd9244e..e452921 100644
--- a/src/com/android/car/settings/home/HomepageFragment.java
+++ b/src/com/android/car/settings/home/HomepageFragment.java
@@ -25,19 +25,20 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Resources;
import android.os.Bundle;
-import android.util.DisplayMetrics;
import android.view.View;
import com.android.car.list.TypedPagedListAdapter;
import com.android.car.settings.R;
import com.android.car.settings.applications.ApplicationSettingsFragment;
import com.android.car.settings.common.ListSettingsFragment;
+import com.android.car.settings.common.Logger;
import com.android.car.settings.datetime.DatetimeSettingsFragment;
+import com.android.car.settings.suggestions.SettingsSuggestionsController;
import com.android.car.settings.display.DisplaySettingsFragment;
import com.android.car.settings.security.ChooseLockTypeFragment;
import com.android.car.settings.sound.SoundSettingsFragment;
+import com.android.car.settings.suggestions.SuggestionLineItem;
import com.android.car.settings.system.SystemSettingsFragment;
import com.android.car.settings.users.UsersListFragment;
import com.android.car.settings.wifi.CarWifiManager;
@@ -49,11 +50,16 @@
/**
* Homepage for settings for car.
*/
-public class HomepageFragment extends ListSettingsFragment implements CarWifiManager.Listener {
- private static final String TAG = "HomepageFragment";
+public class HomepageFragment extends ListSettingsFragment implements
+ CarWifiManager.Listener,
+ SettingsSuggestionsController.Listener {
+ private static final Logger LOG = new Logger(HomepageFragment.class);
+
+ private SettingsSuggestionsController mSettingsSuggestionsController;
private CarWifiManager mCarWifiManager;
private WifiLineItem mWifiLineItem;
private BluetoothLineItem mBluetoothLineItem;
+ private ArrayList<TypedPagedListAdapter.LineItem> mSettingsLineItems = new ArrayList<>();
private final BroadcastReceiver mBtStateReceiver = new BroadcastReceiver() {
@Override
@@ -89,6 +95,13 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
+ LOG.v("onActivityCreated: " + savedInstanceState);
+ mSettingsLineItems.clear();
+ mSettingsSuggestionsController =
+ new SettingsSuggestionsController(
+ getContext(),
+ getLoaderManager(),
+ this /* listener */);
mCarWifiManager = new CarWifiManager(getContext(), this /* listener */);
mWifiLineItem = new WifiLineItem(getContext(), mCarWifiManager, mFragmentController);
mBluetoothLineItem = new BluetoothLineItem(getContext(), mFragmentController);
@@ -113,20 +126,15 @@
public void onStart() {
super.onStart();
mCarWifiManager.start();
+ mSettingsSuggestionsController.start();
getActivity().registerReceiver(mBtStateReceiver, mBtStateChangeFilter);
}
- private static float convertPixelsToDp(float px, Context context){
- Resources resources = context.getResources();
- DisplayMetrics metrics = resources.getDisplayMetrics();
- float dp = px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
- return dp;
- }
-
@Override
public void onStop() {
super.onStop();
mCarWifiManager.stop();
+ mSettingsSuggestionsController.stop();
getActivity().unregisterReceiver(mBtStateReceiver);
}
@@ -138,56 +146,55 @@
@Override
public ArrayList<TypedPagedListAdapter.LineItem> getLineItems() {
- ArrayList<TypedPagedListAdapter.LineItem> lineItems = new ArrayList<>();
ExtraSettingsLoader extraSettingsLoader = new ExtraSettingsLoader(getContext());
Map<String, Collection<TypedPagedListAdapter.LineItem>> extraSettings =
extraSettingsLoader.load();
- lineItems.add(new SimpleIconTransitionLineItem(
+ mSettingsLineItems.add(new SimpleIconTransitionLineItem(
R.string.display_settings,
R.drawable.ic_settings_display,
getContext(),
null,
DisplaySettingsFragment.getInstance(),
mFragmentController));
- lineItems.add(new SimpleIconTransitionLineItem(
+ mSettingsLineItems.add(new SimpleIconTransitionLineItem(
R.string.sound_settings,
R.drawable.ic_settings_sound,
getContext(),
null,
SoundSettingsFragment.getInstance(),
mFragmentController));
- lineItems.add(mWifiLineItem);
- lineItems.addAll(extraSettings.get(WIRELESS_CATEGORY));
- lineItems.add(mBluetoothLineItem);
- lineItems.add(new SimpleIconTransitionLineItem(
+ mSettingsLineItems.add(mWifiLineItem);
+ mSettingsLineItems.addAll(extraSettings.get(WIRELESS_CATEGORY));
+ mSettingsLineItems.add(mBluetoothLineItem);
+ mSettingsLineItems.add(new SimpleIconTransitionLineItem(
R.string.applications_settings,
R.drawable.ic_settings_applications,
getContext(),
null,
ApplicationSettingsFragment.getInstance(),
mFragmentController));
- lineItems.add(new SimpleIconTransitionLineItem(
+ mSettingsLineItems.add(new SimpleIconTransitionLineItem(
R.string.date_and_time_settings_title,
R.drawable.ic_settings_date_time,
getContext(),
null,
DatetimeSettingsFragment.getInstance(),
mFragmentController));
- lineItems.add(new SimpleIconTransitionLineItem(
+ mSettingsLineItems.add(new SimpleIconTransitionLineItem(
R.string.user_and_account_settings_title,
R.drawable.ic_user,
getContext(),
null,
UsersListFragment.newInstance(),
mFragmentController));
- lineItems.add(new SimpleIconTransitionLineItem(
+ mSettingsLineItems.add(new SimpleIconTransitionLineItem(
R.string.security_settings_title,
R.drawable.ic_lock,
getContext(),
null,
ChooseLockTypeFragment.newInstance(),
mFragmentController));
- lineItems.add(new SimpleIconTransitionLineItem(
+ mSettingsLineItems.add(new SimpleIconTransitionLineItem(
R.string.system_setting_title,
R.drawable.ic_settings_about,
getContext(),
@@ -195,8 +202,15 @@
SystemSettingsFragment.getInstance(),
mFragmentController));
- lineItems.addAll(extraSettings.get(DEVICE_CATEGORY));
- lineItems.addAll(extraSettings.get(PERSONAL_CATEGORY));
- return lineItems;
+ mSettingsLineItems.addAll(extraSettings.get(DEVICE_CATEGORY));
+ mSettingsLineItems.addAll(extraSettings.get(PERSONAL_CATEGORY));
+ return mSettingsLineItems;
+ }
+
+ @Override
+ public void onSuggestionsLoaded(ArrayList<SuggestionLineItem> suggestions) {
+ LOG.v("onDeferredSuggestionsLoaded");
+ mSettingsLineItems.addAll(0, suggestions);
+ mPagedListAdapter.updateList(mSettingsLineItems);
}
}
diff --git a/src/com/android/car/settings/suggestions/SettingsSuggestionsController.java b/src/com/android/car/settings/suggestions/SettingsSuggestionsController.java
new file mode 100644
index 0000000..c06f9be
--- /dev/null
+++ b/src/com/android/car/settings/suggestions/SettingsSuggestionsController.java
@@ -0,0 +1,174 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.service.settings.suggestions.Suggestion;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+
+import com.android.car.settings.common.Logger;
+import com.android.settingslib.suggestions.SuggestionController;
+import com.android.settingslib.suggestions.SuggestionControllerMixin;
+import com.android.settingslib.suggestions.SuggestionLoader;
+import com.android.settingslib.utils.IconCache;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Retrieves suggestions and prepares them for rendering.
+ * Modeled after {@link SuggestionControllerMixin}, differs by implementing support library version
+ * of LoaderManager and Loader. Does not implement use of LifeCycle.
+ */
+public class SettingsSuggestionsController implements
+ SuggestionController.ServiceConnectionListener,
+ LoaderManager.LoaderCallbacks<List<Suggestion>> {
+ private static final Logger LOG = new Logger(SettingsSuggestionsController.class);
+ private static final ComponentName mComponentName = new ComponentName(
+ "com.android.settings.intelligence",
+ "com.android.settings.intelligence.suggestions.SuggestionService");
+
+ private final Context mContext;
+ private final LoaderManager mLoaderManager;
+ private final Listener mListener;
+ private final SuggestionController mSuggestionController;
+ private final IconCache mIconCache;
+
+ public SettingsSuggestionsController(
+ Context context,
+ LoaderManager loaderManager,
+ @NonNull Listener listener) {
+ mContext = context;
+ mLoaderManager = loaderManager;
+ mListener = listener;
+ mIconCache = new IconCache(context);
+ mSuggestionController = new SuggestionController(
+ mContext,
+ mComponentName,
+ this /* serviceConnectionListener */);
+ }
+
+ @Override
+ public void onServiceConnected() {
+ LOG.v("onServiceConnected");
+ mLoaderManager.restartLoader(
+ SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS,
+ null /* args */,
+ this /* callback */);
+ }
+
+ @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(mContext, mSuggestionController);
+ }
+ throw new IllegalArgumentException("This loader id is not supported " + id);
+ }
+
+ @Override
+ public void onLoadFinished(
+ @NonNull Loader<List<Suggestion>> loader,
+ List<Suggestion> suggestionList) {
+ LOG.v("onLoadFinished");
+ if (suggestionList == null) {
+ return;
+ }
+ ArrayList<SuggestionLineItem> items = new ArrayList<>();
+ for (final Suggestion suggestion : suggestionList) {
+ LOG.v("Suggestion ID: " + suggestion.getId());
+ Drawable iconDrawable = mIconCache.getIcon(suggestion.getIcon());
+ SuggestionLineItem suggestionLineItem =
+ new SuggestionLineItem(
+ suggestion.getTitle(),
+ suggestion.getSummary(),
+ iconDrawable,
+ v -> {
+ try {
+ suggestion.getPendingIntent().send();
+ launchSuggestion(suggestion);
+ } catch (PendingIntent.CanceledException e) {
+ LOG.w("Failed to start suggestion " + suggestion.getTitle());
+ }
+ });
+ items.add(suggestionLineItem);
+ }
+ mListener.onSuggestionsLoaded(items);
+ }
+
+ @Override
+ public void onLoaderReset(@NonNull Loader<List<Suggestion>> loader) {
+ LOG.v("onLoaderReset");
+ }
+
+ /**
+ * Start the suggestions controller.
+ */
+ public void start() {
+ LOG.v("Start");
+ mSuggestionController.start();
+ }
+
+ /**
+ * Stop the suggestions controller.
+ */
+ public void stop() {
+ LOG.v("Stop");
+ mSuggestionController.stop();
+ cleanupLoader();
+
+ }
+
+ private void cleanupLoader() {
+ LOG.v("cleanupLoader");
+ mLoaderManager.destroyLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS);
+ }
+
+ private void dismissSuggestion(Suggestion suggestion) {
+ LOG.v("dismissSuggestion");
+ mSuggestionController.dismissSuggestions(suggestion);
+ }
+
+ private void launchSuggestion(Suggestion suggestion) {
+ LOG.v("launchSuggestion");
+ mSuggestionController.launchSuggestion(suggestion);
+ }
+
+ /**
+ * Listener interface to notify of data state changes.
+ */
+ public interface Listener {
+ /**
+ * Invoked when deferred setup items have been loaded.
+ * @param suggestions List of deferred setup suggestions.
+ */
+ void onSuggestionsLoaded(@NonNull ArrayList<SuggestionLineItem> suggestions);
+ }
+}
diff --git a/src/com/android/car/settings/suggestions/SettingsSuggestionsLoader.java b/src/com/android/car/settings/suggestions/SettingsSuggestionsLoader.java
new file mode 100644
index 0000000..33c4c0f
--- /dev/null
+++ b/src/com/android/car/settings/suggestions/SettingsSuggestionsLoader.java
@@ -0,0 +1,58 @@
+/*
+ * 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.content.Context;
+import android.service.settings.suggestions.Suggestion;
+
+import com.android.car.settingslib.loader.AsyncLoader;
+import com.android.car.settings.common.Logger;
+import com.android.settingslib.suggestions.SuggestionController;
+
+import java.util.List;
+
+/**
+ * Loads suggestions for car settings. Taken from
+ * {@link com.android.settingslib.suggestions.SuggestionLoader}, only change is to extend from car
+ * settings {@link AsyncLoader} which extends from support library {@link AsyncTaskLoader}.
+ */
+public class SettingsSuggestionsLoader extends AsyncLoader<List<Suggestion>> {
+ private static final Logger LOG = new Logger(SettingsSuggestionsLoader.class);
+
+ /**
+ * ID used for loader that loads the settings suggestions. ID value is an arbitrary value.
+ */
+ public static final int LOADER_ID_SUGGESTIONS = 42;
+
+ private final SuggestionController mSuggestionController;
+
+ public SettingsSuggestionsLoader(Context context, SuggestionController controller) {
+ super(context);
+ mSuggestionController = controller;
+ }
+
+ @Override
+ public List<Suggestion> loadInBackground() {
+ final List<Suggestion> data = mSuggestionController.getSuggestions();
+ if (data == null) {
+ LOG.d("data is null");
+ } else {
+ LOG.d("data size " + data.size());
+ }
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/car/settings/suggestions/SuggestionLineItem.java b/src/com/android/car/settings/suggestions/SuggestionLineItem.java
new file mode 100644
index 0000000..fee0de7
--- /dev/null
+++ b/src/com/android/car/settings/suggestions/SuggestionLineItem.java
@@ -0,0 +1,67 @@
+/*
+ * 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.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.car.list.IconTextLineItem;
+
+/**
+ * Represents suggestion list item.
+ */
+public class SuggestionLineItem extends IconTextLineItem {
+
+ private final CharSequence mSummary;
+ private final Drawable mIconDrawable;
+ private final View.OnClickListener mOnClickListener;
+
+ /**
+ * Constructs an IconTextLIneItem with just the title set.
+ */
+ public SuggestionLineItem(
+ CharSequence title,
+ CharSequence summary,
+ Drawable iconDrawable,
+ View.OnClickListener onClickListener) {
+ super(title);
+ mSummary = summary;
+ mIconDrawable = iconDrawable;
+ mOnClickListener = onClickListener;
+ }
+
+ @Override
+ public void setIcon(ImageView iconView) {
+ iconView.setImageDrawable(mIconDrawable);
+ }
+
+ @Override
+ public CharSequence getDesc() {
+ return mSummary;
+ }
+
+ @Override
+ public boolean isExpandable() {
+ return true;
+ }
+
+ @Override
+ public void onClick(View view) {
+ mOnClickListener.onClick(view);
+ }
+}