| /* |
| * Copyright (C) 2022 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.quicksearchbox |
| |
| import android.app.SearchManager |
| import android.content.Intent |
| import android.net.Uri |
| import android.os.Bundle |
| import com.google.common.annotations.VisibleForTesting |
| import kotlin.text.StringBuilder |
| |
| /** Some utilities for suggestions. */ |
| object SuggestionUtils { |
| @JvmStatic |
| fun getSuggestionIntent(suggestion: SuggestionCursor?, appSearchData: Bundle?): Intent { |
| val action: String? = suggestion?.suggestionIntentAction |
| val data: String? = suggestion?.suggestionIntentDataString |
| val query: String? = suggestion?.suggestionQuery |
| val userQuery: String? = suggestion?.userQuery |
| val extraData: String? = suggestion?.suggestionIntentExtraData |
| |
| // Now build the Intent |
| val intent = Intent(action) |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
| // We need CLEAR_TOP to avoid reusing an old task that has other activities |
| // on top of the one we want. |
| intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) |
| if (data != null) { |
| intent.setData(Uri.parse(data)) |
| } |
| intent.putExtra(SearchManager.USER_QUERY, userQuery) |
| if (query != null) { |
| intent.putExtra(SearchManager.QUERY, query) |
| } |
| if (extraData != null) { |
| intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData) |
| } |
| if (appSearchData != null) { |
| intent.putExtra(SearchManager.APP_DATA, appSearchData) |
| } |
| intent.setComponent(suggestion?.suggestionIntentComponent) |
| return intent |
| } |
| |
| /** |
| * Gets a unique key that identifies a suggestion. This is used to avoid duplicate suggestions. |
| */ |
| @JvmStatic |
| fun getSuggestionKey(suggestion: Suggestion): String { |
| val action: String = makeKeyComponent(suggestion.suggestionIntentAction) |
| val data: String = makeKeyComponent(normalizeUrl(suggestion.suggestionIntentDataString)) |
| val query: String = makeKeyComponent(normalizeUrl(suggestion.suggestionQuery)) |
| // calculating accurate size of string builder avoids an allocation vs starting with |
| // the default size and having to expand. |
| val size: Int = action.length + 2 + data.length + query.length |
| return StringBuilder(size) |
| .append(action) |
| .append('#') |
| .append(data) |
| .append('#') |
| .append(query) |
| .toString() |
| } |
| |
| private fun makeKeyComponent(str: String?): String { |
| return str ?: "" |
| } |
| |
| private const val SCHEME_SEPARATOR = "://" |
| private const val DEFAULT_SCHEME = "http" |
| |
| /** |
| * Simple url normalization that adds http:// if no scheme exists, and strips empty paths, e.g., |
| * www.google.com/ -> http://www.google.com. Used to prevent obvious duplication of nav |
| * suggestions, bookmarks and urls entered by the user. |
| */ |
| @JvmStatic |
| @VisibleForTesting |
| fun normalizeUrl(url: String?): String? { |
| val normalized: String |
| if (url != null) { |
| val start: Int |
| val schemePos: Int = url.indexOf(SCHEME_SEPARATOR) |
| if (schemePos == -1) { |
| // no scheme - add the default |
| normalized = DEFAULT_SCHEME + SCHEME_SEPARATOR + url |
| start = DEFAULT_SCHEME.length + SCHEME_SEPARATOR.length |
| } else { |
| normalized = url |
| start = schemePos + SCHEME_SEPARATOR.length |
| } |
| var end: Int = normalized.length |
| if (normalized.indexOf('/', start) == end - 1) { |
| end-- |
| } |
| return normalized.substring(0, end) |
| } |
| return url |
| } |
| } |