[PB-5926]: show Internxt Drive in SAF picker per session state#430
[PB-5926]: show Internxt Drive in SAF picker per session state#430terrerox wants to merge 3 commits intofeature/PB-5925-kotlin-api-clientfrom
Conversation
InternxtDocumentsProvider.queryRoots() now reads credentials from EncryptedSharedPreferences via a new InternxtAuthManager and returns an empty cursor when no user is signed in, so "Internxt Drive" only appears in the system file picker while the app has an active session; credentials are written by a new RN bridge (InternxtAuthCredentialsModule) from signInThunk and refreshTokensThunk and cleared from signOutThunk, with both paths firing contentResolver.notifyChange(rootsUri) so the picker refreshes without restarting the app, while queryRoots() itself stays strictly synchronous and local-only to avoid freezing the system picker.
0f04755 to
5fe9130
Compare
|
| .putString(KEY_DRIVE_BASE_URL, creds.driveBaseUrl) | ||
| .putString(KEY_BRIDGE_BASE_URL, creds.bridgeBaseUrl) | ||
| .putString(KEY_DESKTOP_TOKEN, creds.desktopToken) | ||
| .apply() |
There was a problem hiding this comment.
apply is async, maybe for this it is preferable use commit
| rootFolderUuid: user.rootFolderUuid || user.rootFolderId, | ||
| email: user.email, | ||
| driveBaseUrl: appService.constants.DRIVE_NEW_API_URL, | ||
| bridgeBaseUrl: appService.constants.BRIDGE_URL, |
There was a problem hiding this comment.
The desktopToken is missing
| bearerToken: token, | ||
| userId: user.userId, | ||
| bridgeUser: user.bridgeUser, | ||
| rootFolderUuid: user.rootFolderUuid || user.rootFolderId, |
There was a problem hiding this comment.
rootFolderId as fallback is wrong, remember that rootFolderId is the numeric remote database id and the rootFolderUuid is the uuid one. For api v2 we use rootFolderUuid :)
| import com.internxt.cloud.BuildConfig | ||
| import com.internxt.cloud.documents.api.AuthConfig | ||
|
|
||
| class InternxtAuthManager(private val prefs: SharedPreferences) { |
There was a problem hiding this comment.
Why is this AuthManager in docuemtns instead of the other auth directory?
| private lateinit var authManager: InternxtAuthManager | ||
|
|
||
| override fun onCreate(): Boolean { | ||
| authManager = InternxtAuthManager.create(context!!.applicationContext) |
There was a problem hiding this comment.
Be carefull with not null assertion operator, I think this logic could lead in a unexpected bugs, better return false if there are no context
val ctx = context ?: return false
authManager = InternxtAuthManager.create(ctx.applicationContext)
return true
| fun create(context: Context): InternxtAuthManager { | ||
| val masterKey = MasterKey.Builder(context) | ||
| .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) | ||
| .build() | ||
| val prefs = EncryptedSharedPreferences.create( | ||
| context, | ||
| PREFS_FILE, | ||
| masterKey, | ||
| EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, | ||
| EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, | ||
| ) | ||
| return InternxtAuthManager(prefs) | ||
| } |
There was a problem hiding this comment.
I think that is recommendable to wrap this logic in a try/catch and return null on failure so the provider degrades gracefully instead of crashing
fun create(context: Context): InternxtAuthManager? {
return try {
...
} catch (e: GeneralSecurityException) {
Log.e(TAG, "Keystore unavailable, SAF auth disabled", e)
null
} catch (e: IOException) {
Log.e(TAG, "Could not open encrypted prefs, SAF auth disabled", e)
null
}
}
If implement something like the example, then we need to handle the nullable return in onCreate().
We could manage as if create() returns null, onCreate return false



InternxtDocumentsProvider.queryRoots() now reads credentials from EncryptedSharedPreferences via a new InternxtAuthManager and returns an empty cursor when no user is signed in, so "Internxt Drive" only appears in the system file picker while the app has an active session; credentials are written by a new RN bridge (InternxtAuthCredentialsModule) from signInThunk and refreshTokensThunk and cleared from signOutThunk, with both paths firing contentResolver.notifyChange(rootsUri) so the picker refreshes without restarting the app, while queryRoots() itself stays strictly synchronous and local-only to avoid freezing the system picker.