1 /*
2  * Copyright (C) 2022 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.quicksearchbox.google
17 
18 import android.app.SearchManager
19 import android.content.ContentProvider
20 import android.content.ContentValues
21 import android.content.Context
22 import android.content.UriMatcher
23 import android.database.Cursor
24 import android.net.Uri
25 import android.util.Log
26 import com.android.quicksearchbox.CursorBackedSourceResult
27 import com.android.quicksearchbox.QsbApplication
28 import com.android.quicksearchbox.SourceResult
29 import com.android.quicksearchbox.SuggestionCursorBackedCursor
30 
31 /**
32  * A suggestion provider which provides content from Genie, a service that offers a superset of the
33  * content provided by Google Suggest.
34  */
35 class GoogleSuggestionProvider : ContentProvider() {
36   private var mUriMatcher: UriMatcher? = null
37   private var mSource: GoogleSource? = null
38 
39   @Override
onCreatenull40   override fun onCreate(): Boolean {
41     mSource = QsbApplication.get(getContext()).googleSource
42     mUriMatcher = buildUriMatcher(getContext())
43     return true
44   }
45 
46   /**
47    * This will always return [SearchManager.SUGGEST_MIME_TYPE] as this provider is purely to provide
48    * suggestions.
49    */
50   @Override
getTypenull51   override fun getType(uri: Uri): String? {
52     return SearchManager.SUGGEST_MIME_TYPE
53   }
54 
emptyIfNullnull55   private fun emptyIfNull(
56     result: SourceResult?,
57     source: GoogleSource?,
58     query: String?
59   ): SourceResult {
60     return result ?: CursorBackedSourceResult(source, query)
61   }
62 
63   @Override
querynull64   override fun query(
65     uri: Uri,
66     projection: Array<String?>?,
67     selection: String?,
68     selectionArgs: Array<String?>?,
69     sortOrder: String?
70   ): Cursor {
71     if (GoogleSuggestionProvider.Companion.DBG)
72       Log.d(GoogleSuggestionProvider.Companion.TAG, "query uri=$uri")
73     val match: Int? = mUriMatcher?.match(uri)
74     return if (match == GoogleSuggestionProvider.Companion.SEARCH_SUGGEST) {
75       val query = getQuery(uri)
76       SuggestionCursorBackedCursor(emptyIfNull(mSource!!.queryExternal(query), mSource, query))
77     } else if (match == GoogleSuggestionProvider.Companion.SEARCH_SHORTCUT) {
78       val shortcutId = getQuery(uri)
79       val extraData: String? = uri.getQueryParameter(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA)
80       SuggestionCursorBackedCursor(mSource!!.refreshShortcut(shortcutId, extraData))
81     } else {
82       throw IllegalArgumentException("Unknown URI $uri")
83     }
84   }
85 
86   /** Gets the search text from a uri. */
getQuerynull87   private fun getQuery(uri: Uri): String? {
88     return if (uri.getPathSegments().size > 1) {
89       uri.getLastPathSegment()
90     } else {
91       ""
92     }
93   }
94 
95   @Override
insertnull96   override fun insert(uri: Uri, values: ContentValues?): Uri? {
97     throw UnsupportedOperationException()
98   }
99 
100   @Override
updatenull101   override fun update(
102     uri: Uri,
103     values: ContentValues?,
104     selection: String?,
105     selectionArgs: Array<String?>?
106   ): Int {
107     throw UnsupportedOperationException()
108   }
109 
110   @Override
deletenull111   override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String?>?): Int {
112     throw UnsupportedOperationException()
113   }
114 
buildUriMatchernull115   private fun buildUriMatcher(context: Context?): UriMatcher {
116     val authority = getAuthority(context)
117     val matcher = UriMatcher(UriMatcher.NO_MATCH)
118     matcher.addURI(
119       authority,
120       SearchManager.SUGGEST_URI_PATH_QUERY,
121       GoogleSuggestionProvider.Companion.SEARCH_SUGGEST
122     )
123     matcher.addURI(
124       authority,
125       SearchManager.SUGGEST_URI_PATH_QUERY.toString() + "/*",
126       GoogleSuggestionProvider.Companion.SEARCH_SUGGEST
127     )
128     matcher.addURI(
129       authority,
130       SearchManager.SUGGEST_URI_PATH_SHORTCUT,
131       GoogleSuggestionProvider.Companion.SEARCH_SHORTCUT
132     )
133     matcher.addURI(
134       authority,
135       SearchManager.SUGGEST_URI_PATH_SHORTCUT.toString() + "/*",
136       GoogleSuggestionProvider.Companion.SEARCH_SHORTCUT
137     )
138     return matcher
139   }
140 
getAuthoritynull141   protected fun getAuthority(context: Context?): String {
142     return context?.getPackageName().toString() + ".google"
143   }
144 
145   companion object {
146     private const val DBG = false
147     private const val TAG = "QSB.GoogleSuggestionProvider"
148 
149     // UriMatcher constants
150     private const val SEARCH_SUGGEST = 0
151     private const val SEARCH_SHORTCUT = 1
152   }
153 }
154