blob: cde4cd6c3d16a41c27ef85d9d648330935e2c8b3 [file] [log] [blame]
/*
* 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
}
}