@@ -41,6 +41,14 @@ import android.content.IntentSender
4141import com.hegocre.nextcloudpasswords.utils.encryptValue
4242import com.hegocre.nextcloudpasswords.utils.sha1Hash
4343import com.hegocre.nextcloudpasswords.api.FoldersApi
44+ import com.hegocre.nextcloudpasswords.utils.AutofillData
45+ import com.hegocre.nextcloudpasswords.utils.PasswordAutofillData
46+ import com.hegocre.nextcloudpasswords.utils.SaveData
47+
48+ data class ListDecryptionStateNonNullable <T >(
49+ val decryptedList : List <T > = emptyList(),
50+ val isLoading : Boolean = false
51+ )
4452
4553@TargetApi(Build .VERSION_CODES .O )
4654class NCPAutofillService : AutofillService () {
@@ -60,21 +68,24 @@ class NCPAutofillService : AutofillService() {
6068 val searchByUsername by lazy { preferencesManager.getSearchByUsername() }
6169 val strictUrlMatching by lazy { preferencesManager.getUseStrictUrlMatching() }
6270
63- private lateinit var decryptedPasswordsState: StateFlow <List <Password >>
71+ private lateinit var decryptedPasswordsState: StateFlow <ListDecryptionStateNonNullable <Password >>
72+
73+ private val passwordsDecrypted = MutableStateFlow (false )
6474
6575 override fun onCreate () {
6676 super .onCreate()
6777 decryptedPasswordsState = combine(
6878 passwordController.getPasswords().asFlow(),
6979 apiController.csEv1Keychain.asFlow()
7080 ) { passwords, keychain ->
71- passwords.filter { ! it.trashed && ! it.hidden }.decryptPasswords(keychain)
81+ ListDecryptionStateNonNullable <Password >(isLoading = true )
82+ ListDecryptionStateNonNullable (passwords.filter { ! it.trashed && ! it.hidden }.decryptPasswords(keychain), false )
7283 }
7384 .flowOn(Dispatchers .Default )
7485 .stateIn(
7586 scope = serviceScope,
7687 started = SharingStarted .Eagerly ,
77- initialValue = emptyList( )
88+ initialValue = ListDecryptionStateNonNullable (isLoading = true )
7889 )
7990 }
8091
@@ -122,24 +133,23 @@ class NCPAutofillService : AutofillService() {
122133 return null
123134 }
124135
136+ // TODO: when to sync with server?
125137 // Check Login Status
126- try {
127- userController.getServer()
128- } catch (_: UserException ) {
129- Log .e(TAG , " User not logged in, cannot autofill" )
130- return null
131- }
138+ // try {
139+ // userController.getServer()
140+ // } catch (_: UserException) {
141+ // Log.e(TAG, "User not logged in, cannot autofill")
142+ // return null
143+ // }
132144
133- Log .d(TAG , " User is logged in" )
145+ // Log.d(TAG, "User is logged in")
134146
135147 // Try to open Session
136- if (! apiController.sessionOpen.value && ! apiController.openSession(preferencesManager.getMasterPassword())) {
137- Log .w(TAG , " Session is not open and cannot be opened" )
138- // TODO: stop if we need the decrypted keychain
139- }
140- Log .d(TAG , " Session is open" )
148+ // if (!apiController.sessionOpen.value && !apiController.openSession(preferencesManager.getMasterPassword())) {
149+ // Log.w(TAG, "Session is not open and cannot be opened")
150+ // }
151+ // Log.d(TAG, "Session is open")
141152
142- // TODO: when to update?
143153 // if (apiController.sessionOpen.value) {
144154 // passwordController.syncPasswords()
145155 // }
@@ -150,7 +160,9 @@ class NCPAutofillService : AutofillService() {
150160 Log .d(TAG , " Search hint determined: $searchHint " )
151161
152162 // wait for passwords to be decrypted, then filter by search hint and sort them
153- val filteredList = decryptedPasswordsState.value.filter {
163+ decryptedPasswordsState.first { ! it.isLoading }
164+
165+ val filteredList = decryptedPasswordsState.value.decryptedList.filter {
154166 it.matches(searchHint, strictUrlMatching.first()) ||
155167 (searchByUsername.first() && it.username.contains(searchHint, ignoreCase = true ))
156168 }.let { list ->
@@ -222,10 +234,10 @@ class NCPAutofillService : AutofillService() {
222234 builder.addDataset(
223235 AutofillHelper .buildDataset(
224236 applicationContext,
225- PasswordAutofillData (label = " Create new password" , id = null , username = null , password = null ),
237+ PasswordAutofillData (label = " Create new password" , id = null , username = null , password = null ), // TODO: translation
226238 helper,
227239 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) inlineRequest?.inlinePresentationSpecs?.first() else null ,
228- buildSaveIntent (applicationContext, saveData, true ),
240+ AutofillHelper .buildIntent (applicationContext, 1003 , AutofillData . SaveAutofill (searchHint, saveData, helper.structure) ),
229241 false
230242 )
231243 )
@@ -237,22 +249,22 @@ class NCPAutofillService : AutofillService() {
237249 builder.addDataset(
238250 AutofillHelper .buildDataset(
239251 applicationContext,
240- null ,
252+ PasswordAutofillData (label = " > " , id = null , username = null , password = null ), // TODO use icon
241253 helper,
242254 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) inlineRequest?.inlinePresentationSpecs?.first() else null ,
243- buildMainAppIntent (applicationContext, searchHint),
255+ AutofillHelper .buildIntent (applicationContext, 1004 , AutofillData . ChoosePwd ( searchHint, helper.structure) ),
244256 false
245257 )
246258 )
247259
248260 Log .d(TAG , " Button to open app added to FillResponse" )
249261
250262 // set Save Info, with an optional bundle if delaying the save
251- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O_MR1 ) {
263+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .P ) {
252264 AutofillHelper .buildSaveInfo(helper)?.let { pair ->
253265 builder.setSaveInfo(pair.first)
254266 pair.second?.let { bundle ->
255- builder.setClientState(bundle)
267+ builder.setClientState(bundle)
256268 }
257269 }
258270 }
@@ -279,18 +291,22 @@ class NCPAutofillService : AutofillService() {
279291 }
280292
281293 override fun onSaveRequest (request : SaveRequest , callback : SaveCallback ) {
282- val job = serviceScope.launch {
283- try {
284- val intent: IntentSender ? = withContext(Dispatchers .Default ) {
285- processSaveRequest(request)
294+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .P ) {
295+ val job = serviceScope.launch {
296+ try {
297+ val intent: IntentSender ? = withContext(Dispatchers .Default ) {
298+ processSaveRequest(request)
299+ }
300+ if (intent != null ) callback.onSuccess(intent)
301+ else callback.onFailure(" Unable to complete Save Request" )
302+ } catch (e: CancellationException ) {
303+ throw e
304+ } catch (e: Exception ) {
305+ callback.onFailure(" Error handling save request: ${e.message} " )
286306 }
287- if (intent != null ) callback.onSuccess(intent)
288- else callback.onFailure(" Unable to complete Save Request" )
289- } catch (e: CancellationException ) {
290- throw e
291- } catch (e: Exception ) {
292- callback.onFailure(" Error handling save request: ${e.message} " )
293307 }
308+ } else {
309+ callback.onFailure(" Saving not supported on android < 9.0" )
294310 }
295311 }
296312
@@ -325,63 +341,12 @@ class NCPAutofillService : AutofillService() {
325341 // Determine Search Hint
326342 val searchHint = helper.webDomain ? : getAppLabel(helper.packageName)
327343
328- return buildSaveIntent(applicationContext, prepareSaveData(searchHint, username, password, searchHint))
329- }
330-
331- private suspend fun prepareSaveData (label : String , username : String , password : String , url : String ): SaveData {
332- val keychain = apiController.csEv1Keychain.asFlow().first()
333- val serverSettings = apiController.serverSettings.asFlow().first()
334-
335- return if (keychain != null && serverSettings.encryptionCse != 0 ) SaveData (
336- password = password.encryptValue(keychain.current, keychain),
337- label = label.encryptValue(keychain.current, keychain),
338- username = username.encryptValue(keychain.current, keychain),
339- url = url.encryptValue(keychain.current, keychain),
340- )
341- else SaveData (
342- password = password,
343- label = label,
344- username = username,
345- url = url,
346- )
347- }
348-
349- private fun buildMainAppIntent (context : Context , searchHint : String ): IntentSender {
350- val appIntent = Intent (context, MainActivity ::class .java).apply {
351- putExtra(AUTOFILL_REQUEST , true )
352- putExtra(AUTOFILL_SEARCH_HINT , searchHint)
353- }
354-
355- val intentFlags = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S ) {
356- PendingIntent .FLAG_CANCEL_CURRENT or PendingIntent .FLAG_MUTABLE
357- } else {
358- PendingIntent .FLAG_CANCEL_CURRENT
359- }
360-
361- return PendingIntent .getActivity(
362- context, 1001 , appIntent, intentFlags
363- ).intentSender
364- }
365-
366- private fun buildSaveIntent (context : Context , saveData : SaveData , isAutofill : Boolean = false): IntentSender {
367- val appIntent = Intent (context, MainActivity ::class .java).apply {
368- if (isAutofill) putExtra(AUTOFILL_REQUEST , true )
369- putExtra(SAVE_DATA , saveData)
370- }
371-
372- val intentFlags = PendingIntent .FLAG_CANCEL_CURRENT or PendingIntent .FLAG_IMMUTABLE
373-
374- return PendingIntent .getActivity(
375- context, 1001 , appIntent, intentFlags
376- ).intentSender
344+ return AutofillHelper .buildIntent(applicationContext, 1005 , AutofillData .Save (searchHint, SaveData (searchHint, username, password, searchHint)))
377345 }
378346
379347 companion object {
380348 const val TAG = " NCPAutofillService"
381349 private const val TIMEOUT_MS = 2000L
382- const val AUTOFILL_REQUEST = " autofill_request"
383- const val AUTOFILL_SEARCH_HINT = " autofill_query"
384- const val PASSWORD_ID = " password_id"
385- const val SAVE_DATA = " save_data"
350+ const val AUTOFILL_DATA = " autofill_data"
386351 }
387352}
0 commit comments