Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2753,54 +2753,55 @@ class FileDisplayActivity :
super.onStart()
val optionalUser = user
val storageManager = getStorageManager()
if (optionalUser.isPresent && storageManager != null) {
/** Check whether the 'main' OCFile handled by the Activity is contained in the */
// current Account
var file = getFile()
// get parent from path
if (file != null) {
if (file.isDown && file.lastSyncDateForProperties == 0L) {
// upload in progress - right now, files are not inserted in the local
// cache until the upload is successful get parent from path
val parentPath =
file.remotePath.substring(0, file.remotePath.lastIndexOf(file.fileName))
if (storageManager.getFileByPath(parentPath) == null) {
file = null // not able to know the directory where the file is uploading
}
} else {
file = storageManager.getFileByPath(file.remotePath)
// currentDir = null if not in the current Account
}
}
if (file == null) {
// fall back to root folder
file = storageManager.getFileByPath(OCFile.ROOT_PATH) // never returns null
}
setFile(file)

val user = optionalUser.get()
setupDrawer()
if (!optionalUser.isPresent || storageManager == null) return
Copy link
Collaborator

@alperozturk96 alperozturk96 Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey. Thank you for the PR.

if (optionalUser.isPresent && storageManager != null) {
if (!optionalUser.isPresent || storageManager == null) return

These two conditions are logically equivalent — they express the same control flow in opposite forms. Therefore, changing one to the other wouldn’t actually resolve the issue.

The real problem likely occurs when the app is suspended and then resumed, during which the state (such as optionalUser or storageManager) is lost or not properly restored.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two conditions are logically equivalent — they express the same control flow in opposite forms. Therefore, changing one to the other wouldn’t actually resolve the issue.

Switching the conditions permits the early return, which avoids running the later NPE code path though, right? (which in the existing code happens outside the conditional)

The real problem likely occurs when the app is suspended and then resumed, during which the state (such as optionalUser or storageManager) is lost or not properly restored.

I can't argue there. Admittedly did not go down that rabbit hole.

I guess the path forward comes down to two questions:

  • Is the true underlying problem (suspend/resume state) readily fixable in the near future?
  • What's the impact (cons specifically) of returning w/o at least crashing (as proposed in this PR)? I.e., Are we likely introducing new problems or just working around one until we can identify and fix the state matter?

I don't have the answers. Just posing to think about these a bit myself. :) To answer I'd have to take a deeper dive, which admittedly I haven't on this matter.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR doesn’t seem likely to introduce any new issues, so we can go ahead and merge it. It might even help by returning early instead of failing within the condition.

As for the actual fix, it’s not an easy issue to reproduce or verify because of the tightly coupled, nested fragment hierarchy and the complexity of tracking their lifecycles. The NPE seems related to a specific execution flow — in theory, if the optional user is present, the user object should be accessible, but it appears there’s a lifecycle-related issue causing the null reference.

I’ll investigate this further, but thank you for the PR 🙏


mSwitchAccountButton.tag = user.accountName
DisplayUtils.setAvatar(
user,
this,
getResources().getDimension(R.dimen.nav_drawer_menu_avatar_radius),
getResources(),
mSwitchAccountButton,
this
)
val userChanged = (user.accountName != lastDisplayedAccountName)
if (userChanged) {
Log_OC.d(TAG, "Initializing Fragments in onAccountChanged..")
initFragments()
if (file.isFolder && TextUtils.isEmpty(searchQuery)) {
startSyncFolderOperation(file, false)
/** Check whether the 'main' OCFile handled by the Activity is contained in the */
// current Account
var file = getFile()
// get parent from path
if (file != null) {
if (file.isDown && file.lastSyncDateForProperties == 0L) {
// upload in progress - right now, files are not inserted in the local
// cache until the upload is successful get parent from path
val parentPath =
file.remotePath.substring(0, file.remotePath.lastIndexOf(file.fileName))
if (storageManager.getFileByPath(parentPath) == null) {
file = null // not able to know the directory where the file is uploading
}
} else {
updateActionBarTitleAndHomeButton(if (file.isFolder) null else file)
file = storageManager.getFileByPath(file.remotePath)
// currentDir = null if not in the current Account
}
}
if (file == null) {
// fall back to root folder
file = storageManager.getFileByPath(OCFile.ROOT_PATH) // never returns null
}
setFile(file)

val user = optionalUser.get()
setupDrawer()

mSwitchAccountButton.tag = user.accountName
DisplayUtils.setAvatar(
user,
this,
getResources().getDimension(R.dimen.nav_drawer_menu_avatar_radius),
getResources(),
mSwitchAccountButton,
this
)
val userChanged = (user.accountName != lastDisplayedAccountName)
if (userChanged) {
Log_OC.d(TAG, "Initializing Fragments in onAccountChanged..")
initFragments()
if (file.isFolder && TextUtils.isEmpty(searchQuery)) {
startSyncFolderOperation(file, false)
}
} else {
updateActionBarTitleAndHomeButton(if (file.isFolder) null else file)
}

val newLastDisplayedAccountName = optionalUser.orElse(null).accountName
preferences.lastDisplayedAccountName = newLastDisplayedAccountName
Expand All @@ -2809,7 +2810,7 @@ class FileDisplayActivity :
EventBus.getDefault().post(TokenPushEvent())
checkForNewDevVersionNecessary(applicationContext)
}

override fun onRestart() {
super.onRestart()
checkForNewDevVersionNecessary(applicationContext)
Expand Down
Loading