Skip to content

Commit 157192e

Browse files
Merge pull request #16452 from nextcloud/fix/session-mix-oc-capability-npe
fix(session-mixing): capabilities npe
2 parents e7e15b6 + e609e0b commit 157192e

12 files changed

Lines changed: 228 additions & 138 deletions

File tree

app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import com.owncloud.android.utils.theme.CapabilityUtils
7272
import com.owncloud.android.utils.theme.ViewThemeUtils
7373
import io.mockk.mockk
7474
import org.junit.After
75+
import org.junit.Assert.fail
7576
import org.junit.Rule
7677
import org.junit.Test
7778
import java.net.URI
@@ -490,7 +491,12 @@ class DialogFragmentIT : AbstractIT() {
490491
json
491492
)
492493

493-
val capability = fda.capabilities.apply {
494+
val optionalCapability = fda.capabilities
495+
if (optionalCapability.isEmpty) {
496+
fail("capabilities is empty")
497+
}
498+
499+
val capability = optionalCapability.get().apply {
494500
richDocuments = CapabilityBooleanType.TRUE
495501
richDocumentsDirectEditing = CapabilityBooleanType.TRUE
496502
richDocumentsTemplatesAvailable = CapabilityBooleanType.TRUE

app/src/main/java/com/nextcloud/client/mixins/SessionMixin.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import android.os.Bundle
1414
import com.nextcloud.client.account.User
1515
import com.nextcloud.client.account.UserAccountManager
1616
import com.nextcloud.utils.extensions.isAnonymous
17+
import com.owncloud.android.lib.common.utils.Log_OC
1718
import com.owncloud.android.lib.resources.status.OCCapability
1819
import com.owncloud.android.utils.theme.CapabilityUtils
1920
import java.util.Optional
@@ -29,10 +30,21 @@ class SessionMixin(private val activity: Activity, private val accountManager: U
2930
var currentAccount: Account = getDefaultAccount()
3031
private set
3132

32-
val capabilities: OCCapability?
33-
get() = getUser()
34-
.map { CapabilityUtils.getCapability(it, activity) }
35-
.orElse(null)
33+
companion object {
34+
private const val TAG = "SessionMixin"
35+
}
36+
37+
fun getCapabilities(): Optional<OCCapability> {
38+
val optionalUser = getUser()
39+
if (optionalUser.isEmpty) {
40+
Log_OC.e(TAG, "user is empty, returning empty capabilities")
41+
return Optional.empty()
42+
}
43+
44+
val user = optionalUser.get()
45+
val capability = CapabilityUtils.getCapability(user, activity)
46+
return Optional.of(capability)
47+
}
3648

3749
fun setAccount(account: Account) {
3850
val validAccount = (accountManager.setCurrentOwnCloudAccount(account.name))

app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,12 @@ class ComposeActivity : DrawerActivity() {
130130
val dao = NextcloudDatabase.instance().assistantDao()
131131
val sessionId = (currentScreen as? ComposeDestination.AssistantScreen)?.sessionId
132132
val client = nextcloudClient ?: return
133-
val remoteRepository = AssistantRemoteRepositoryImpl(client, capabilities)
133+
val optionalCapability = capabilities
134+
if (optionalCapability.isEmpty) {
135+
return
136+
}
137+
val capability = optionalCapability.get()
138+
val remoteRepository = AssistantRemoteRepositoryImpl(client, capability)
134139

135140
AssistantScreen(
136141
composeViewModel = composeViewModel,
@@ -145,7 +150,7 @@ class ComposeActivity : DrawerActivity() {
145150
remoteRepository = ConversationRemoteRepositoryImpl(client)
146151
),
147152
activity = this,
148-
capability = capabilities
153+
capability = capability
149154
)
150155
}
151156

app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ protected void startAccountCreation() {
164164
* @return Capabilities of the server where the current OC account lives. Null if the account is not
165165
* set yet.
166166
*/
167-
public OCCapability getCapabilities() {
167+
public Optional<OCCapability> getCapabilities() {
168168
return sessionMixin.getCapabilities();
169169
}
170170

app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -327,12 +327,10 @@ private void themeBottomNavigationMenu() {
327327

328328
@SuppressFBWarnings("RV")
329329
private void checkAssistantBottomNavigationMenu() {
330-
final var capability = getCapabilities();
331-
boolean isAssistantAvailable;
332-
if (capability == null) {
333-
isAssistantAvailable = false;
334-
} else {
335-
isAssistantAvailable = capability.getAssistant().isTrue();
330+
final var optionalCapabilities = getCapabilities();
331+
boolean isAssistantAvailable = false;
332+
if (optionalCapabilities.isPresent()) {
333+
isAssistantAvailable = optionalCapabilities.get().getAssistant().isTrue();
336334
}
337335

338336
bottomNavigationView
@@ -415,26 +413,28 @@ private void setupQuotaElement() {
415413
public void updateHeader() {
416414
final var account = getAccount();
417415
boolean isClientBranded = getResources().getBoolean(R.bool.is_branded_client);
418-
final OCCapability capability = getCapabilities();
419-
420-
if (capability != null && account != null && capability.getServerBackground() != null && !isClientBranded) {
421-
int primaryColor = themeColorUtils.unchangedPrimaryColor(account, this);
422-
String serverLogoURL = capability.getServerLogo();
423-
424-
// set background to primary color
425-
LinearLayout drawerHeader = mNavigationViewHeader.findViewById(R.id.drawer_header_view);
426-
drawerHeader.setBackgroundColor(primaryColor);
427-
428-
if (!TextUtils.isEmpty(serverLogoURL) && URLUtil.isValidUrl(serverLogoURL)) {
429-
Target<Drawable> target = createSVGLogoTarget(primaryColor, capability);
430-
getClientRepository().getNextcloudClient(nextcloudClient -> {
431-
GlideHelper.INSTANCE.loadIntoTarget(DrawerActivity.this,
432-
nextcloudClient,
433-
serverLogoURL,
434-
target,
435-
R.drawable.background);
436-
return Unit.INSTANCE;
437-
});
416+
final var optionalCapability = getCapabilities();
417+
if (optionalCapability.isPresent()) {
418+
final var capability = optionalCapability.get();
419+
if (account != null && capability.getServerBackground() != null && !isClientBranded) {
420+
int primaryColor = themeColorUtils.unchangedPrimaryColor(account, this);
421+
String serverLogoURL = capability.getServerLogo();
422+
423+
// set background to primary color
424+
LinearLayout drawerHeader = mNavigationViewHeader.findViewById(R.id.drawer_header_view);
425+
drawerHeader.setBackgroundColor(primaryColor);
426+
427+
if (!TextUtils.isEmpty(serverLogoURL) && URLUtil.isValidUrl(serverLogoURL)) {
428+
Target<Drawable> target = createSVGLogoTarget(primaryColor, capability);
429+
getClientRepository().getNextcloudClient(nextcloudClient -> {
430+
GlideHelper.INSTANCE.loadIntoTarget(DrawerActivity.this,
431+
nextcloudClient,
432+
serverLogoURL,
433+
target,
434+
R.drawable.background);
435+
return Unit.INSTANCE;
436+
});
437+
}
438438
}
439439
}
440440

@@ -514,8 +514,14 @@ private void showTopBanner(ConstraintLayout banner) {
514514

515515
moreView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppStore("Nextcloud", true, this));
516516
assistantView.setOnClickListener(v -> startAssistantScreen());
517-
if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) {
518-
assistantView.setVisibility(View.VISIBLE);
517+
final var optionalCapabilities = getCapabilities();
518+
if (optionalCapabilities.isPresent()) {
519+
final var capabilities = optionalCapabilities.get();
520+
if (capabilities.getAssistant().isTrue()) {
521+
assistantView.setVisibility(View.VISIBLE);
522+
} else {
523+
assistantView.setVisibility(View.GONE);
524+
}
519525
} else {
520526
assistantView.setVisibility(View.GONE);
521527
}
@@ -582,13 +588,16 @@ private void setupDrawerMenu(NavigationView navigationView) {
582588
}
583589

584590
private void filterDrawerMenu(final Menu menu, @NonNull final User user) {
585-
OCCapability capability = getCapabilities();
591+
final var optionalCapability = getCapabilities();
592+
if (optionalCapability.isPresent()) {
593+
final var capability = optionalCapability.get();
594+
DrawerMenuUtil.filterTrashbinMenuItem(menu, capability);
595+
DrawerMenuUtil.filterActivityMenuItem(menu, capability);
596+
DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability);
597+
DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources());
598+
}
586599

587600
DrawerMenuUtil.filterSearchMenuItems(menu, user, getResources());
588-
DrawerMenuUtil.filterTrashbinMenuItem(menu, capability);
589-
DrawerMenuUtil.filterActivityMenuItem(menu, capability);
590-
DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability);
591-
DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources());
592601
DrawerMenuUtil.setupHomeMenuItem(menu, getResources());
593602
DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, !getResources().getBoolean(R.bool.participate_enabled));
594603
DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !getResources().getBoolean(R.bool.shared_enabled));
@@ -1371,11 +1380,13 @@ public void fetchExternalLinks(final boolean force) {
13711380

13721381
Thread t = new Thread(() -> {
13731382
// fetch capabilities as early as possible
1374-
final OCCapability capability = getCapabilities();
1375-
if ((capability == null || capability.getAccountName() == null || !capability.getAccountName().isEmpty())
1376-
&& getStorageManager() != null) {
1377-
GetCapabilitiesOperation getCapabilities = new GetCapabilitiesOperation(getStorageManager());
1378-
getCapabilities.execute(getBaseContext());
1383+
final var optionalCapability = getCapabilities();
1384+
if (optionalCapability.isPresent()) {
1385+
final var capability = optionalCapability.get();
1386+
if ((capability.getAccountName() == null || !capability.getAccountName().isEmpty()) && getStorageManager() != null) {
1387+
GetCapabilitiesOperation getCapabilities = new GetCapabilitiesOperation(getStorageManager());
1388+
getCapabilities.execute(getBaseContext());
1389+
}
13791390
}
13801391

13811392
if (getStorageManager() != null && CapabilityUtils.getCapability(user, this)

app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@
8888
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
8989
import com.owncloud.android.ui.events.DialogEvent;
9090
import com.owncloud.android.ui.events.DialogEventType;
91-
import com.owncloud.android.ui.events.FavoriteEvent;
9291
import com.owncloud.android.ui.fragment.FileDetailFragment;
9392
import com.owncloud.android.ui.fragment.FileDetailSharingFragment;
9493
import com.owncloud.android.ui.fragment.OCFileListFragment;
@@ -887,20 +886,20 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope
887886
} else {
888887
// Detect Failure (403) --> maybe needs password
889888
String password = operation.getPassword();
890-
if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_FORBIDDEN &&
891-
TextUtils.isEmpty(password) &&
892-
getCapabilities().getFilesSharingPublicEnabled().isUnknown()) {
893-
// Was tried without password, but not sure that it's optional.
894-
895-
// Try with password before giving up; see also ShareFileFragment#OnShareViaLinkListener
896-
if (sharingFragment != null && sharingFragment.isAdded()) {
897-
// only if added to the view hierarchy
898-
899-
sharingFragment.requestPasswordForShareViaLink(true,
900-
getCapabilities().getFilesSharingPublicAskForOptionalPassword()
901-
.isTrue());
889+
final var optionalCapabilities = getCapabilities();
890+
891+
if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_FORBIDDEN && TextUtils.isEmpty(password)) {
892+
if (optionalCapabilities.isPresent()) {
893+
final var capabilities = optionalCapabilities.get();
894+
if (capabilities.getFilesSharingPublicEnabled().isUnknown()) {
895+
// Was tried without password, but not sure that it's optional.
896+
// Try with password before giving up; see also ShareFileFragment#OnShareViaLinkListener
897+
if (sharingFragment != null && sharingFragment.isAdded()) {
898+
// only if added to the view hierarchy
899+
sharingFragment.requestPasswordForShareViaLink(true, capabilities.getFilesSharingPublicAskForOptionalPassword().isTrue());
900+
}
901+
}
902902
}
903-
904903
} else {
905904
if (sharingFragment != null) {
906905
sharingFragment.refreshSharesFromDB();

app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -448,13 +448,16 @@ class FileDisplayActivity :
448448

449449
private fun checkOutdatedServer() {
450450
val user = getUser()
451+
val optionalCapability = capabilities
452+
451453
// show outdated warning
452454
if (user.isPresent &&
455+
optionalCapability.isPresent &&
453456
CapabilityUtils.checkOutdatedWarning(
454457
getResources(),
455458
user.get().server.version,
456-
capabilities.extendedSupport.isTrue,
457-
capabilities.hasValidSubscription.isTrue
459+
optionalCapability.get().extendedSupport.isTrue,
460+
optionalCapability.get().hasValidSubscription.isTrue
458461
)
459462
) {
460463
DisplayUtils.showServerOutdatedSnackbar(this, Snackbar.LENGTH_LONG)
@@ -1084,28 +1087,38 @@ class FileDisplayActivity :
10841087

10851088
connectivityService.isNetworkAndServerAvailable { result: Boolean? ->
10861089
if (result == true) {
1087-
val isValidFolderPath = remotePathBase?.let { checkFolderPath(it, capabilities, this) }
1088-
if (isValidFolderPath == false) {
1089-
DisplayUtils.showSnackMessage(
1090-
this,
1091-
R.string.file_name_validator_error_contains_reserved_names_or_invalid_characters
1090+
val optionalCapabilities = capabilities
1091+
if (optionalCapabilities.isPresent) {
1092+
val isValidFolderPath =
1093+
remotePathBase?.let {
1094+
checkFolderPath(
1095+
it,
1096+
optionalCapabilities.get(),
1097+
this
1098+
)
1099+
}
1100+
if (isValidFolderPath == false) {
1101+
DisplayUtils.showSnackMessage(
1102+
this,
1103+
R.string.file_name_validator_error_contains_reserved_names_or_invalid_characters
1104+
)
1105+
return@isNetworkAndServerAvailable
1106+
}
1107+
1108+
FileUploadHelper.instance().uploadNewFiles(
1109+
user.orElseThrow(
1110+
Supplier { RuntimeException() }
1111+
),
1112+
filePaths,
1113+
decryptedRemotePaths,
1114+
behaviour,
1115+
true,
1116+
UploadFileOperation.CREATED_BY_USER,
1117+
false,
1118+
false,
1119+
NameCollisionPolicy.ASK_USER
10921120
)
1093-
return@isNetworkAndServerAvailable
10941121
}
1095-
1096-
FileUploadHelper.Companion.instance().uploadNewFiles(
1097-
user.orElseThrow(
1098-
Supplier { RuntimeException() }
1099-
),
1100-
filePaths,
1101-
decryptedRemotePaths,
1102-
behaviour,
1103-
true,
1104-
UploadFileOperation.CREATED_BY_USER,
1105-
false,
1106-
false,
1107-
NameCollisionPolicy.ASK_USER
1108-
)
11091122
} else {
11101123
fileDataStorageManager.addCreateFileOfflineOperation(filePaths, decryptedRemotePaths)
11111124
}
@@ -2450,7 +2463,12 @@ class FileDisplayActivity :
24502463
}
24512464

24522465
private fun fetchRecommendedFilesIfNeeded(ignoreETag: Boolean, folder: OCFile?) {
2453-
if (folder?.isRootDirectory == false || capabilities == null || capabilities.recommendations.isFalse) {
2466+
val optionalCapabilities = capabilities
2467+
if (optionalCapabilities.isEmpty) {
2468+
return
2469+
}
2470+
2471+
if (folder?.isRootDirectory == false || optionalCapabilities.get().recommendations.isFalse) {
24542472
return
24552473
}
24562474

app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,14 +376,24 @@ open class FolderPickerActivity :
376376
updateNavigationElementsInActionBar()
377377
}
378378

379+
@Suppress("ReturnCount")
379380
private fun toggleChooseEnabled() {
380381
if (this is FilePickerActivity) {
381382
return
382383
}
383384

384385
val selectedFolderPathTitle = getSelectedFolderPathTitle()
386+
val optionalCapabilities = capabilities
387+
if (optionalCapabilities.isEmpty) {
388+
return
389+
}
390+
385391
val isFolderPathValid = if (selectedFolderPathTitle != null) {
386-
FileNameValidator.checkFolderPath(selectedFolderPathTitle, capabilities, this)
392+
FileNameValidator.checkFolderPath(
393+
selectedFolderPathTitle,
394+
optionalCapabilities.get(),
395+
this
396+
)
387397
} else {
388398
true
389399
}

0 commit comments

Comments
 (0)