summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Stetson <alexstetson@google.com>2021-02-23 14:38:21 -0800
committerAlex Stetson <alexstetson@google.com>2021-02-25 10:58:11 -0800
commit115b7829316d75ba4eb2e07f35c3dd2be4006726 (patch)
treefe8f826cd4e8bb057f7a4c695275054090125e67
parent0be74c0cc29371e596372271a9c66d0347cee22b (diff)
downloadSettingsIntelligence-115b7829316d75ba4eb2e07f35c3dd2be4006726.tar.gz
Move car code to CarSettingsIntelligence
Migrate car-specific code from phone SettingsIntelligence to CarSettingsIntelligence Bug: 174688736 Test: manual Change-Id: I26ed08ee9e0fe32161c899e390e1fd5c50f6d33d
-rw-r--r--Android.bp33
-rw-r--r--AndroidManifest.xml28
-rw-r--r--OWNERS8
-rw-r--r--proguard.cfg32
-rw-r--r--res/values/themes.xml20
-rw-r--r--res/xml/car_search_fragment.xml21
-rw-r--r--src/com/android/settings/intelligence/search/SearchActivity.java50
-rw-r--r--src/com/android/settings/intelligence/search/car/CarFeatureFactoryImpl.java36
-rw-r--r--src/com/android/settings/intelligence/search/car/CarIntentSearchViewHolder.java69
-rw-r--r--src/com/android/settings/intelligence/search/car/CarSearchFeatureProviderImpl.java163
-rw-r--r--src/com/android/settings/intelligence/search/car/CarSearchFragment.java338
-rw-r--r--src/com/android/settings/intelligence/search/car/CarSearchResultsAdapter.java122
-rw-r--r--src/com/android/settings/intelligence/search/car/CarSearchViewHolder.java50
-rw-r--r--src/com/android/settings/intelligence/search/indexing/car/CarDatabaseIndexingManager.java39
-rw-r--r--src/com/android/settings/intelligence/search/indexing/car/CarIndexData.java46
-rw-r--r--src/com/android/settings/intelligence/search/indexing/car/CarIndexDataConverter.java38
-rw-r--r--src/com/android/settings/intelligence/search/savedqueries/car/CarSavedQueryController.java161
-rw-r--r--src/com/android/settings/intelligence/search/savedqueries/car/CarSavedQueryViewHolder.java44
18 files changed, 1298 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..ceef57f
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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.
+
+android_app {
+ name: "CarSettingsIntelligence",
+ overrides: ["SettingsIntelligence"],
+ optimize: {
+ proguard_flags_files: ["proguard.cfg"],
+ },
+ sdk_version: "system_current",
+ product_specific: true,
+ privileged: true,
+ required: ["privapp_whitelist_com.android.settings.intelligence"],
+
+ libs: ["android.car-stubs"],
+ static_libs: [
+ "SettingsIntelligence-core",
+ "car-ui-lib",
+ ],
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+}
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..9b0b89a
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settings.intelligence"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <application tools:node="merge">
+ <activity
+ android:name=".search.SearchActivity"
+ android:theme="@style/Theme.CarSettings"
+ tools:replace="android:theme"/>
+ </application>
+</manifest>
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..7f8081c
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,8 @@
+# People who can approve changes for submission.
+
+# Primary
+alexstetson@google.com
+
+# Secondary (only if people in Primary are unreachable)
+hseog@google.com
+nehah@google.com
diff --git a/proguard.cfg b/proguard.cfg
new file mode 100644
index 0000000..4ff3790
--- /dev/null
+++ b/proguard.cfg
@@ -0,0 +1,32 @@
+# This is a configuration file for ProGuard.
+# http://proguard.sourceforge.net/index.html#manual/usage.html
+
+# We want to keep methods in Activity that could be used in the XML attribute onClick.
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+ public void *(android.view.MenuItem);
+}
+
+# Keep setters in Views so that animations can still work.
+-keep public class * extends android.view.View {
+ public <init>(android.content.Context);
+ public <init>(android.content.Context, android.util.AttributeSet);
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+
+ void set*(***);
+ *** get*();
+}
+
+# Keep classes that may be inflated from XML.
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+# Keep annotated classes or class members.
+-keep @androidx.annotation.Keep class *
+-keepclassmembers class * {
+ @androidx.annotation.Keep *;
+}
diff --git a/res/values/themes.xml b/res/values/themes.xml
new file mode 100644
index 0000000..45ee9f3
--- /dev/null
+++ b/res/values/themes.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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.
+ -->
+
+<resources>
+ <style name="Theme.CarSettings" parent="@style/Theme.CarUi.WithToolbar"/>
+</resources> \ No newline at end of file
diff --git a/res/xml/car_search_fragment.xml b/res/xml/car_search_fragment.xml
new file mode 100644
index 0000000..201c60a
--- /dev/null
+++ b/res/xml/car_search_fragment.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/app_name_settings_intelligence"/>
diff --git a/src/com/android/settings/intelligence/search/SearchActivity.java b/src/com/android/settings/intelligence/search/SearchActivity.java
new file mode 100644
index 0000000..07422e2
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/SearchActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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.settings.intelligence.search;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settings.intelligence.R;
+import com.android.settings.intelligence.search.car.CarSearchFragment;
+
+public class SearchActivity extends FragmentActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_main);
+
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ Fragment fragment = fragmentManager.findFragmentById(R.id.main_content);
+ if (fragment == null) {
+ fragmentManager.beginTransaction()
+ .add(R.id.main_content, new CarSearchFragment())
+ .commit();
+ }
+ }
+
+ @Override
+ public boolean onNavigateUp() {
+ finish();
+ return true;
+ }
+}
diff --git a/src/com/android/settings/intelligence/search/car/CarFeatureFactoryImpl.java b/src/com/android/settings/intelligence/search/car/CarFeatureFactoryImpl.java
new file mode 100644
index 0000000..452ab27
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/car/CarFeatureFactoryImpl.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.car;
+
+import androidx.annotation.Keep;
+
+import com.android.settings.intelligence.overlay.FeatureFactoryImpl;
+import com.android.settings.intelligence.search.SearchFeatureProvider;
+
+/**
+ * FeatureFactory implementation for car settings search.
+ */
+@Keep
+public class CarFeatureFactoryImpl extends FeatureFactoryImpl {
+ @Override
+ public SearchFeatureProvider searchFeatureProvider() {
+ if (mSearchFeatureProvider == null) {
+ mSearchFeatureProvider = new CarSearchFeatureProviderImpl();
+ }
+ return mSearchFeatureProvider;
+ }
+}
diff --git a/src/com/android/settings/intelligence/search/car/CarIntentSearchViewHolder.java b/src/com/android/settings/intelligence/search/car/CarIntentSearchViewHolder.java
new file mode 100644
index 0000000..a8353cd
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/car/CarIntentSearchViewHolder.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.car;
+
+import android.content.pm.PackageManager;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.settings.intelligence.R;
+import com.android.settings.intelligence.search.AppSearchResult;
+import com.android.settings.intelligence.search.SearchResult;
+
+/**
+ * ViewHolder for intent based search results.
+ */
+public class CarIntentSearchViewHolder extends CarSearchViewHolder {
+
+ public CarIntentSearchViewHolder(View view) {
+ super(view);
+ }
+
+ @Override
+ public void onBind(CarSearchFragment fragment, SearchResult result) {
+ mTitle.setText(result.title);
+ if (result instanceof AppSearchResult) {
+ AppSearchResult appResult = (AppSearchResult) result;
+ PackageManager pm = fragment.getActivity().getPackageManager();
+ mIcon.setImageDrawable(appResult.info.loadIcon(pm));
+ } else {
+ mIcon.setImageDrawable(result.icon);
+ }
+ bindBreadcrumbView(result);
+
+ itemView.setOnClickListener(v -> fragment.onSearchResultClicked(result));
+ }
+
+ private void bindBreadcrumbView(SearchResult result) {
+ if (result.breadcrumbs == null || result.breadcrumbs.isEmpty()) {
+ mSummary.setVisibility(View.GONE);
+ return;
+ }
+ String breadcrumb = result.breadcrumbs.get(0);
+ int count = result.breadcrumbs.size();
+ for (int i = 1; i < count; i++) {
+ breadcrumb = mContext.getString(R.string.search_breadcrumb_connector,
+ breadcrumb, result.breadcrumbs.get(i));
+ }
+ if (breadcrumb == null || TextUtils.isEmpty(breadcrumb.trim())) {
+ mSummary.setVisibility(View.GONE);
+ } else {
+ mSummary.setText(breadcrumb);
+ mSummary.setVisibility(View.VISIBLE);
+ }
+ }
+}
diff --git a/src/com/android/settings/intelligence/search/car/CarSearchFeatureProviderImpl.java b/src/com/android/settings/intelligence/search/car/CarSearchFeatureProviderImpl.java
new file mode 100644
index 0000000..2491cbd
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/car/CarSearchFeatureProviderImpl.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.car;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+
+import com.android.settings.intelligence.search.SearchFeatureProvider;
+import com.android.settings.intelligence.search.SearchFragment;
+import com.android.settings.intelligence.search.SearchResult;
+import com.android.settings.intelligence.search.SearchResultLoader;
+import com.android.settings.intelligence.search.indexing.DatabaseIndexingManager;
+import com.android.settings.intelligence.search.indexing.IndexData;
+import com.android.settings.intelligence.search.indexing.IndexingCallback;
+import com.android.settings.intelligence.search.indexing.car.CarDatabaseIndexingManager;
+import com.android.settings.intelligence.search.query.DatabaseResultTask;
+import com.android.settings.intelligence.search.query.InstalledAppResultTask;
+import com.android.settings.intelligence.search.query.SearchQueryTask;
+import com.android.settings.intelligence.search.savedqueries.SavedQueryLoader;
+import com.android.settings.intelligence.search.sitemap.SiteMapManager;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+
+/**
+ * SearchFeatureProvider for car settings search.
+ */
+public class CarSearchFeatureProviderImpl implements SearchFeatureProvider {
+ private static final String TAG = "CarSearchFeatureProvider";
+ private static final long SMART_SEARCH_RANKING_TIMEOUT = 300L;
+
+ private CarDatabaseIndexingManager mDatabaseIndexingManager;
+ private ExecutorService mExecutorService;
+ private SiteMapManager mSiteMapManager;
+
+ @Override
+ public SearchResultLoader getSearchResultLoader(Context context, String query) {
+ return new SearchResultLoader(context, cleanQuery(query));
+ }
+
+ @Override
+ public List<SearchQueryTask> getSearchQueryTasks(Context context, String query) {
+ List<SearchQueryTask> tasks = new ArrayList<>();
+ String cleanQuery = cleanQuery(query);
+ tasks.add(DatabaseResultTask.newTask(context, getSiteMapManager(), cleanQuery));
+ tasks.add(InstalledAppResultTask.newTask(context, getSiteMapManager(), cleanQuery));
+ return tasks;
+ }
+
+ @Override
+ public SavedQueryLoader getSavedQueryLoader(Context context) {
+ return new SavedQueryLoader(context);
+ }
+
+ @Override
+ public DatabaseIndexingManager getIndexingManager(Context context) {
+ if (mDatabaseIndexingManager == null) {
+ mDatabaseIndexingManager = new CarDatabaseIndexingManager(
+ context.getApplicationContext());
+ }
+ return mDatabaseIndexingManager;
+ }
+
+ @Override
+ public SiteMapManager getSiteMapManager() {
+ if (mSiteMapManager == null) {
+ mSiteMapManager = new SiteMapManager();
+ }
+ return mSiteMapManager;
+ }
+
+ @Override
+ public boolean isIndexingComplete(Context context) {
+ return getIndexingManager(context).isIndexingComplete();
+ }
+
+ @Override
+ public void initFeedbackButton() {
+ }
+
+ @Override
+ public void showFeedbackButton(SearchFragment fragment, View root) {
+ }
+
+ @Override
+ public void hideFeedbackButton(View root) {
+ }
+
+ @Override
+ public void searchResultClicked(Context context, String query, SearchResult searchResult) {
+ }
+
+ @Override
+ public boolean isSmartSearchRankingEnabled(Context context) {
+ return false;
+ }
+
+ @Override
+ public long smartSearchRankingTimeoutMs(Context context) {
+ return SMART_SEARCH_RANKING_TIMEOUT;
+ }
+
+ @Override
+ public void searchRankingWarmup(Context context) {
+ }
+
+ @Override
+ public FutureTask<List<Pair<String, Float>>> getRankerTask(Context context, String query) {
+ return null;
+ }
+
+ @Override
+ public void updateIndexAsync(Context context, IndexingCallback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "updating index async");
+ }
+ getIndexingManager(context).indexDatabase(callback);
+ }
+
+ @Override
+ public ExecutorService getExecutorService() {
+ if (mExecutorService == null) {
+ mExecutorService = Executors.newCachedThreadPool();
+ }
+ return mExecutorService;
+ }
+
+ /**
+ * A generic method to make the query suitable for searching the database.
+ *
+ * @return the cleaned query string
+ */
+ private String cleanQuery(String query) {
+ if (TextUtils.isEmpty(query)) {
+ return null;
+ }
+ if (Locale.getDefault().equals(Locale.JAPAN)) {
+ query = IndexData.normalizeJapaneseString(query);
+ }
+ return query.trim();
+ }
+}
diff --git a/src/com/android/settings/intelligence/search/car/CarSearchFragment.java b/src/com/android/settings/intelligence/search/car/CarSearchFragment.java
new file mode 100644
index 0000000..702be42
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/car/CarSearchFragment.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.car;
+
+import static com.android.car.ui.core.CarUi.requireInsets;
+import static com.android.car.ui.core.CarUi.requireToolbar;
+import static com.android.car.ui.utils.CarUiUtils.drawableToBitmap;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.annotation.NonNull;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.ui.imewidescreen.CarUiImeSearchListItem;
+import com.android.car.ui.preference.PreferenceFragment;
+import com.android.car.ui.recyclerview.CarUiContentListItem;
+import com.android.car.ui.toolbar.MenuItem;
+import com.android.car.ui.toolbar.Toolbar;
+import com.android.car.ui.toolbar.ToolbarController;
+import com.android.settings.intelligence.R;
+import com.android.settings.intelligence.overlay.FeatureFactory;
+import com.android.settings.intelligence.search.AppSearchResult;
+import com.android.settings.intelligence.search.SearchCommon;
+import com.android.settings.intelligence.search.SearchFeatureProvider;
+import com.android.settings.intelligence.search.SearchResult;
+import com.android.settings.intelligence.search.indexing.IndexingCallback;
+import com.android.settings.intelligence.search.savedqueries.car.CarSavedQueryController;
+import com.android.settings.intelligence.search.savedqueries.car.CarSavedQueryViewHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Search fragment for car settings.
+ */
+public class CarSearchFragment extends PreferenceFragment implements
+ LoaderManager.LoaderCallbacks<List<? extends SearchResult>>, IndexingCallback {
+ private static final String TAG = "CarSearchFragment";
+ private static final int REQUEST_CODE_NO_OP = 0;
+
+ private SearchFeatureProvider mSearchFeatureProvider;
+
+ private ToolbarController mToolbar;
+ private RecyclerView mRecyclerView;
+
+ private String mQuery;
+ private boolean mShowingSavedQuery;
+
+ private CarSearchResultsAdapter mSearchAdapter;
+ private CarSavedQueryController mSavedQueryController;
+
+ private final RecyclerView.OnScrollListener mScrollListener =
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ if (dy != 0) {
+ hideKeyboard();
+ }
+ }
+ };
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.car_search_fragment, rootKey);
+ }
+
+ protected ToolbarController getToolbar() {
+ return requireToolbar(requireActivity());
+ }
+
+ protected List<MenuItem> getToolbarMenuItems() {
+ return null;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mSearchFeatureProvider = FeatureFactory.get(context).searchFeatureProvider();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ mQuery = savedInstanceState.getString(SearchCommon.STATE_QUERY);
+ mShowingSavedQuery = savedInstanceState.getBoolean(
+ SearchCommon.STATE_SHOWING_SAVED_QUERY);
+ } else {
+ mShowingSavedQuery = true;
+ }
+
+ LoaderManager loaderManager = getLoaderManager();
+ mSearchAdapter = new CarSearchResultsAdapter(/* fragment= */ this);
+ mToolbar = getToolbar();
+ mSavedQueryController = new CarSavedQueryController(
+ getContext(), loaderManager, mSearchAdapter, mToolbar, this);
+ mSearchFeatureProvider.updateIndexAsync(getContext(), /* indexingCallback= */ this);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ if (mToolbar != null) {
+ List<MenuItem> items = getToolbarMenuItems();
+ mToolbar.setTitle(getPreferenceScreen().getTitle());
+ mToolbar.setMenuItems(items);
+ mToolbar.setNavButtonMode(Toolbar.NavButtonMode.BACK);
+ mToolbar.setState(Toolbar.State.SUBPAGE);
+ mToolbar.setState(Toolbar.State.SEARCH);
+ mToolbar.setSearchHint(R.string.abc_search_hint);
+ mToolbar.registerOnSearchListener(this::onQueryTextChange);
+ mToolbar.registerOnSearchCompletedListener(this::onSearchComplete);
+ mToolbar.setShowMenuItemsWhileSearching(true);
+ mToolbar.setSearchQuery(mQuery);
+ }
+ mRecyclerView = getListView();
+ if (mRecyclerView != null) {
+ mRecyclerView.setAdapter(mSearchAdapter);
+ mRecyclerView.addOnScrollListener(mScrollListener);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ onCarUiInsetsChanged(requireInsets(requireActivity()));
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(SearchCommon.STATE_QUERY, mQuery);
+ outState.putBoolean(SearchCommon.STATE_SHOWING_SAVED_QUERY, mShowingSavedQuery);
+ }
+
+ private void onQueryTextChange(String query) {
+ if (TextUtils.equals(query, mQuery)) {
+ return;
+ }
+ boolean isEmptyQuery = TextUtils.isEmpty(query);
+
+ mQuery = query;
+
+ // If indexing is not finished, register the query text, but don't search.
+ if (!mSearchFeatureProvider.isIndexingComplete(getActivity())) {
+ mToolbar.getProgressBar().setVisible(!isEmptyQuery);
+ return;
+ }
+
+ if (isEmptyQuery) {
+ LoaderManager loaderManager = getLoaderManager();
+ loaderManager.destroyLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT);
+ mShowingSavedQuery = true;
+ mSavedQueryController.loadSavedQueries();
+ } else {
+ restartLoaders();
+ }
+ }
+
+ private void onSearchComplete() {
+ if (!TextUtils.isEmpty(mQuery)) {
+ mSavedQueryController.saveQuery(mQuery);
+ }
+ }
+
+ /**
+ * Gets called when a saved query is clicked.
+ */
+ public void onSavedQueryClicked(CharSequence query) {
+ String queryString = query.toString();
+ mToolbar.setSearchQuery(queryString);
+ onQueryTextChange(queryString);
+ }
+
+ @Override
+ public Loader<List<? extends SearchResult>> onCreateLoader(int id, Bundle args) {
+ Activity activity = getActivity();
+
+ if (id == SearchCommon.SearchLoaderId.SEARCH_RESULT) {
+ return mSearchFeatureProvider.getSearchResultLoader(activity, mQuery);
+ }
+ return null;
+ }
+
+ @Override
+ public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
+ List<? extends SearchResult> data) {
+
+ if (mToolbar.canShowSearchResultItems()) {
+ List<CarUiImeSearchListItem> searchItems = new ArrayList<>();
+ for (SearchResult result : data) {
+ CarUiImeSearchListItem item = new CarUiImeSearchListItem(
+ CarUiContentListItem.Action.ICON);
+ item.setTitle(result.title);
+ if (result.breadcrumbs != null && !result.breadcrumbs.isEmpty()) {
+ item.setBody(getBreadcrumb(result));
+ }
+
+ if (result instanceof AppSearchResult) {
+ AppSearchResult appResult = (AppSearchResult) result;
+ PackageManager pm = getActivity().getPackageManager();
+ Drawable drawable = appResult.info.loadIcon(pm);
+ Bitmap bm = drawableToBitmap(drawable);
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bm);
+ item.setIcon(bitmapDrawable);
+ } else if (result.icon != null) {
+ Bitmap bm = drawableToBitmap(result.icon);
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bm);
+ item.setIcon(bitmapDrawable);
+ }
+ item.setOnItemClickedListener(v -> onSearchResultClicked(result));
+
+ searchItems.add(item);
+ }
+ mToolbar.setSearchResultItems(searchItems);
+ }
+
+ mSearchAdapter.postSearchResults(data);
+ mRecyclerView.scrollToPosition(0);
+ }
+
+ private String getBreadcrumb(SearchResult result) {
+ String breadcrumb = result.breadcrumbs.get(0);
+ int count = result.breadcrumbs.size();
+ for (int i = 1; i < count; i++) {
+ breadcrumb = getContext().getString(R.string.search_breadcrumb_connector,
+ breadcrumb, result.breadcrumbs.get(i));
+ }
+
+ return breadcrumb;
+ }
+
+ /**
+ * Gets called when a search result is clicked.
+ */
+ protected void onSearchResultClicked(SearchResult result) {
+ mSearchFeatureProvider.searchResultClicked(getContext(), mQuery, result);
+ mSavedQueryController.saveQuery(mQuery);
+
+ Intent intent = result.payload.getIntent();
+ if (result instanceof AppSearchResult) {
+ getActivity().startActivity(intent);
+ } else {
+ PackageManager pm = getActivity().getPackageManager();
+ List<ResolveInfo> info = pm.queryIntentActivities(intent, /* flags= */ 0);
+ if (info != null && !info.isEmpty()) {
+ startActivityForResult(intent, REQUEST_CODE_NO_OP);
+ } else {
+ Log.e(TAG, "Cannot launch search result, title: "
+ + result.title + ", " + intent);
+ }
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<List<? extends SearchResult>> loader) {
+ }
+
+ /**
+ * Gets called when Indexing is completed.
+ */
+ @Override
+ public void onIndexingFinished() {
+ if (getActivity() == null) {
+ return;
+ }
+ mToolbar.getProgressBar().setVisible(false);
+ if (mShowingSavedQuery) {
+ mSavedQueryController.loadSavedQueries();
+ } else {
+ LoaderManager loaderManager = getLoaderManager();
+ loaderManager.initLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT,
+ /* args= */ null, /* callback= */ this);
+ }
+ requery();
+ }
+
+ private void requery() {
+ if (TextUtils.isEmpty(mQuery)) {
+ return;
+ }
+ String query = mQuery;
+ mQuery = "";
+ onQueryTextChange(query);
+ }
+
+ private void restartLoaders() {
+ mShowingSavedQuery = false;
+ LoaderManager loaderManager = getLoaderManager();
+ loaderManager.restartLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT,
+ /* args= */ null, /* callback= */ this);
+ }
+
+ private void hideKeyboard() {
+ Activity activity = getActivity();
+ if (activity != null) {
+ View view = activity.getCurrentFocus();
+ InputMethodManager imm = (InputMethodManager)
+ activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm.isActive(view)) {
+ imm.hideSoftInputFromWindow(view.getWindowToken(), /* flags= */ 0);
+ }
+ }
+
+ if (mRecyclerView != null) {
+ mRecyclerView.requestFocus();
+ }
+ }
+}
diff --git a/src/com/android/settings/intelligence/search/car/CarSearchResultsAdapter.java b/src/com/android/settings/intelligence/search/car/CarSearchResultsAdapter.java
new file mode 100644
index 0000000..be35482
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/car/CarSearchResultsAdapter.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.car;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.intelligence.R;
+import com.android.settings.intelligence.search.ResultPayload;
+import com.android.settings.intelligence.search.SearchResult;
+import com.android.settings.intelligence.search.SearchResultDiffCallback;
+import com.android.settings.intelligence.search.savedqueries.car.CarSavedQueryViewHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * RecyclerView Adapter for the car search results RecyclerView.
+ * The adapter uses the CarSearchViewHolder for its view contents.
+ */
+public class CarSearchResultsAdapter extends RecyclerView.Adapter<CarSearchViewHolder> {
+
+ private final CarSearchFragment mFragment;
+ private final List<SearchResult> mSearchResults;
+
+ public CarSearchResultsAdapter(CarSearchFragment fragment) {
+ mFragment = fragment;
+ mSearchResults = new ArrayList<>();
+
+ setHasStableIds(true);
+ }
+
+ @Override
+ public CarSearchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ Context context = parent.getContext();
+ LayoutInflater inflater = LayoutInflater.from(context);
+ View view;
+ switch (viewType) {
+ case ResultPayload.PayloadType.INTENT:
+ view = inflater.inflate(R.layout.car_ui_preference, parent,
+ /* attachToRoot= */ false);
+ return new CarIntentSearchViewHolder(view);
+ case ResultPayload.PayloadType.SAVED_QUERY:
+ view = inflater.inflate(R.layout.car_ui_preference, parent,
+ /* attachToRoot= */ false);
+ return new CarSavedQueryViewHolder(view);
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(CarSearchViewHolder holder, int position) {
+ holder.onBind(mFragment, mSearchResults.get(position));
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mSearchResults.get(position).hashCode();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mSearchResults.get(position).viewType;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSearchResults.size();
+ }
+
+ protected void postSearchResults(List<? extends SearchResult> newSearchResults) {
+ DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
+ new SearchResultDiffCallback(mSearchResults, newSearchResults));
+ mSearchResults.clear();
+ mSearchResults.addAll(newSearchResults);
+ diffResult.dispatchUpdatesTo(/* adapter= */ this);
+ }
+
+ /**
+ * Displays recent searched queries.
+ */
+ public void displaySavedQuery(List<? extends SearchResult> data) {
+ clearResults();
+ mSearchResults.addAll(data);
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Clear current search results.
+ */
+ public void clearResults() {
+ mSearchResults.clear();
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Get current search results.
+ */
+ public List<SearchResult> getSearchResults() {
+ return mSearchResults;
+ }
+}
diff --git a/src/com/android/settings/intelligence/search/car/CarSearchViewHolder.java b/src/com/android/settings/intelligence/search/car/CarSearchViewHolder.java
new file mode 100644
index 0000000..2657d9a
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/car/CarSearchViewHolder.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.car;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.intelligence.search.SearchResult;
+
+/** The ViewHolder for the car search RecyclerView.
+ * There are multiple search result types with different UI requirements, such as Intent results
+ * and saved query results.
+ */
+public abstract class CarSearchViewHolder extends RecyclerView.ViewHolder {
+ protected Context mContext;
+ protected ImageView mIcon;
+ protected TextView mTitle;
+ protected TextView mSummary;
+
+ public CarSearchViewHolder(View view) {
+ super(view);
+ mContext = view.getContext();
+ mIcon = view.findViewById(android.R.id.icon);
+ mTitle = view.findViewById(android.R.id.title);
+ mSummary = view.findViewById(android.R.id.summary);
+ }
+
+ /**
+ * Update the ViewHolder data when bound.
+ */
+ public abstract void onBind(CarSearchFragment fragment, SearchResult result);
+}
diff --git a/src/com/android/settings/intelligence/search/indexing/car/CarDatabaseIndexingManager.java b/src/com/android/settings/intelligence/search/indexing/car/CarDatabaseIndexingManager.java
new file mode 100644
index 0000000..3026e82
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/indexing/car/CarDatabaseIndexingManager.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.indexing.car;
+
+import android.content.Context;
+
+import com.android.settings.intelligence.search.indexing.DatabaseIndexingManager;
+import com.android.settings.intelligence.search.indexing.IndexDataConverter;
+import com.android.settings.intelligence.search.indexing.PreIndexData;
+
+/**
+ * Car extension to {@link DatabaseIndexingManager} to use {@link CarIndexDataConverter} for
+ * converting {@link PreIndexData} into {@link CarIndexData}.
+ */
+public class CarDatabaseIndexingManager extends DatabaseIndexingManager {
+
+ public CarDatabaseIndexingManager(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected IndexDataConverter getIndexDataConverter(Context context) {
+ return new CarIndexDataConverter(context);
+ }
+}
diff --git a/src/com/android/settings/intelligence/search/indexing/car/CarIndexData.java b/src/com/android/settings/intelligence/search/indexing/car/CarIndexData.java
new file mode 100644
index 0000000..a57d003
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/indexing/car/CarIndexData.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.indexing.car;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.settings.intelligence.search.indexing.DatabaseIndexingUtils;
+import com.android.settings.intelligence.search.indexing.IndexData;
+
+/**
+ * Car data class representing a single row in the Setting Search results database.
+ */
+public class CarIndexData extends IndexData {
+
+ public CarIndexData(IndexData.Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Builder class for {@link CarIndexData}, extending {@link IndexData.Builder}, which replaces
+ * all intents with direct search intents, since CarSettings doesn't support
+ * SearchResultTrampolineIntents.
+ */
+ public static class Builder extends IndexData.Builder {
+ @Override
+ protected Intent buildIntent(Context context) {
+ return DatabaseIndexingUtils.buildDirectSearchResultIntent(getIntentAction(),
+ getIntentTargetPackage(), getIntentTargetClass(), getKey());
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/settings/intelligence/search/indexing/car/CarIndexDataConverter.java b/src/com/android/settings/intelligence/search/indexing/car/CarIndexDataConverter.java
new file mode 100644
index 0000000..11f35cb
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/indexing/car/CarIndexDataConverter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.indexing.car;
+
+import android.content.Context;
+
+import com.android.settings.intelligence.search.indexing.IndexData;
+import com.android.settings.intelligence.search.indexing.IndexDataConverter;
+import com.android.settings.intelligence.search.indexing.PreIndexData;
+
+/**
+ * Car helper class to convert {@link PreIndexData} to {@link CarIndexData}.
+ */
+public class CarIndexDataConverter extends IndexDataConverter {
+
+ public CarIndexDataConverter(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected IndexData.Builder getIndexDataBuilder() {
+ return new CarIndexData.Builder();
+ }
+}
diff --git a/src/com/android/settings/intelligence/search/savedqueries/car/CarSavedQueryController.java b/src/com/android/settings/intelligence/search/savedqueries/car/CarSavedQueryController.java
new file mode 100644
index 0000000..4c9a458
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/savedqueries/car/CarSavedQueryController.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.savedqueries.car;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MenuItem;
+
+import androidx.annotation.NonNull;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+
+import com.android.car.ui.imewidescreen.CarUiImeSearchListItem;
+import com.android.car.ui.recyclerview.CarUiContentListItem;
+import com.android.car.ui.toolbar.ToolbarController;
+import com.android.settings.intelligence.R;
+import com.android.settings.intelligence.overlay.FeatureFactory;
+import com.android.settings.intelligence.search.SearchCommon;
+import com.android.settings.intelligence.search.SearchFeatureProvider;
+import com.android.settings.intelligence.search.SearchResult;
+import com.android.settings.intelligence.search.car.CarSearchFragment;
+import com.android.settings.intelligence.search.car.CarSearchResultsAdapter;
+import com.android.settings.intelligence.search.savedqueries.SavedQueryRecorder;
+import com.android.settings.intelligence.search.savedqueries.SavedQueryRemover;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for managing saved queries.
+ */
+public class CarSavedQueryController implements LoaderManager.LoaderCallbacks,
+ MenuItem.OnMenuItemClickListener {
+
+ private static final String ARG_QUERY = "remove_query";
+ private static final String TAG = "CarSearchSavedQueryCtrl";
+
+ private static final int MENU_SEARCH_HISTORY = 1000;
+
+ private final Context mContext;
+ private final LoaderManager mLoaderManager;
+ private final SearchFeatureProvider mSearchFeatureProvider;
+ private final CarSearchResultsAdapter mResultAdapter;
+ private ToolbarController mToolbar;
+ private CarSearchFragment mFragment;
+
+ public CarSavedQueryController(Context context, LoaderManager loaderManager,
+ CarSearchResultsAdapter resultsAdapter, @NonNull ToolbarController toolbar,
+ CarSearchFragment fragment) {
+ mContext = context;
+ mLoaderManager = loaderManager;
+ mResultAdapter = resultsAdapter;
+ mSearchFeatureProvider = FeatureFactory.get(context)
+ .searchFeatureProvider();
+ mToolbar = toolbar;
+ mFragment = fragment;
+ }
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case SearchCommon.SearchLoaderId.SAVE_QUERY_TASK:
+ return new SavedQueryRecorder(mContext, args.getString(ARG_QUERY));
+ case SearchCommon.SearchLoaderId.REMOVE_QUERY_TASK:
+ return new SavedQueryRemover(mContext);
+ case SearchCommon.SearchLoaderId.SAVED_QUERIES:
+ return mSearchFeatureProvider.getSavedQueryLoader(mContext);
+ }
+ return null;
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Object data) {
+ switch (loader.getId()) {
+ case SearchCommon.SearchLoaderId.REMOVE_QUERY_TASK:
+ mLoaderManager.restartLoader(SearchCommon.SearchLoaderId.SAVED_QUERIES,
+ /* args= */ null, /* callback= */ this);
+ break;
+ case SearchCommon.SearchLoaderId.SAVED_QUERIES:
+ if (SearchFeatureProvider.DEBUG) {
+ Log.d(TAG, "Saved queries loaded");
+ }
+ List<SearchResult> results = (List<SearchResult>) data;
+ if (mToolbar.canShowSearchResultItems()) {
+ List<CarUiImeSearchListItem> searchItems = new ArrayList<>();
+ for (SearchResult result : results) {
+ CarUiImeSearchListItem item = new CarUiImeSearchListItem(
+ CarUiContentListItem.Action.ICON);
+ item.setTitle(result.title);
+ item.setIconResId(R.drawable.ic_restore);
+ item.setOnItemClickedListener(
+ v -> mFragment.onSavedQueryClicked(result.title));
+
+ searchItems.add(item);
+ }
+ mToolbar.setSearchResultItems(searchItems);
+ }
+
+ mResultAdapter.displaySavedQuery(results);
+ break;
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (item.getItemId() != MENU_SEARCH_HISTORY) {
+ return false;
+ }
+ removeQueries();
+ return true;
+ }
+
+ /**
+ * Save a query to the DB.
+ */
+ public void saveQuery(String query) {
+ Bundle args = new Bundle();
+ args.putString(ARG_QUERY, query);
+ mLoaderManager.restartLoader(SearchCommon.SearchLoaderId.SAVE_QUERY_TASK, args,
+ /* callback= */ this);
+ }
+
+ /**
+ * Remove all saved queries from the DB.
+ */
+ public void removeQueries() {
+ Bundle args = new Bundle();
+ mLoaderManager.restartLoader(SearchCommon.SearchLoaderId.REMOVE_QUERY_TASK, args,
+ /* callback= */ this);
+ }
+
+ /**
+ * Load the saved queries from the DB.
+ */
+ public void loadSavedQueries() {
+ if (SearchFeatureProvider.DEBUG) {
+ Log.d(TAG, "loading saved queries");
+ }
+ mLoaderManager.restartLoader(SearchCommon.SearchLoaderId.SAVED_QUERIES,
+ /* args= */ null, /* callback= */ this);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/settings/intelligence/search/savedqueries/car/CarSavedQueryViewHolder.java b/src/com/android/settings/intelligence/search/savedqueries/car/CarSavedQueryViewHolder.java
new file mode 100644
index 0000000..12051a8
--- /dev/null
+++ b/src/com/android/settings/intelligence/search/savedqueries/car/CarSavedQueryViewHolder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 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.settings.intelligence.search.savedqueries.car;
+
+import android.view.View;
+
+import com.android.settings.intelligence.R;
+import com.android.settings.intelligence.search.car.CarSearchFragment;
+import com.android.settings.intelligence.search.car.CarSearchViewHolder;
+import com.android.settings.intelligence.search.SearchResult;
+
+/**
+ * ViewHolder for saved queries from past searches.
+ */
+public class CarSavedQueryViewHolder extends CarSearchViewHolder {
+
+ public CarSavedQueryViewHolder(View view) {
+ super(view);
+ }
+
+ @Override
+ public void onBind(CarSearchFragment fragment, SearchResult result) {
+ mTitle.setText(result.title);
+ mIcon.setImageResource(R.drawable.ic_restore);
+ mSummary.setVisibility(View.GONE);
+ itemView.setOnClickListener(v -> {
+ fragment.onSavedQueryClicked(result.title);
+ });
+ }
+}