1 /*
2  * Copyright (C) 2020 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.cts.mockspellchecker
17 
18 import android.content.ComponentName
19 import android.service.textservice.SpellCheckerService
20 import android.util.Log
21 import android.view.textservice.SentenceSuggestionsInfo
22 import android.view.textservice.SuggestionsInfo
23 import android.view.textservice.TextInfo
24 import com.android.cts.mockspellchecker.MockSpellCheckerProto.MockSpellCheckerConfiguration
25 import com.android.cts.mockspellchecker.MockSpellCheckerProto.SuggestionRule
26 import java.io.FileDescriptor
27 import java.io.PrintWriter
28 
withLognull29 internal inline fun <T> withLog(msg: String, block: () -> T): T {
30     Log.i(TAG, msg)
31     return block()
32 }
33 
34 const val EXTRAS_KEY_PREFIX = "prefix"
35 
36 /** Mock Spell checker for end-to-end tests. */
37 class MockSpellChecker : SpellCheckerService() {
38 
<lambda>null39     override fun onCreate() = withLog("MockSpellChecker.onCreate") {
40         super.onCreate()
41     }
42 
<lambda>null43     override fun onDestroy() = withLog("MockSpellChecker.onDestroy") {
44         super.onDestroy()
45     }
46 
dumpnull47     override fun dump(fd: FileDescriptor?, writer: PrintWriter?, args: Array<out String>?) {
48         writer?.println("MockSpellChecker")
49     }
50 
<lambda>null51     override fun createSession(): Session = withLog("MockSpellChecker.createSession") {
52         return MockSpellCheckerSession()
53     }
54 
55     private inner class MockSpellCheckerSession : SpellCheckerService.Session() {
56 
<lambda>null57         override fun onCreate() = withLog("MockSpellCheckerSession.onCreate") {
58         }
59 
onGetSentenceSuggestionsMultiplenull60         override fun onGetSentenceSuggestionsMultiple(
61             textInfos: Array<out TextInfo>?,
62             suggestionsLimit: Int
63         ): Array<SentenceSuggestionsInfo> = withLog(
64                 "MockSpellCheckerSession.onGetSuggestionsMultiple " +
65                         "${textInfos?.map { it.text }?.joinToString(":")}") {
66             if (textInfos == null) return emptyArray()
67             val configuration = MockSpellCheckerConfiguration.parseFrom(
68                     SharedPrefsProvider.get(contentResolver, KEY_CONFIGURATION))
69             if (configuration.matchSentence)
70                 return textInfos.map { matchSentenceSuggestion(configuration, it) }.toTypedArray()
71             return super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit)
72         }
73 
matchSentenceSuggestionnull74         private fun matchSentenceSuggestion(
75             configuration: MockSpellCheckerConfiguration,
76             textInfo: TextInfo
77         ): SentenceSuggestionsInfo {
78             return configuration.suggestionRulesList.find { it.match == textInfo.text }
79                     ?.let {
80                         SentenceSuggestionsInfo(
81                                 arrayOf(suggestionsInfo(it, textInfo.cookie, textInfo.sequence)),
82                                 intArrayOf(if (it.hasStartOffset()) it.startOffset else 0),
83                                 intArrayOf(if (it.hasLength()) it.length else textInfo.text.length))
84                     }
85                     ?: SentenceSuggestionsInfo(emptyArray(), intArrayOf(), intArrayOf())
86         }
87 
onGetSuggestionsnull88         override fun onGetSuggestions(
89             textInfo: TextInfo?,
90             suggestionsLimit: Int
91         ): SuggestionsInfo = withLog(
92                 "MockSpellCheckerSession.onGetSuggestions: ${textInfo?.text}") {
93             if (textInfo == null) return emptySuggestionsInfo()
94             val configuration = MockSpellCheckerConfiguration.parseFrom(
95                     SharedPrefsProvider.get(contentResolver, KEY_CONFIGURATION))
96             return configuration.suggestionRulesList
97                     .find { it.match == textInfo.text }
98                     ?.let { suggestionsInfo(it) }
99                     ?: emptySuggestionsInfo()
100         }
101 
suggestionsInfonull102         private fun suggestionsInfo(rule: SuggestionRule): SuggestionsInfo {
103             return suggestionsInfo(rule, 0, 0)
104         }
105 
suggestionsInfonull106         private fun suggestionsInfo(
107             rule: SuggestionRule,
108             cookie: Int,
109             sequence: Int
110         ): SuggestionsInfo {
111             // Only use attrs in supportedAttributes
112             val attrs = rule.attributes and supportedAttributes
113             // Add prefix if it is passed in getBundle()
114             val prefix = bundle.getString(EXTRAS_KEY_PREFIX)
115             val suggestions = if (prefix != null) {
116                 rule.suggestionsList.map { prefix + it }.toTypedArray()
117             } else {
118                 rule.suggestionsList.toTypedArray()
119             }
120             return SuggestionsInfo(attrs, suggestions, cookie, sequence)
121         }
122 
emptySuggestionsInfonull123         private fun emptySuggestionsInfo() = SuggestionsInfo(0, arrayOf())
124     }
125 
126     companion object {
127         @JvmStatic
128         fun getId(): String =
129                 ComponentName(PACKAGE, MockSpellChecker::class.java.name).flattenToShortString()
130     }
131 }