diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.kt b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.kt
index 9612adc09060..745696d45571 100644
--- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.kt
+++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.kt
@@ -54,7 +54,7 @@ import com.owncloud.android.ui.activity.BaseActivity
import com.owncloud.android.ui.activity.ConflictsResolveActivity
import com.owncloud.android.ui.activity.ContactsPreferenceActivity
import com.owncloud.android.ui.activity.CopyToClipboardActivity
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity
import com.owncloud.android.ui.activity.ExternalSiteWebView
import com.owncloud.android.ui.activity.FileActivity
diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt
index d6108fbd76be..3251a30cb908 100644
--- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt
+++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt
@@ -33,7 +33,7 @@ import com.nextcloud.ui.ClientIntegrationScreen
import com.nextcloud.utils.extensions.getParcelableArgument
import com.owncloud.android.R
import com.owncloud.android.databinding.ActivityComposeBinding
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
class ComposeActivity : DrawerActivity() {
@@ -81,7 +81,8 @@ class ComposeActivity : DrawerActivity() {
composeViewModel.updateSelectedText(text.toString())
}
- override fun getMenuItemId(): Int = R.id.nav_assistant
+ override val menuItemId: Int
+ get() = R.id.nav_assistant
override fun onResume() {
super.onResume()
diff --git a/app/src/main/java/com/nextcloud/ui/dialog/account/ChooseAccountDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/dialog/account/ChooseAccountDialogFragment.kt
index 89b03994ca8d..e4475ce85055 100644
--- a/app/src/main/java/com/nextcloud/ui/dialog/account/ChooseAccountDialogFragment.kt
+++ b/app/src/main/java/com/nextcloud/ui/dialog/account/ChooseAccountDialogFragment.kt
@@ -37,7 +37,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.lib.resources.users.Status
import com.owncloud.android.ui.StatusDrawable
import com.owncloud.android.ui.activity.BaseActivity
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.adapter.UserListAdapter
import com.owncloud.android.ui.adapter.UserListItem
import com.owncloud.android.utils.theme.ViewThemeUtils
diff --git a/app/src/main/java/com/nextcloud/utils/extensions/DrawerActivityExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/DrawerActivityExtensions.kt
index 800fbeb9c9dd..d09b812affa5 100644
--- a/app/src/main/java/com/nextcloud/utils/extensions/DrawerActivityExtensions.kt
+++ b/app/src/main/java/com/nextcloud/utils/extensions/DrawerActivityExtensions.kt
@@ -10,7 +10,7 @@ package com.nextcloud.utils.extensions
import android.content.Intent
import com.owncloud.android.MainApp
import com.owncloud.android.R
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.ui.navigation.model.ActionBarStyle
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java
index e73e4fbcfc1e..fc5d538659d9 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java
+++ b/app/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java
@@ -49,10 +49,10 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
private final MixinRegistry mixinRegistry = new MixinRegistry();
private SessionMixin sessionMixin;
- @Inject UserAccountManager accountManager;
- @Inject AppPreferences preferences;
- @Inject FileDataStorageManager fileDataStorageManager;
- @Inject ClientRepository clientRepository;
+ @Inject protected UserAccountManager accountManager;
+ @Inject protected AppPreferences preferences;
+ @Inject protected FileDataStorageManager fileDataStorageManager;
+ @Inject protected ClientRepository clientRepository;
private final AppPreferences.Listener onPreferencesChanged = new AppPreferences.Listener() {
@Override
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
deleted file mode 100644
index 1c5f1731084d..000000000000
--- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
+++ /dev/null
@@ -1,1568 +0,0 @@
-/*
- * Nextcloud - Android Client
- *
- * SPDX-FileCopyrightText: 2021-2024 TSI-mc
- * SPDX-FileCopyrightText: 2020 Infomaniak Network SA
- * SPDX-FileCopyrightText: 2020 Chris Narkiewicz
- * SPDX-FileCopyrightText: 2017 Tobias Kaminsky
- * SPDX-FileCopyrightText: 2016 Andy Scherzinger
- * SPDX-FileCopyrightText: 2016 Nextcloud
- * SPDX-FileCopyrightText: 2016 ownCloud Inc.
- * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only)
- */
-package com.owncloud.android.ui.activity;
-
-import android.accounts.Account;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.PictureDrawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.URLUtil;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.bumptech.glide.request.target.CustomTarget;
-import com.bumptech.glide.request.target.Target;
-import com.bumptech.glide.request.transition.Transition;
-import com.google.android.material.bottomnavigation.BottomNavigationView;
-import com.google.android.material.button.MaterialButton;
-import com.google.android.material.navigation.NavigationView;
-import com.google.android.material.progressindicator.LinearProgressIndicator;
-import com.nextcloud.android.common.core.utils.ecosystem.EcosystemApp;
-import com.nextcloud.android.common.core.utils.ecosystem.EcosystemManager;
-import com.nextcloud.android.common.ui.color.ColorUtil;
-import com.nextcloud.android.common.ui.theme.utils.ColorRole;
-import com.nextcloud.android.common.ui.util.PlatformThemeUtil;
-import com.nextcloud.client.account.User;
-import com.nextcloud.client.di.Injectable;
-import com.nextcloud.client.files.DeepLinkConstants;
-import com.nextcloud.client.network.ClientFactory;
-import com.nextcloud.client.onboarding.FirstRunActivity;
-import com.nextcloud.client.player.media3.PlaybackModel;
-import com.nextcloud.client.preferences.AppPreferences;
-import com.nextcloud.client.systembars.SystemBarBackgroundCallbacks;
-import com.nextcloud.client.utils.IntentUtil;
-import com.nextcloud.common.NextcloudClient;
-import com.nextcloud.ui.dialog.account.ChooseAccountDialogFragment;
-import com.nextcloud.ui.composeActivity.ComposeActivity;
-import com.nextcloud.ui.composeActivity.ComposeDestination;
-import com.nextcloud.utils.GlideHelper;
-import com.nextcloud.utils.HumanReadableFormatter;
-import com.nextcloud.utils.LinkHelper;
-import com.nextcloud.utils.SnackbarUtil;
-import com.nextcloud.utils.avatar.AvatarGenerationListener;
-import com.nextcloud.utils.avatar.AvatarGenerator;
-import com.nextcloud.utils.extensions.ActivityExtensionsKt;
-import com.nextcloud.utils.extensions.DrawerActivityExtensionsKt;
-import com.nextcloud.utils.extensions.NavigationViewExtensionsKt;
-import com.nextcloud.utils.extensions.ViewExtensionsKt;
-import com.nextcloud.utils.mdm.MDMConfig;
-import com.nextcloud.utils.view.ScreenMetrics;
-import com.owncloud.android.MainApp;
-import com.owncloud.android.R;
-import com.owncloud.android.authentication.PassCodeManager;
-import com.owncloud.android.datamodel.ArbitraryDataProvider;
-import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
-import com.owncloud.android.datamodel.ExternalLinksProvider;
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.lib.common.ExternalLink;
-import com.owncloud.android.lib.common.ExternalLinkType;
-import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
-import com.owncloud.android.lib.common.Quota;
-import com.owncloud.android.lib.common.UserInfo;
-import com.owncloud.android.lib.common.accounts.ExternalLinksOperation;
-import com.owncloud.android.lib.common.operations.RemoteOperation;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.files.SearchRemoteOperation;
-import com.owncloud.android.lib.resources.status.OCCapability;
-import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation;
-import com.owncloud.android.operations.GetCapabilitiesOperation;
-import com.owncloud.android.ui.events.AccountRemovedEvent;
-import com.owncloud.android.ui.events.ChangeMenuEvent;
-import com.owncloud.android.ui.events.SearchEvent;
-import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment;
-import com.owncloud.android.ui.fragment.GalleryFragment;
-import com.owncloud.android.ui.fragment.OCFileListFragment;
-import com.owncloud.android.ui.fragment.albums.AlbumItemsFragment;
-import com.owncloud.android.ui.fragment.albums.AlbumsFragment;
-import com.owncloud.android.ui.navigation.NavigatorActivity;
-import com.owncloud.android.ui.navigation.NavigatorScreen;
-import com.owncloud.android.ui.navigation.model.ActionBarStyle;
-import com.owncloud.android.utils.BitmapUtils;
-import com.owncloud.android.utils.DrawableUtil;
-import com.owncloud.android.utils.DrawerMenuUtil;
-import com.owncloud.android.utils.FilesSyncHelper;
-import com.owncloud.android.utils.theme.CapabilityUtils;
-
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
-
-import javax.inject.Inject;
-
-import androidx.activity.OnBackPressedCallback;
-import androidx.annotation.IdRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.core.content.ContextCompat;
-import androidx.core.content.res.ResourcesCompat;
-import androidx.core.view.GravityCompat;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentTransaction;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import hct.Hct;
-import kotlin.Unit;
-
-/**
- * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback
- * generation.
- */
-public abstract class DrawerActivity extends ToolbarActivity
- implements AvatarGenerationListener, Injectable {
-
- private static final String TAG = DrawerActivity.class.getSimpleName();
- private static final String KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE";
- private static final int ACTION_MANAGE_ACCOUNTS = 101;
- private static final int MENU_ORDER_EXTERNAL_LINKS = 3;
- private static final int MENU_ITEM_EXTERNAL_LINK = 111;
- private static final int MAX_LOGO_SIZE_PX = 1000;
- private static final int RELATIVE_THRESHOLD_WARNING = 80;
- public static final int REQ_ALL_FILES_ACCESS = 3001;
- public static final int REQ_MEDIA_ACCESS = 3000;
-
- @Inject
- protected PlaybackModel playbackModel;
-
- /**
- * Reference to the drawer layout.
- */
- private DrawerLayout mDrawerLayout;
-
- /**
- * Reference to the drawer toggle.
- */
- protected ActionBarDrawerToggle mDrawerToggle;
-
- /**
- * Reference to the navigation view header.
- */
- private View mNavigationViewHeader;
-
- /**
- * Flag to signal if the account chooser is active.
- */
- private boolean mIsAccountChooserActive;
-
- /**
- * container layout of the quota view.
- */
- private LinearLayout mQuotaView;
-
- /**
- * progress bar of the quota view.
- */
- private LinearProgressIndicator mQuotaProgressBar;
-
- /**
- * text view of the quota view.
- */
- private TextView mQuotaTextPercentage;
- private TextView mQuotaTextLink;
-
- /**
- * runnable that will be executed after the drawer has been closed.
- */
- private Runnable pendingRunnable;
-
- private ExternalLinksProvider externalLinksProvider;
- private ArbitraryDataProvider arbitraryDataProvider;
-
- private BottomNavigationView bottomNavigationView;
- private NavigationView drawerNavigationView;
-
- /**
- * Returns the navigation drawer menu item ID that represents
- * the current activity.
- *
- *
- * This method is used by the DrawerActivity to determine
- * which drawer item should be highlighted (checked) when the
- * activity is visible.
- *
- *
- *
- * Subclasses that are displayed within the drawer must override
- * this method and return their corresponding menu item ID
- * (e.g. R.id.nav_gallery, R.id.nav_settings).
- *
- *
- *
- * The default implementation returns {@link R.id#nav_all_files}.
- *
- *
- * @return the menu item ID to be marked as selected in the drawer
- */
- protected int getMenuItemId() {
- return R.id.nav_all_files;
- }
-
- private EcosystemManager ecosystemManager;
-
- @Inject
- AppPreferences preferences;
-
- @Inject
- protected ClientFactory clientFactory;
-
- @Inject
- public AvatarGenerator avatarGenerator;
-
- @Inject
- ColorUtil colorUtil;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
- super.onCreate(savedInstanceState, persistentState);
- addOnBackPressedCallback();
- }
-
- /**
- * Initializes the drawer and its content. This method needs to be called after the content view has been set.
- */
- protected void setupDrawer(int id) {
- if (mDrawerLayout == null) {
- mDrawerLayout = findViewById(R.id.drawer_layout);
- }
-
- if (drawerNavigationView == null) {
- drawerNavigationView = findViewById(R.id.nav_view);
- }
-
- if (drawerNavigationView != null) {
- viewThemeUtils.files.colorNavigationView(drawerNavigationView);
-
- // Setting up drawer header
- mNavigationViewHeader = drawerNavigationView.getHeaderView(0);
- updateHeader();
-
- setupDrawerMenu(drawerNavigationView);
- getAndDisplayUserQuota();
- setupQuotaElement();
- highlightNavigationViewItem(id);
- }
-
- setupDrawerToggle();
-
- if (getSupportActionBar() != null) {
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- }
-
- if (bottomNavigationView == null) {
- bottomNavigationView = findViewById(R.id.bottom_navigation);
- }
-
- if (bottomNavigationView != null) {
- themeBottomNavigationMenu();
- checkAssistantBottomNavigationMenu();
- handleBottomNavigationViewClicks();
- highlightNavigationViewItem(id);
- }
- }
-
- /**
- * Highlights (checks) the given menu item ID in the app's navigation bars.
- *
- *
- * This method updates both the navigation drawer (`drawerNavigationView`) and
- * the bottom navigation bar (`bottomNavigationView`).
- *
- *
- *
- * This method is needs to be called from onResume() of child activities with all possible menu item ids.
- * This fixes:
- *
- * - When navigating back from another activity (e.g., Activity B → Activity A),
- * the previously selected menu item remains highlighted.
- *
- *
- *
- * @param menuItemId the ID of the menu item to mark as selected/highlighted
- */
- public void highlightNavigationViewItem(int menuItemId) {
- NavigationViewExtensionsKt.highlightNavigationView(drawerNavigationView,
- bottomNavigationView,
- menuItemId);
- SystemBarBackgroundCallbacks.apply(this, viewThemeUtils);
- Log_OC.d(TAG, "New menu item is: " + menuItemId);
- }
-
- private void themeBottomNavigationMenu() {
- viewThemeUtils.platform.colorBottomNavigationView(bottomNavigationView);
- }
-
- @SuppressFBWarnings("RV")
- private void checkAssistantBottomNavigationMenu() {
- final var optionalCapabilities = getCapabilities();
- boolean isAssistantAvailable = false;
- if (optionalCapabilities.isPresent()) {
- isAssistantAvailable = optionalCapabilities.get().getAssistant().isTrue();
- }
-
- bottomNavigationView
- .getMenu()
- .findItem(R.id.nav_assistant)
- .setVisible(isAssistantAvailable);
- }
-
- private void openFavoritesTab() {
- resetOnlyPersonalAndOnDevice();
- showActionBar(ActionBarStyle.Plain);
- SearchEvent searchEvent = new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH);
- launchActivityForSearch(searchEvent, R.id.nav_favorites);
- }
-
- private void openMediaTab(int menuItemId) {
- GalleryFragment.Companion.clearSavedViewState();
- resetOnlyPersonalAndOnDevice();
- showActionBar(ActionBarStyle.Plain);
- startPhotoSearch(menuItemId);
- }
-
- @Nullable
- public OCFileListFragment getOCFileListFragment() {
- Fragment fragment = ActivityExtensionsKt.lastFragment(this);
- if (fragment instanceof OCFileListFragment fileListFragment) {
- return fileListFragment;
- }
-
- fragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
- if (fragment instanceof OCFileListFragment fileListFragment) {
- return fileListFragment;
- }
-
- return null;
- }
-
- private void exitSelectionMode() {
- Fragment fragment = getOCFileListFragment();
- if (fragment instanceof OCFileListFragment fileListFragment) {
- fileListFragment.exitSelectionMode();
- }
- }
-
- private void setupDrawerToggle() {
- mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {
- public void onDrawerClosed(View view) {
- super.onDrawerClosed(view);
-
- if (pendingRunnable != null) {
- new Handler().post(pendingRunnable);
- pendingRunnable = null;
- }
-
- closeDrawer();
- }
- };
-
- mDrawerLayout.addDrawerListener(mDrawerToggle);
- mDrawerToggle.setDrawerIndicatorEnabled(true);
- mDrawerToggle.setDrawerSlideAnimationEnabled(true);
- final Drawable backArrow = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_arrow_back, null);
- if (backArrow != null) {
- viewThemeUtils.platform.tintToolbarArrowDrawable(this, mDrawerToggle, backArrow);
- }
- }
-
- /**
- * setup quota elements of the drawer.
- */
- private void setupQuotaElement() {
- mQuotaView = (LinearLayout) findQuotaViewById(R.id.drawer_quota);
- mQuotaProgressBar = (LinearProgressIndicator) findQuotaViewById(R.id.drawer_quota_ProgressBar);
- mQuotaTextPercentage = (TextView) findQuotaViewById(R.id.drawer_quota_percentage);
- mQuotaTextLink = (TextView) findQuotaViewById(R.id.drawer_quota_link);
- viewThemeUtils.material.colorProgressBar(mQuotaProgressBar, ColorRole.PRIMARY);
- mQuotaProgressBar.setTrackStopIndicatorSize(0);
- viewThemeUtils.platform.colorViewBackground(mQuotaView);
- }
-
- public void updateHeader() {
- final var account = getAccount();
- boolean isClientBranded = getResources().getBoolean(R.bool.is_branded_client);
- final var optionalCapability = getCapabilities();
- if (optionalCapability.isPresent()) {
- final var capability = optionalCapability.get();
- if (account != null && capability.getServerBackground() != null && !isClientBranded) {
- int primaryColor = getServerPrimaryColor(account);
- String serverLogoURL = capability.getServerLogo();
-
- // set background to primary color
- LinearLayout drawerHeader = mNavigationViewHeader.findViewById(R.id.drawer_header_view);
- drawerHeader.setBackgroundColor(primaryColor);
-
- if (!TextUtils.isEmpty(serverLogoURL) && URLUtil.isValidUrl(serverLogoURL)) {
- Target target = createSVGLogoTarget(primaryColor, capability);
- GlideHelper.INSTANCE.loadIntoTarget(this,
- accountManager.getCurrentOwnCloudAccount(),
- serverLogoURL,
- target,
- R.drawable.background);
- }
- }
- }
-
- // hide ecosystem apps according to user preference or in branded client
- ConstraintLayout banner = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps);
- boolean shouldHideTopBanner = isClientBranded || !preferences.isShowEcosystemApps();
-
- if (shouldHideTopBanner) {
- hideTopBanner(banner);
- } else {
- showTopBanner(banner);
- }
- }
-
- private Target createSVGLogoTarget(int primaryColor, OCCapability capability) {
- return new CustomTarget<>() {
- @Override
- public void onResourceReady(@NonNull Drawable resource, @Nullable Transition super Drawable> transition) {
- Bitmap bitmap;
-
- if (resource instanceof PictureDrawable pictureDrawable) {
- bitmap = Bitmap.createBitmap(
- pictureDrawable.getIntrinsicWidth(),
- pictureDrawable.getIntrinsicHeight(),
- Bitmap.Config.ARGB_8888);
-
- Canvas canvas = new Canvas(bitmap);
- canvas.drawPicture(pictureDrawable.getPicture());
-
- } else if (resource instanceof BitmapDrawable bitmapDrawable) {
- bitmap = bitmapDrawable.getBitmap();
- } else {
- Log_OC.e(TAG, "Unsupported drawable type: " + resource.getClass().getName());
- return;
- }
-
- // Scale down if necessary
- Bitmap logo = bitmap;
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- int max = Math.max(width, height);
- if (max > MAX_LOGO_SIZE_PX) {
- logo = BitmapUtils.scaleBitmap(bitmap, MAX_LOGO_SIZE_PX, width, height, max);
- }
-
- Drawable[] drawables = {
- new ColorDrawable(primaryColor),
- new BitmapDrawable(getResources(), logo)
- };
- LayerDrawable layerDrawable = new LayerDrawable(drawables);
-
- String name = capability.getServerName();
- setDrawerHeaderLogo(layerDrawable, name);
- }
-
- @Override
- public void onLoadCleared(@Nullable Drawable placeholder) {}
- };
- }
-
- private void hideTopBanner(ConstraintLayout banner) {
- banner.setVisibility(View.GONE);
- }
-
- private void showTopBanner(ConstraintLayout banner) {
- LinearLayout notesView = banner.findViewById(R.id.drawer_ecosystem_notes);
- LinearLayout talkView = banner.findViewById(R.id.drawer_ecosystem_talk);
- LinearLayout moreView = banner.findViewById(R.id.drawer_ecosystem_more);
- LinearLayout assistantView = banner.findViewById(R.id.drawer_ecosystem_assistant);
-
- final var optionalUser = getUser();
- if (optionalUser.isPresent()) {
- final var accountName = optionalUser.get().getAccountName();
- notesView.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.NOTES, accountName));
- talkView.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.TALK, accountName));
- }
-
- moreView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppStore("Nextcloud", true, this));
- assistantView.setOnClickListener(v -> startAssistantScreen());
- final var optionalCapabilities = getCapabilities();
- if (optionalCapabilities.isPresent()) {
- final var capabilities = optionalCapabilities.get();
- if (capabilities.getAssistant().isTrue()) {
- assistantView.setVisibility(View.VISIBLE);
- } else {
- assistantView.setVisibility(View.GONE);
- }
- } else {
- assistantView.setVisibility(View.GONE);
- }
-
- List views = Arrays.asList(notesView, talkView, moreView, assistantView);
-
- int iconColor;
- final var account = getAccount();
- if (account != null) {
- int primaryColor = getServerPrimaryColor(account);
- if (Hct.fromInt(primaryColor).getTone() < 80.0) {
- iconColor = Color.WHITE;
- } else {
- iconColor = getColor(R.color.grey_800_transparent);
- }
- } else {
- iconColor = getColor(R.color.grey_800_transparent);
- }
-
- for (LinearLayout view : views) {
- ImageView imageView = (ImageView) view.getChildAt(0);
- imageView.setImageTintList(ColorStateList.valueOf(iconColor));
- GradientDrawable background = (GradientDrawable) imageView.getBackground();
- background.setStroke(ScreenMetrics.dpToPx(1, this), iconColor);
- TextView textView = (TextView) view.getChildAt(1);
- textView.setTextColor(iconColor);
- }
-
- banner.setVisibility(View.VISIBLE);
- }
-
- private void setDrawerHeaderLogo(Drawable drawable, String serverName) {
- ImageView imageHeader = mNavigationViewHeader.findViewById(R.id.drawer_header_logo);
- imageHeader.setImageDrawable(drawable);
- imageHeader.setAdjustViewBounds(true);
-
- if (!TextUtils.isEmpty(serverName)) {
- TextView serverNameView = mNavigationViewHeader.findViewById(R.id.drawer_header_server_name);
- serverNameView.setVisibility(View.VISIBLE);
- serverNameView.setText(serverName);
- serverNameView.setTextColor(getServerTextColor());
- }
-
- }
-
- private int getServerPrimaryColor(Account account) {
- final var serverColor = CapabilityUtils.getCapability(account, this).getServerColor();
- return colorUtil.getNullSafeColor(serverColor, getColor(R.color.primary));
- }
-
- private int getServerTextColor() {
- final var serverTextColor = CapabilityUtils.getCapability(this).getServerTextColor();
- final int fallbackColor = PlatformThemeUtil.isDarkMode(this) ? Color.WHITE : Color.BLACK;
- return colorUtil.getNullSafeColor(serverTextColor, fallbackColor);
- }
-
- /**
- * setup drawer content, basically setting the item selected listener.
- *
- * @param navigationView the drawers navigation view
- */
- private void setupDrawerMenu(NavigationView navigationView) {
-
- // setup actions for drawer menu items
- navigationView.setNavigationItemSelectedListener(
- menuItem -> {
- mDrawerLayout.closeDrawers();
- // pending runnable will be executed after the drawer has been closed
- pendingRunnable = () -> onNavigationItemClicked(menuItem);
- return true;
- });
-
- User account = accountManager.getUser();
- filterDrawerMenu(navigationView.getMenu(), account);
- }
-
- private void filterDrawerMenu(final Menu menu, @NonNull final User user) {
- final var optionalCapability = getCapabilities();
- if (optionalCapability.isPresent()) {
- final var capability = optionalCapability.get();
- DrawerMenuUtil.filterTrashbinMenuItem(menu, capability);
- DrawerMenuUtil.filterActivityMenuItem(menu, capability);
- DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability);
- DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources());
- }
-
- DrawerMenuUtil.filterSearchMenuItems(menu, user);
- DrawerMenuUtil.setupHomeMenuItem(menu, getResources());
- DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, !getResources().getBoolean(R.bool.participate_enabled));
- DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !getResources().getBoolean(R.bool.shared_enabled));
- DrawerMenuUtil.removeMenuItem(menu, R.id.nav_logout, !getResources().getBoolean(R.bool.show_drawer_logout));
- }
-
- // region navigation item click
- private void onNavigationItemClicked(final MenuItem menuItem) {
- int itemId = menuItem.getItemId();
-
- if (itemId == R.id.nav_all_files || itemId == R.id.nav_personal_files) {
- closeDrawer();
- DrawerActivityExtensionsKt.navigateToAllFiles(this,itemId == R.id.nav_personal_files);
- EventBus.getDefault().post(new ChangeMenuEvent());
- } else if (itemId == R.id.nav_favorites) {
- openFavoritesTab();
- } else if (itemId == R.id.nav_gallery) {
- openMediaTab(menuItem.getItemId());
- } else if (itemId == R.id.nav_album) {
- if (this instanceof FileDisplayActivity) {
- replaceAlbumFragment();
- } else {
- // when user is not on FileDisplayActivity
- // if user is on TrashbinActivity then we have to start activity again
- Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.setAction(FileDisplayActivity.ALBUMS);
- startActivity(intent);
- }
- } else if (itemId == R.id.nav_on_device) {
- showOnDeviceFiles();
- } else if (itemId == R.id.nav_uploads) {
- resetOnlyPersonalAndOnDevice();
- pushFragment(NavigatorScreen.UploadList.INSTANCE);
- } else if (itemId == R.id.nav_trashbin) {
- resetOnlyPersonalAndOnDevice();
- pushFragment(NavigatorScreen.Trashbin.INSTANCE);
- } else if (itemId == R.id.nav_activity) {
- resetOnlyPersonalAndOnDevice();
- pushFragment(NavigatorScreen.Activities.INSTANCE);
- } else if (itemId == R.id.nav_settings) {
- resetOnlyPersonalAndOnDevice();
- final Intent intent = new Intent(this, SettingsActivity.class);
- startActivity(intent);
- } else if (itemId == R.id.nav_community) {
- resetOnlyPersonalAndOnDevice();
- pushFragment(NavigatorScreen.Community.INSTANCE);
- } else if (itemId == R.id.nav_logout) {
- resetOnlyPersonalAndOnDevice();
- MenuItem isNewMenuItemChecked = menuItem.setChecked(false);
- Log_OC.d(TAG,"onNavigationItemClicked nav_logout setChecked " + isNewMenuItemChecked);
- final Optional optionalUser = getUser();
- if (optionalUser.isPresent()) {
- UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager());
- }
- } else if (itemId == R.id.nav_shared) {
- openSharedTab();
- } else if (itemId == R.id.nav_recent_files) {
- resetOnlyPersonalAndOnDevice();
- startRecentlyModifiedSearch(menuItem);
- } else if (itemId == R.id.nav_assistant) {
- resetOnlyPersonalAndOnDevice();
- startAssistantScreen();
- } else if (itemId == R.id.nav_groupfolders) {
- resetOnlyPersonalAndOnDevice();
- Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.setAction(FileDisplayActivity.LIST_GROUPFOLDERS);
- startActivity(intent);
- } else {
- if (menuItem.getGroupId() == R.id.drawer_menu_external_links) {
- externalLinkClicked(menuItem);
- } else {
- Log_OC.w(TAG, "Unknown drawer menu item clicked: " + menuItem.getTitle());
- }
- }
-
- // from navigation user always sees root level
- resetFileDepth();
-
- highlightNavigationViewItem(itemId);
- }
-
- /**
- * If navigator activity already exists just push else start navigator activity.
- */
- public void pushFragment(NavigatorScreen screen) {
- if (this instanceof NavigatorActivity navigatorActivity) {
- navigatorActivity.push(screen);
- } else {
- final var intent = NavigatorActivity.Companion.intent(this, screen);
- startActivity(intent);
- }
- }
-
- @SuppressFBWarnings("RV")
- private void handleBottomNavigationViewClicks() {
- bottomNavigationView.setOnItemSelectedListener(menuItem -> {
- int menuItemId = menuItem.getItemId();
- exitSelectionMode();
- resetOnlyPersonalAndOnDevice();
-
- if (menuItemId == R.id.nav_all_files) {
- DrawerActivityExtensionsKt.navigateToAllFiles(this);
- EventBus.getDefault().post(new ChangeMenuEvent());
- } else if (menuItemId == R.id.nav_favorites) {
- openFavoritesTab();
- } else if (menuItemId == R.id.nav_assistant && !(this instanceof ComposeActivity)) {
- startAssistantScreen();
- } else if (menuItemId == R.id.nav_gallery) {
- openMediaTab(menuItem.getItemId());
- } else if (menuItemId == R.id.nav_album) {
- replaceAlbumFragment();
- }
-
- // Remove extra icon from the action bar
- if (getSupportActionBar() != null) {
- getSupportActionBar().setIcon(null);
- }
-
- // from navigation user always sees root level
- resetFileDepth();
-
- highlightNavigationViewItem(menuItemId);
- return false;
- });
- }
- // endregion
-
- private void startAssistantScreen() {
- final var destination = ComposeDestination.Companion.getAssistantScreen(this);
- Intent composeActivity = new Intent(getApplicationContext(), ComposeActivity.class);
- final Bundle bundle = new Bundle();
- bundle.putParcelable(ComposeActivity.DESTINATION, destination);
- composeActivity.putExtras(bundle);
- startActivity(composeActivity);
- }
-
- public void showManageAccountsDialog() {
- ChooseAccountDialogFragment choseAccountDialog = ChooseAccountDialogFragment.newInstance(accountManager.getUser());
- choseAccountDialog.show(getSupportFragmentManager(), "fragment_chose_account");
- }
-
- public void openManageAccounts() {
- Intent manageAccountsIntent = new Intent(getApplicationContext(), ManageAccountsActivity.class);
- startActivityForResult(manageAccountsIntent, ACTION_MANAGE_ACCOUNTS);
- }
-
- public void openAddAccount() {
- stopMediaPlayerAndHidePip();
- if (MDMConfig.INSTANCE.showIntro(this)) {
- Intent firstRunIntent = new Intent(getApplicationContext(), FirstRunActivity.class);
- firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true);
- startActivity(firstRunIntent);
- } else {
- startAccountCreation();
- }
- }
-
- protected void stopMediaPlayerAndHidePip() {
- playbackModel.release();
- }
-
- private void resetFileDepth() {
- final var ocFileListFragment = getOCFileListFragment();
- if (ocFileListFragment != null) {
- ocFileListFragment.resetFileDepth();
- }
- }
-
- private void openSharedTab() {
- resetOnlyPersonalAndOnDevice();
- SearchEvent searchEvent = new SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER);
- launchActivityForSearch(searchEvent, R.id.nav_shared);
- }
-
- private void startRecentlyModifiedSearch(MenuItem menuItem) {
- SearchEvent searchEvent = new SearchEvent("", SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH);
- MainApp.showOnlyFilesOnDevice(false);
-
- launchActivityForSearch(searchEvent, menuItem.getItemId());
- }
-
- public void startPhotoSearch(int id) {
- SearchEvent searchEvent = new SearchEvent("image/%", SearchRemoteOperation.SearchType.PHOTO_SEARCH);
- MainApp.showOnlyFilesOnDevice(false);
-
- launchActivityForSearch(searchEvent, id);
- }
-
- private void launchActivityForSearch(SearchEvent searchEvent, int menuItemId) {
- Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.setAction(Intent.ACTION_SEARCH);
- intent.putExtra(OCFileListFragment.SEARCH_EVENT, searchEvent);
- startActivity(intent);
- }
-
- public EcosystemManager getEcosystemManager() {
- return ecosystemManager;
- }
-
- /**
- * sets the new/current account and restarts. In case the given account equals the actual/current account the call
- * will be ignored.
- *
- * @param user to be set
- */
- public void accountClicked(User user) {
- final User currentUser = accountManager.getUser();
- if (!currentUser.nameEquals(user) && accountManager.setCurrentOwnCloudAccount(user)) {
- fetchExternalLinks(true);
- restart();
- }
- }
-
- private void externalLinkClicked(MenuItem menuItem) {
- int linkId = menuItem.getItemId() - MENU_ITEM_EXTERNAL_LINK;
-
- externalLinksProvider.getExternalLink(ExternalLinkType.LINK, externalLinks -> {
- ExternalLink link = externalLinks.stream()
- .filter(candidate -> candidate.getId() == linkId)
- .findFirst()
- .orElse(null);
-
- if (link == null) {
- Log_OC.w(TAG, "No external link found for menu item: " + menuItem.getTitle());
- } else {
- openExternalLink(link);
- }
- return Unit.INSTANCE;
- });
- }
-
- private void openExternalLink(ExternalLink link) {
- if (link.getRedirect()) {
- IntentUtil.startLinkIntent(this, link.getUrl());
- return;
- }
-
- Intent intent = new Intent(getApplicationContext(), ExternalSiteWebView.class)
- .putExtra(ExternalSiteWebView.EXTRA_TITLE, link.getName())
- .putExtra(ExternalSiteWebView.EXTRA_URL, link.getUrl())
- .putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true);
- startActivity(intent);
- }
-
- /**
- * checks if the drawer exists and is opened.
- *
- * @return true if the drawer is open, else false
- */
- public boolean isDrawerOpen() {
- return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START);
- }
-
- public void toggleDrawer() {
- if (isDrawerOpen()) {
- closeDrawer();
- } else {
- openDrawer();
- }
- }
-
- /**
- * closes the drawer.
- */
- public void closeDrawer() {
- if (mDrawerLayout != null) {
- mDrawerLayout.closeDrawer(GravityCompat.START);
- }
- }
-
- /**
- * opens the drawer.
- */
- public void openDrawer() {
- if (mDrawerLayout != null) {
- mDrawerLayout.openDrawer(GravityCompat.START);
- updateExternalLinksInDrawer();
- updateQuotaLink();
- }
- }
-
- /**
- * Enable or disable interaction with all drawers.
- *
- * @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED},
- * {@link DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}.
- */
- public void setDrawerLockMode(int lockMode) {
- if (mDrawerLayout != null) {
- mDrawerLayout.setDrawerLockMode(lockMode);
- }
- }
-
- /**
- * Enable or disable the drawer indicator.
- *
- * @param enable true to enable, false to disable
- */
- public void setDrawerIndicatorEnabled(boolean enable) {
- if (mDrawerToggle != null) {
- mDrawerToggle.setDrawerIndicatorEnabled(enable);
- }
- }
-
- /**
- * Updates title bar and home buttons (state and icon). Assumes that navigation drawer is NOT visible.
- */
- protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
- super.updateActionBarTitleAndHomeButton(chosenFile);
-
- // set home button properties
- if (mDrawerToggle != null) {
- mDrawerToggle.setDrawerIndicatorEnabled(chosenFile != null && isRoot(chosenFile));
- }
- }
-
- /**
- * shows or hides the quota UI elements.
- *
- * @param showQuota show/hide quota information
- */
- private void showQuota(boolean showQuota) {
- if (showQuota) {
- mQuotaView.setVisibility(View.VISIBLE);
- } else {
- mQuotaView.setVisibility(View.GONE);
- }
- }
-
- /**
- * configured the quota to be displayed.
- *
- * @param usedSpace the used space
- * @param totalSpace the total space
- * @param relative the percentage of space already used
- * @param quotaValue {@link GetUserInfoRemoteOperation#SPACE_UNLIMITED} or other to determinate state
- */
- private void setQuotaInformation(long usedSpace, long totalSpace, int relative, long quotaValue) {
- if (GetUserInfoRemoteOperation.SPACE_UNLIMITED == quotaValue) {
- mQuotaTextPercentage.setText(String.format(
- getString(R.string.drawer_quota_unlimited),
- HumanReadableFormatter.formatBytes(usedSpace)));
- } else {
- mQuotaTextPercentage.setText(String.format(
- getString(R.string.drawer_quota),
- HumanReadableFormatter.formatBytes(usedSpace),
- HumanReadableFormatter.formatBytes(totalSpace)));
- }
-
- mQuotaProgressBar.setProgress(relative);
-
- if (relative < RELATIVE_THRESHOLD_WARNING) {
- viewThemeUtils.material.colorProgressBar(mQuotaProgressBar, ColorRole.PRIMARY);
- } else {
- viewThemeUtils.material.colorProgressBar(
- mQuotaProgressBar,
- getResources().getColor(R.color.infolevel_warning, null)
- );
- }
-
- updateQuotaLink();
- showQuota(true);
- }
-
- private void updateQuotaLink() {
- if (mQuotaTextLink == null) {
- return;
- }
-
- if (!MDMConfig.INSTANCE.externalSiteSupport(this)) {
- mQuotaTextLink.setVisibility(View.GONE);
- return;
- }
-
- externalLinksProvider.getExternalLink(ExternalLinkType.QUOTA, quotas -> {
- float density = getResources().getDisplayMetrics().density;
- final int size = Math.round(24 * density);
- if (quotas.isEmpty()) {
- mQuotaTextLink.setVisibility(View.GONE);
- return Unit.INSTANCE;
- }
-
- final ExternalLink firstQuota = quotas.get(0);
- mQuotaTextLink.setText(firstQuota.getName());
- mQuotaTextLink.setClickable(true);
- mQuotaTextLink.setVisibility(View.VISIBLE);
- mQuotaTextLink.setOnClickListener(v -> {
- Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class);
- externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, firstQuota.getName());
- externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, firstQuota.getUrl());
- externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true);
- startActivity(externalWebViewIntent);
- });
-
- Target quotaTarget = createQuotaDrawableTarget(size, mQuotaTextLink);
- GlideHelper.INSTANCE.loadIntoTarget(DrawerActivity.this,
- accountManager.getCurrentOwnCloudAccount(),
- firstQuota.getIconUrl(),
- quotaTarget,
- R.drawable.ic_link);
- return Unit.INSTANCE;
- });
- }
-
- private Target createQuotaDrawableTarget(int size, TextView quotaTextLink) {
- return new CustomTarget<>() {
- @Override
- public void onResourceReady(@NonNull Drawable resource, @Nullable Transition super Drawable> transition) {
- Drawable drawable = resource.getCurrent();
- drawable.setBounds(0, 0, size, size);
- quotaTextLink.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
- }
-
- @Override
- public void onLoadCleared(@Nullable Drawable placeholder) {
-
- }
-
- @Override
- public void onLoadFailed(@Nullable Drawable errorDrawable) {
- super.onLoadFailed(errorDrawable);
-
- Drawable drawable = errorDrawable != null ? errorDrawable.getCurrent() : null;
- if (drawable != null) {
- drawable.setBounds(0, 0, size, size);
- quotaTextLink.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
- }
- }
- };
- }
-
-
- /**
- * Retrieves and shows the user quota if available
- */
- private void getAndDisplayUserQuota() {
- // set user space information
- Thread t = new Thread(() -> {
- final User user = accountManager.getUser();
-
- if (user.isAnonymous()) {
- return;
- }
-
- final Context context = MainApp.getAppContext();
- NextcloudClient nextcloudClient = null;
- try {
- nextcloudClient = OwnCloudClientManagerFactory
- .getDefaultSingleton()
- .getNextcloudClientFor(user.toOwnCloudAccount(),
- context);
- } catch (OperationCanceledException | AuthenticatorException | IOException e) {
- Log_OC.e(this, "Error retrieving user quota", e);
- }
-
- if (nextcloudClient == null) {
- return;
- }
-
- RemoteOperationResult result = new GetUserInfoRemoteOperation().execute(nextcloudClient);
-
- if (result.isSuccess() && result.getResultData() != null) {
- final UserInfo userInfo = result.getResultData();
- final Quota quota = userInfo.getQuota();
-
- if (quota != null) {
- final long used = quota.getUsed();
- final long total = quota.getTotal();
- final int relative = (int) Math.ceil(quota.getRelative());
- final long quotaValue = quota.getQuota();
-
- runOnUiThread(() -> {
- if (quotaValue > 0 || quotaValue == GetUserInfoRemoteOperation.SPACE_UNLIMITED
- || quotaValue == GetUserInfoRemoteOperation.QUOTA_LIMIT_INFO_NOT_AVAILABLE) {
- /*
- * show quota in case
- * it is available and calculated (> 0) or
- * in case of legacy servers (==QUOTA_LIMIT_INFO_NOT_AVAILABLE)
- */
- setQuotaInformation(used, total, relative, quotaValue);
- } else {
- /*
- * quotaValue < 0 means special cases like
- * {@link RemoteGetUserQuotaOperation.SPACE_NOT_COMPUTED},
- * {@link RemoteGetUserQuotaOperation.SPACE_UNKNOWN} or
- * {@link RemoteGetUserQuotaOperation.SPACE_UNLIMITED}
- * thus don't display any quota information.
- */
- showQuota(false);
- }
- });
- }
- }
- });
-
- t.start();
- }
-
- private void updateExternalLinksInDrawer() {
- if (drawerNavigationView == null || !MDMConfig.INSTANCE.externalSiteSupport(this)) {
- return;
- }
-
- drawerNavigationView.getMenu().removeGroup(R.id.drawer_menu_external_links);
-
- int greyColor = ContextCompat.getColor(this, R.color.drawer_menu_icon);
- externalLinksProvider.getExternalLink(ExternalLinkType.LINK, externalLinks -> {
- for (final ExternalLink link : externalLinks) {
- int id = drawerNavigationView
- .getMenu()
- .add(R.id.drawer_menu_external_links,
- MENU_ITEM_EXTERNAL_LINK +
- link.getId(),
- MENU_ORDER_EXTERNAL_LINKS,
- link.getName()
- )
- .setCheckable(true)
- .getItemId();
-
- Target iconTarget = createMenuItemTarget(id, greyColor);
- GlideHelper.INSTANCE.loadIntoTarget(DrawerActivity.this,
- accountManager.getCurrentOwnCloudAccount(),
- link.getIconUrl(),
- iconTarget,
- R.drawable.ic_link);
- }
- return Unit.INSTANCE;
- });
- }
-
- private Target createMenuItemTarget(int menuItemId, int tintColor) {
- return new CustomTarget<>() {
- @Override
- public void onResourceReady(@NonNull Drawable resource, @Nullable Transition super Drawable> transition) {
- setExternalLinkIcon(menuItemId, resource, tintColor);
- }
-
- @Override
- public void onLoadFailed(@Nullable Drawable errorDrawable) {
- setExternalLinkIcon(menuItemId, errorDrawable, tintColor);
- }
-
- @Override
- public void onLoadCleared(@Nullable Drawable placeholder) {
-
- }
- };
- }
-
- private void setExternalLinkIcon(int id, Drawable drawable, int greyColor) {
- MenuItem menuItem = drawerNavigationView.getMenu().findItem(id);
- if (menuItem == null) {
- return;
- }
-
- if (drawable == null) {
- menuItem.setIcon(R.drawable.ic_link);
- return;
- }
-
- final var resizedDrawable = DrawableUtil.INSTANCE.getResizedDrawable(this, drawable,32);
- menuItem.setIcon(viewThemeUtils.platform.colorDrawable(resizedDrawable, greyColor));
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false);
- }
-
- externalLinksProvider = new ExternalLinksProvider(getContentResolver());
- arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
- ecosystemManager = new EcosystemManager(this);
- }
-
- @Override
- protected void onDestroy() {
- externalLinksProvider.cleanup();
- super.onDestroy();
- }
-
- @Override
- protected void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, mIsAccountChooserActive);
- }
-
- @Override
- public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false);
- highlightNavigationViewItem(getSelectedMenuItemId());
- }
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- // Sync the toggle state after onRestoreInstanceState has occurred.
- if (mDrawerToggle != null) {
- mDrawerToggle.syncState();
- if (isDrawerOpen()) {
- mDrawerToggle.setDrawerIndicatorEnabled(true);
- }
- }
- updateExternalLinksInDrawer();
- updateQuotaLink();
- }
-
- @Override
- public void onConfigurationChanged(@NonNull Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (mDrawerToggle != null) {
- mDrawerToggle.onConfigurationChanged(newConfig);
- }
- }
-
- public void addOnBackPressedCallback() {
- getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) {
- @Override
- public void handleOnBackPressed() {
- if (isDrawerOpen()) {
- closeDrawer();
- return;
- }
-
- final var fragment = getSupportFragmentManager().findFragmentByTag(FileDetailsSharingProcessFragment.TAG);
- if (fragment instanceof FileDetailsSharingProcessFragment fileDetailsSharingProcessFragment) {
- fileDetailsSharingProcessFragment.onBackPressed();
- } else {
- setEnabled(false);
- getOnBackPressedDispatcher().onBackPressed();
- }
- }
- });
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- // update Account list and active account if Manage Account activity replies with
- // - ACCOUNT_LIST_CHANGED = true
- // - RESULT_OK
- if (requestCode == ACTION_MANAGE_ACCOUNTS && resultCode == RESULT_OK
- && data.getBooleanExtra(ManageAccountsActivity.KEY_ACCOUNT_LIST_CHANGED, false)) {
-
- // current account has changed
- if (data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) {
- setAccount(accountManager.getCurrentAccount(), false);
- restart();
- }
- } else if (requestCode == PassCodeManager.PASSCODE_ACTIVITY && data != null) {
- int result = data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT,
- RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE);
-
- if (result == RequestCredentialsActivity.KEY_CHECK_RESULT_CANCEL) {
- Log_OC.d(TAG, "PassCodeManager cancelled");
- preferences.setLockTimestamp(0);
- finish();
- }
- } else if (requestCode == REQ_ALL_FILES_ACCESS || requestCode == REQ_MEDIA_ACCESS) {
- checkStoragePermissionWarningBannerVisibility();
- }
- }
-
- /**
- * Quota view can be either at navigation bottom or header
- *
- * @param id the view's id
- * @return The view if found or null otherwise.
- */
- private View findQuotaViewById(int id) {
- View v = ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id);
-
- if (v != null) {
- return v;
- } else {
- return findViewById(id);
- }
- }
-
- /**
- * restart helper method which is called after a changing the current account.
- */
- private void restart() {
- Intent i = new Intent(this, FileDisplayActivity.class);
- i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- i.setAction(FileDisplayActivity.RESTART);
- startActivity(i);
-
- fetchExternalLinks(false);
- }
-
- private void resetOnlyPersonalAndOnDevice() {
- MainApp.showOnlyFilesOnDevice(false);
- MainApp.showOnlyPersonalFiles(false);
- }
-
- /**
- * show the file list to the user.
- *
- * @param onDeviceOnly flag to decide if all files or only the ones on the device should be shown
- */
- public void showFiles(boolean onDeviceOnly, boolean onlyPersonalFiles) {
- MainApp.showOnlyFilesOnDevice(onDeviceOnly);
- MainApp.showOnlyPersonalFiles(onlyPersonalFiles);
-
- Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.setAction(FileDisplayActivity.ALL_FILES);
- startActivity(intent);
- }
-
- private void showOnDeviceFiles() {
- MainApp.showOnlyFilesOnDevice(true);
- MainApp.showOnlyPersonalFiles(false);
-
- Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.setAction(FileDisplayActivity.ON_DEVICE);
- startActivity(intent);
- }
-
- @Override
- public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
- if (callContext instanceof MenuItem menuItem) {
- MenuItem newIcon = menuItem.setIcon(avatarDrawable);
- Log_OC.d(TAG,"avatarGenerated new icon: " + newIcon);
- } else if (callContext instanceof ImageView imageView) {
- imageView.setImageDrawable(avatarDrawable);
- } else if (callContext instanceof MaterialButton materialButton) {
- materialButton.setIcon(avatarDrawable);
- }
- }
-
- @Override
- public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
- if (callContext instanceof MenuItem menuItem) {
- return String.valueOf(menuItem.getTitle()).equals(tag);
- } else if (callContext instanceof ImageView imageView) {
- return String.valueOf(imageView.getTag()).equals(tag);
- } else if (callContext instanceof MaterialButton materialButton) {
- return String.valueOf(materialButton.getTag()).equals(tag);
- }
- return false;
- }
-
- /**
- * Adds other listeners to react on changes of the drawer layout.
- *
- * @param listener Object interested in changes of the drawer layout.
- */
- public void addDrawerListener(DrawerLayout.DrawerListener listener) {
- if (mDrawerLayout != null) {
- mDrawerLayout.addDrawerListener(listener);
- } else {
- Log_OC.e(TAG, "Drawer layout not ready to add drawer listener");
- }
- }
-
- public boolean isDrawerIndicatorAvailable() {
- return true;
- }
-
- public AppPreferences getAppPreferences() {
- return preferences;
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- EventBus.getDefault().register(this);
- }
-
- @Override
- protected void onStop() {
- if (preferences.getLockTimestamp() != 0) {
- preferences.setLockTimestamp(SystemClock.elapsedRealtime());
- }
- EventBus.getDefault().unregister(this);
- super.onStop();
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onAccountRemovedEvent(AccountRemovedEvent event) {
- restart();
- }
-
- /**
- * Retrieves external links via api from 'external' app
- */
- public void fetchExternalLinks(final boolean force) {
- if (!MDMConfig.INSTANCE.externalSiteSupport(this)) {
- return;
- }
-
- User user = accountManager.getUser();
- if (user.isAnonymous()) {
- Log_OC.d(TAG, "Trying to execute a sync operation with a storage manager for an anonymous account");
- return;
- }
-
- Thread t = new Thread(() -> {
- // fetch capabilities as early as possible
- final var optionalCapability = getCapabilities();
- if (optionalCapability.isPresent()) {
- final var capability = optionalCapability.get();
- if ((capability.getAccountName() == null || !capability.getAccountName().isEmpty()) && getStorageManager() != null) {
- GetCapabilitiesOperation getCapabilities = new GetCapabilitiesOperation(getStorageManager());
- getCapabilities.execute(getBaseContext());
- }
- }
-
- if (getStorageManager() != null && CapabilityUtils.getCapability(user, this)
- .getExternalLinks().isTrue()) {
-
- int count = arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL,
- FileActivity.APP_OPENED_COUNT);
-
- if (count > 10 || count == -1 || force) {
- if (force) {
- Log_OC.d("ExternalLinks", "force update");
- }
-
- arbitraryDataProvider.storeOrUpdateKeyValue(FilesSyncHelper.GLOBAL,
- FileActivity.APP_OPENED_COUNT, "0");
-
- Log_OC.d("ExternalLinks", "update via api");
- RemoteOperation getExternalLinksOperation = new ExternalLinksOperation();
- RemoteOperationResult result = getExternalLinksOperation.execute(user, this);
-
- if (result.isSuccess() && result.getData() != null) {
- externalLinksProvider.deleteAllExternalLinks();
-
- ArrayList externalLinks = (ArrayList) (Object) result.getData();
-
- for (ExternalLink link : externalLinks) {
- externalLinksProvider.storeExternalLink(link);
- }
- }
- } else {
- arbitraryDataProvider.storeOrUpdateKeyValue(FilesSyncHelper.GLOBAL,
- FileActivity.APP_OPENED_COUNT, String.valueOf(count + 1));
- }
- } else {
- externalLinksProvider.deleteAllExternalLinks();
- Log_OC.d("ExternalLinks", "links disabled");
- }
- runOnUiThread(this::updateExternalLinksInDrawer);
- });
- t.start();
- }
-
- protected void handleDeepLink(@NonNull Uri uri) {
- String path = uri.getLastPathSegment();
- if (path == null) return;
-
- DeepLinkConstants deepLinkType = DeepLinkConstants.Companion.fromPath(path);
- if (deepLinkType == null) {
- SnackbarUtil.show(this, getString(R.string.invalid_url));
- return;
- }
-
- switch (deepLinkType) {
- case OPEN_AUTO_UPLOAD:
- pushFragment(NavigatorScreen.AutoUpload.INSTANCE);
- break;
- case OPEN_EXTERNAL_URL:
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri.getQueryParameter("url")));
- startActivity(intent);
- break;
- case ACTION_CREATE_NEW:
- findViewById(R.id.fab_main).callOnClick();
- break;
- case ACTION_APP_UPDATE:
- LinkHelper.INSTANCE.openAppStore(getPackageName(), false, this);
- break;
- case OPEN_NOTIFICATIONS:
- pushFragment(NavigatorScreen.Notifications.INSTANCE);
- break;
- default:
- handleNavItemClickEvent(deepLinkType.getNavId());
- break;
- }
- }
-
- private void handleNavItemClickEvent(@IdRes int menuItemId) {
- Menu navMenu = drawerNavigationView.getMenu();
- onNavigationItemClicked(navMenu.findItem(menuItemId));
- }
-
- public void showBottomNavigationBar(boolean show) {
- ViewExtensionsKt.setVisibleIf(bottomNavigationView, show);
- }
-
- public BottomNavigationView getBottomNavigationView() {
- return bottomNavigationView;
- }
-
- private void checkStoragePermissionWarningBannerVisibility() {
- if (this instanceof UploadFilesActivity uploadFilesActivity) {
- uploadFilesActivity.setupStoragePermissionWarningBanner();
- }
- }
-
- private int getSelectedMenuItemId() {
- if (drawerNavigationView == null) {
- return R.id.nav_all_files;
- }
-
- return NavigationViewExtensionsKt.getSelectedMenuItemId(drawerNavigationView);
- }
-
- public boolean isToolbarStyleSearch() {
- int menuItemId = getSelectedMenuItemId();
-
- return menuItemId == Menu.NONE ||
- menuItemId == R.id.nav_all_files ||
- menuItemId == R.id.nav_personal_files;
- }
-
- public void replaceAlbumFragment() {
- if (isAlbumsFragment()) {
- return;
- }
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.addToBackStack(null);
- transaction.replace(R.id.left_fragment_container, AlbumsFragment.Companion.newInstance(false), AlbumsFragment.Companion.getTAG());
- transaction.commit();
- }
-
- public Optional getFragment(String tag, Class clazz) {
- return Optional.ofNullable(getSupportFragmentManager().findFragmentByTag(tag))
- .filter(clazz::isInstance)
- .map(clazz::cast);
- }
-
- public boolean isAlbumItemsFragment() {
- return getFragment(AlbumItemsFragment.Companion.getTAG(), AlbumItemsFragment.class)
- .filter(Fragment::isVisible)
- .isPresent();
- }
-
- public boolean isAlbumsFragment() {
- return getFragment(AlbumsFragment.Companion.getTAG(), AlbumsFragment.class)
- .filter(Fragment::isVisible)
- .isPresent();
- }
-}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java
index a15055bc6c9a..ac256cd2375c 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java
+++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java
@@ -75,6 +75,7 @@
import com.owncloud.android.providers.UsersAndGroupsSearchProvider;
import com.owncloud.android.services.OperationsService;
import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
+import com.owncloud.android.ui.activity.drawer.DrawerActivity;
import com.owncloud.android.ui.asynctasks.CheckRemoteWipeTask;
import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt
index 991f623dba03..fe0a26c60237 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt
+++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt
@@ -978,7 +978,8 @@ class FileDisplayActivity :
* 2) Currently visible fragment (and its active child)
* 3) Fallback to All Files
*/
- override fun getMenuItemId(): Int = MainApp.getMenuItemId() ?: listOfFilesFragment?.menuItemId ?: R.id.nav_all_files
+ override val menuItemId: Int
+ get() = MainApp.getMenuItemId() ?: listOfFilesFragment?.menuItemId ?: R.id.nav_all_files
override fun setFile(file: OCFile?) {
super.setFile(file)
@@ -1185,7 +1186,8 @@ class FileDisplayActivity :
supportInvalidateOptionsMenu()
}
- override fun isDrawerIndicatorAvailable(): Boolean = isRoot(getCurrentDir())
+ override val isDrawerIndicatorAvailable: Boolean
+ get() = isRoot(getCurrentDir())
fun configureToolbarForPreview(file: OCFile) {
lockScrolling()
@@ -1301,7 +1303,7 @@ class FileDisplayActivity :
view.onActionViewCollapsed()
setDrawerIndicatorEnabled(isDrawerIndicatorAvailable) // order matters
supportActionBar?.setDisplayHomeAsUpEnabled(true)
- mDrawerToggle.syncState()
+ mDrawerToggle?.syncState()
listOfFilesFragment?.run {
isSearchFragment = false
@@ -2334,7 +2336,7 @@ class FileDisplayActivity :
private fun refreshGalleryFragmentIfNeeded() {
if (topFileListFragment is GalleryFragment) {
- startPhotoSearch(R.id.nav_gallery)
+ startPhotoSearch()
}
}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/InternalTwoWaySyncActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/InternalTwoWaySyncActivity.kt
index 47a32e0b150d..4710bb01f8e4 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/InternalTwoWaySyncActivity.kt
+++ b/app/src/main/java/com/owncloud/android/ui/activity/InternalTwoWaySyncActivity.kt
@@ -27,6 +27,7 @@ import com.owncloud.android.BuildConfig
import com.owncloud.android.R
import com.owncloud.android.databinding.InternalTwoWaySyncLayoutBinding
import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.adapter.InternalTwoWaySyncAdapter
import com.owncloud.android.ui.navigation.model.ActionBarStyle
import kotlinx.coroutines.Dispatchers
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java
index 6580a0119b24..0b8c51755f78 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java
+++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java
@@ -100,7 +100,7 @@
import androidx.core.content.res.ResourcesCompat;
import kotlin.Unit;
-import static com.owncloud.android.ui.activity.DrawerActivity.REQ_ALL_FILES_ACCESS;
+import static com.owncloud.android.ui.activity.drawer.DrawerActivity.REQ_ALL_FILES_ACCESS;
/**
* An Activity that allows the user to change the application's settings.
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt
index 3a14fc353272..d52539f636dc 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt
+++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt
@@ -32,6 +32,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.OCFileDepth
import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.fragment.OCFileListFragment
import com.owncloud.android.ui.fragment.SearchType
import com.owncloud.android.ui.navigation.model.ActionBarStyle
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.kt
index 4eae09235429..4c450af20f75 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.kt
+++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.kt
@@ -40,6 +40,7 @@ import com.owncloud.android.R
import com.owncloud.android.databinding.UploadFilesLayoutBinding
import com.owncloud.android.datamodel.SyncedFolderProvider
import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.activity.uploadfiles.UploadBehaviourSelector
import com.owncloud.android.ui.activity.uploadfiles.UploadFilesDialogs
import com.owncloud.android.ui.activity.uploadfiles.UploadFilesDialogs.Companion.QUERY_TO_MOVE_DIALOG_TAG
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java
index cc5fc52526f3..155ba49e45c9 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java
+++ b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java
@@ -44,6 +44,7 @@
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation;
+import com.owncloud.android.ui.activity.drawer.DrawerActivity;
import com.owncloud.android.ui.adapter.UserInfoAdapter;
import com.owncloud.android.ui.dialog.AccountRemovalDialog;
import com.owncloud.android.ui.events.TokenPushEvent;
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/DrawerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/DrawerActivity.kt
new file mode 100644
index 000000000000..8f16eda75bee
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/DrawerActivity.kt
@@ -0,0 +1,455 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer
+
+import android.content.Intent
+import android.content.res.Configuration
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.PersistableBundle
+import android.os.SystemClock
+import android.view.Menu
+import android.view.View
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.ActionBarDrawerToggle
+import androidx.core.content.res.ResourcesCompat
+import androidx.core.view.GravityCompat
+import androidx.drawerlayout.widget.DrawerLayout
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import com.google.android.material.bottomnavigation.BottomNavigationView
+import com.google.android.material.navigation.NavigationView
+import com.nextcloud.android.common.core.utils.ecosystem.EcosystemManager
+import com.nextcloud.android.common.ui.color.ColorUtil
+import com.nextcloud.client.account.User
+import com.nextcloud.client.di.Injectable
+import com.nextcloud.client.onboarding.FirstRunActivity
+import com.nextcloud.client.player.media3.PlaybackModel
+import com.nextcloud.client.preferences.AppPreferences
+import com.nextcloud.client.systembars.SystemBarBackgroundCallbacks
+import com.nextcloud.ui.dialog.account.ChooseAccountDialogFragment
+import com.nextcloud.utils.avatar.AvatarGenerationListener
+import com.nextcloud.utils.avatar.AvatarGenerator
+import com.nextcloud.utils.extensions.getSelectedMenuItemId
+import com.nextcloud.utils.extensions.highlightNavigationView
+import com.nextcloud.utils.extensions.setVisibleIf
+import com.nextcloud.utils.mdm.MDMConfig
+import com.owncloud.android.R
+import com.owncloud.android.authentication.PassCodeManager
+import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
+import com.owncloud.android.datamodel.ExternalLinksProvider
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.ui.activity.ManageAccountsActivity
+import com.owncloud.android.ui.activity.RequestCredentialsActivity
+import com.owncloud.android.ui.activity.ToolbarActivity
+import com.owncloud.android.ui.activity.UploadFilesActivity
+import com.owncloud.android.ui.activity.drawer.avatar.DrawerAvatarGenerationListener
+import com.owncloud.android.ui.activity.drawer.back.DrawerOnBackPressedCallback
+import com.owncloud.android.ui.activity.drawer.externallinks.DrawerExternalLinksMenu
+import com.owncloud.android.ui.activity.drawer.externallinks.ExternalLinkOpener
+import com.owncloud.android.ui.activity.drawer.externallinks.ExternalLinksSynchronizer
+import com.owncloud.android.ui.activity.drawer.fragment.DrawerFragmentFinder
+import com.owncloud.android.ui.activity.drawer.header.DrawerHeaderController
+import com.owncloud.android.ui.activity.drawer.navigation.DrawerDeepLinkHandler
+import com.owncloud.android.ui.activity.drawer.navigation.DrawerMenuFilter
+import com.owncloud.android.ui.activity.drawer.navigation.DrawerMenuItemClickHandler
+import com.owncloud.android.ui.activity.drawer.navigation.DrawerNavigator
+import com.owncloud.android.ui.activity.drawer.quota.DrawerQuotaController
+import com.owncloud.android.ui.events.AccountRemovedEvent
+import com.owncloud.android.ui.fragment.OCFileListFragment
+import com.owncloud.android.ui.navigation.NavigatorScreen
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.Optional
+import javax.inject.Inject
+import kotlin.jvm.optionals.getOrNull
+
+abstract class DrawerActivity :
+ ToolbarActivity(),
+ AvatarGenerationListener by DrawerAvatarGenerationListener(),
+ Injectable {
+
+ @Inject
+ lateinit var playbackModel: PlaybackModel
+
+ @Inject
+ lateinit var avatarGenerator: AvatarGenerator
+
+ @Inject
+ lateinit var colorUtil: ColorUtil
+
+ @JvmField
+ protected var mDrawerToggle: ActionBarDrawerToggle? = null
+
+ var bottomNavigationView: BottomNavigationView? = null
+ private set
+
+ lateinit var ecosystemManager: EcosystemManager
+ private set
+
+ internal var drawerNavigationView: NavigationView? = null
+ private set
+
+ internal lateinit var externalLinksProvider: ExternalLinksProvider
+ private set
+
+ private var drawerLayout: DrawerLayout? = null
+ private var navigationViewHeader: View? = null
+ private var pendingRunnable: Runnable? = null
+ private lateinit var externalLinksSynchronizer: ExternalLinksSynchronizer
+
+ private val fragmentFinder = DrawerFragmentFinder(this)
+ private val drawerNavigator = DrawerNavigator(this)
+ private val externalLinkOpener = ExternalLinkOpener(this)
+ private val externalLinksMenu by lazy { DrawerExternalLinksMenu(this, externalLinkOpener) }
+ private val quotaController by lazy { DrawerQuotaController(this, externalLinkOpener) }
+ private val headerController by lazy { DrawerHeaderController(this, drawerNavigator) }
+ private val menuItemClickHandler by lazy { DrawerMenuItemClickHandler(this, drawerNavigator, externalLinksMenu) }
+ private val deepLinkHandler by lazy { DrawerDeepLinkHandler(this, drawerNavigator, menuItemClickHandler) }
+
+ private val manageAccountsLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult(), ::onManageAccountsResult)
+
+ protected open val menuItemId: Int
+ get() = R.id.nav_all_files
+
+ open val isDrawerIndicatorAvailable: Boolean
+ get() = true
+
+ val appPreferences: AppPreferences
+ get() = preferences
+
+ val isDrawerOpen: Boolean
+ get() = drawerLayout?.isDrawerOpen(GravityCompat.START) == true
+
+ val ocFileListFragment: OCFileListFragment?
+ get() = fragmentFinder.ocFileListFragment
+
+ val isAlbumItemsFragment: Boolean
+ get() = fragmentFinder.isAlbumItemsFragmentVisible
+
+ val isAlbumsFragment: Boolean
+ get() = fragmentFinder.isAlbumsFragmentVisible
+
+ val isToolbarStyleSearch: Boolean
+ get() = when (selectedMenuItemId) {
+ Menu.NONE, R.id.nav_all_files, R.id.nav_personal_files -> true
+ else -> false
+ }
+
+ internal val isAssistantAvailable: Boolean
+ get() = capabilities.getOrNull()?.assistant?.isTrue == true
+
+ private val selectedMenuItemId: Int
+ get() = drawerNavigationView?.getSelectedMenuItemId() ?: R.id.nav_all_files
+
+ // region Lifecycle
+ override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
+ super.onCreate(savedInstanceState, persistentState)
+ onBackPressedDispatcher.addCallback(DrawerOnBackPressedCallback(this))
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ externalLinksProvider = ExternalLinksProvider(contentResolver)
+ externalLinksSynchronizer =
+ ExternalLinksSynchronizer(this, externalLinksProvider, ArbitraryDataProviderImpl(this))
+ ecosystemManager = EcosystemManager(this)
+ }
+
+ public override fun onRestoreInstanceState(savedInstanceState: Bundle) {
+ super.onRestoreInstanceState(savedInstanceState)
+ highlightNavigationViewItem(selectedMenuItemId)
+ }
+
+ override fun onPostCreate(savedInstanceState: Bundle?) {
+ super.onPostCreate(savedInstanceState)
+ mDrawerToggle?.let { toggle ->
+ toggle.syncState()
+ if (isDrawerOpen) {
+ toggle.isDrawerIndicatorEnabled = true
+ }
+ }
+ externalLinksMenu.update()
+ quotaController.updateQuotaLink()
+ }
+
+ override fun onStart() {
+ super.onStart()
+ EventBus.getDefault().register(this)
+ }
+
+ override fun onStop() {
+ if (preferences.lockTimestamp != 0L) {
+ preferences.lockTimestamp = SystemClock.elapsedRealtime()
+ }
+ EventBus.getDefault().unregister(this)
+ super.onStop()
+ }
+
+ override fun onDestroy() {
+ externalLinksProvider.cleanup()
+ super.onDestroy()
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ mDrawerToggle?.onConfigurationChanged(newConfig)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ when (requestCode) {
+ PassCodeManager.PASSCODE_ACTIVITY -> data?.let(::onPassCodeResult)
+ REQ_ALL_FILES_ACCESS, REQ_MEDIA_ACCESS -> checkStoragePermissionWarningBannerVisibility()
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ open fun onAccountRemovedEvent(event: AccountRemovedEvent) {
+ restart()
+ }
+ // endregion
+
+ // region Drawer setup
+ protected fun setupDrawer(id: Int) {
+ drawerLayout = drawerLayout ?: findViewById(R.id.drawer_layout)
+ drawerNavigationView = drawerNavigationView ?: findViewById(R.id.nav_view)
+ drawerNavigationView?.let { setupDrawerNavigationView(it, id) }
+
+ setupDrawerToggle()
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+
+ bottomNavigationView = bottomNavigationView ?: findViewById(R.id.bottom_navigation)
+ bottomNavigationView?.let { setupBottomNavigationView(it, id) }
+ }
+
+ private fun setupDrawerNavigationView(navigationView: NavigationView, id: Int) {
+ viewThemeUtils.files.colorNavigationView(navigationView)
+ navigationViewHeader = navigationView.getHeaderView(0)
+ updateHeader()
+
+ navigationView.setNavigationItemSelectedListener { menuItem ->
+ drawerLayout?.closeDrawers()
+ pendingRunnable = Runnable { menuItemClickHandler.onNavigationItemClicked(menuItem) }
+ true
+ }
+ DrawerMenuFilter.filter(navigationView.menu, userAccountManager.user, capabilities.getOrNull(), resources)
+
+ quotaController.fetchAndDisplay()
+ quotaController.setupViews(navigationView)
+ highlightNavigationViewItem(id)
+ }
+
+ private fun setupBottomNavigationView(bottomNavigation: BottomNavigationView, id: Int) {
+ viewThemeUtils.platform.colorBottomNavigationView(bottomNavigation)
+ showAssistantInBottomNavigation(bottomNavigation)
+ bottomNavigation.setOnItemSelectedListener(menuItemClickHandler::onBottomNavigationItemSelected)
+ highlightNavigationViewItem(id)
+ }
+
+ @SuppressFBWarnings("RV")
+ private fun showAssistantInBottomNavigation(bottomNavigation: BottomNavigationView) {
+ bottomNavigation.menu.findItem(R.id.nav_assistant)?.isVisible = isAssistantAvailable
+ }
+
+ private fun setupDrawerToggle() {
+ val drawerLayout = drawerLayout ?: return
+ val toggle = object : ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close) {
+ override fun onDrawerClosed(drawerView: View) {
+ super.onDrawerClosed(drawerView)
+ runPendingNavigation()
+ closeDrawer()
+ }
+ }
+ mDrawerToggle = toggle
+
+ drawerLayout.addDrawerListener(toggle)
+ toggle.isDrawerIndicatorEnabled = true
+ toggle.isDrawerSlideAnimationEnabled = true
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_arrow_back, null)?.let { backArrow ->
+ viewThemeUtils.platform.tintToolbarArrowDrawable(this, toggle, backArrow)
+ }
+ }
+
+ private fun runPendingNavigation() {
+ val runnable = pendingRunnable ?: return
+ Handler(Looper.getMainLooper()).post(runnable)
+ pendingRunnable = null
+ }
+
+ fun updateHeader() {
+ val header = navigationViewHeader ?: return
+ headerController.update(header)
+ }
+ // endregion
+
+ // region Navigation
+ fun highlightNavigationViewItem(menuItemId: Int) {
+ highlightNavigationView(drawerNavigationView, bottomNavigationView, menuItemId)
+ SystemBarBackgroundCallbacks.apply(this, viewThemeUtils)
+ Log_OC.d(TAG, "New menu item is: $menuItemId")
+ }
+
+ fun pushFragment(screen: NavigatorScreen) = drawerNavigator.pushFragment(screen)
+
+ fun startPhotoSearch() = drawerNavigator.startPhotoSearch()
+
+ open fun showFiles(onDeviceOnly: Boolean, onlyPersonalFiles: Boolean) =
+ drawerNavigator.showFiles(onDeviceOnly, onlyPersonalFiles)
+
+ fun replaceAlbumFragment() = drawerNavigator.replaceAlbumFragment()
+
+ fun getFragment(tag: String?, clazz: Class): Optional = fragmentFinder.getFragment(tag, clazz)
+
+ fun showBottomNavigationBar(show: Boolean) {
+ bottomNavigationView.setVisibleIf(show)
+ }
+
+ protected fun handleDeepLink(uri: Uri) = deepLinkHandler.handle(uri)
+
+ private fun restart() {
+ drawerNavigator.restartFileDisplayActivity()
+ fetchExternalLinks(false)
+ }
+ // endregion
+
+ // region Drawer state
+ fun toggleDrawer() {
+ if (isDrawerOpen) {
+ closeDrawer()
+ } else {
+ openDrawer()
+ }
+ }
+
+ fun closeDrawer() {
+ drawerLayout?.closeDrawer(GravityCompat.START)
+ }
+
+ fun openDrawer() {
+ val drawerLayout = drawerLayout ?: return
+ drawerLayout.openDrawer(GravityCompat.START)
+ externalLinksMenu.update()
+ quotaController.updateQuotaLink()
+ }
+
+ fun setDrawerLockMode(lockMode: Int) {
+ drawerLayout?.setDrawerLockMode(lockMode)
+ }
+
+ fun setDrawerIndicatorEnabled(enable: Boolean) {
+ mDrawerToggle?.isDrawerIndicatorEnabled = enable
+ }
+
+ fun addDrawerListener(listener: DrawerLayout.DrawerListener?) {
+ val drawerLayout = drawerLayout
+ if (drawerLayout == null) {
+ Log_OC.e(TAG, "Drawer layout not ready to add drawer listener")
+ return
+ }
+ listener?.let(drawerLayout::addDrawerListener)
+ }
+
+ override fun updateActionBarTitleAndHomeButton(chosenFile: OCFile?) {
+ super.updateActionBarTitleAndHomeButton(chosenFile)
+ mDrawerToggle?.isDrawerIndicatorEnabled = chosenFile != null && isRoot(chosenFile)
+ }
+ // endregion
+
+ // region Accounts
+ fun showManageAccountsDialog() {
+ ChooseAccountDialogFragment.newInstance(userAccountManager.user)
+ .show(supportFragmentManager, CHOOSE_ACCOUNT_DIALOG_TAG)
+ }
+
+ fun openManageAccounts() {
+ manageAccountsLauncher.launch(Intent(applicationContext, ManageAccountsActivity::class.java))
+ }
+
+ fun openAddAccount() {
+ stopMediaPlayerAndHidePip()
+ if (!MDMConfig.showIntro(this)) {
+ startAccountCreation()
+ return
+ }
+
+ val intent = Intent(applicationContext, FirstRunActivity::class.java)
+ .putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true)
+ startActivity(intent)
+ }
+
+ protected fun stopMediaPlayerAndHidePip() {
+ playbackModel.release()
+ }
+
+ fun accountClicked(user: User?) {
+ val accountManager = userAccountManager
+ if (accountManager.user.nameEquals(user) || !accountManager.setCurrentOwnCloudAccount(user)) return
+ fetchExternalLinks(true)
+ restart()
+ }
+
+ fun fetchExternalLinks(force: Boolean) {
+ if (!MDMConfig.externalSiteSupport(this)) return
+
+ val user = userAccountManager.user
+ if (user.isAnonymous) {
+ Log_OC.d(TAG, "Trying to execute a sync operation with a storage manager for an anonymous account")
+ return
+ }
+
+ lifecycleScope.launch {
+ withContext(Dispatchers.IO) { externalLinksSynchronizer.sync(user, force) }
+ externalLinksMenu.update()
+ }
+ }
+
+ private fun onManageAccountsResult(result: ActivityResult) {
+ if (result.resultCode != RESULT_OK) return
+ val data = result.data ?: return
+ if (!data.getBooleanExtra(ManageAccountsActivity.KEY_ACCOUNT_LIST_CHANGED, false)) return
+ if (!data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) return
+
+ setAccount(userAccountManager.currentAccount, false)
+ restart()
+ }
+
+ private fun onPassCodeResult(data: Intent) {
+ val result = data.getIntExtra(
+ RequestCredentialsActivity.KEY_CHECK_RESULT,
+ RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE
+ )
+ if (result != RequestCredentialsActivity.KEY_CHECK_RESULT_CANCEL) return
+
+ Log_OC.d(TAG, "PassCodeManager cancelled")
+ preferences.lockTimestamp = 0
+ finish()
+ }
+
+ private fun checkStoragePermissionWarningBannerVisibility() {
+ (this as? UploadFilesActivity)?.setupStoragePermissionWarningBanner()
+ }
+ // endregion
+
+ companion object {
+ private const val TAG = "DrawerActivity"
+ private const val CHOOSE_ACCOUNT_DIALOG_TAG = "fragment_chose_account"
+
+ const val REQ_ALL_FILES_ACCESS = 3001
+ const val REQ_MEDIA_ACCESS = 3000
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/avatar/DrawerAvatarGenerationListener.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/avatar/DrawerAvatarGenerationListener.kt
new file mode 100644
index 000000000000..8e3d860875ac
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/avatar/DrawerAvatarGenerationListener.kt
@@ -0,0 +1,44 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.avatar
+
+import android.graphics.drawable.Drawable
+import android.view.MenuItem
+import android.widget.ImageView
+import com.google.android.material.button.MaterialButton
+import com.nextcloud.utils.avatar.AvatarGenerationListener
+import com.owncloud.android.lib.common.utils.Log_OC
+
+class DrawerAvatarGenerationListener : AvatarGenerationListener {
+
+ override fun avatarGenerated(avatarDrawable: Drawable?, callContext: Any?) {
+ when (callContext) {
+ is MenuItem -> {
+ val newIcon = callContext.setIcon(avatarDrawable)
+ Log_OC.d(TAG, "avatarGenerated new icon: $newIcon")
+ }
+
+ is ImageView -> callContext.setImageDrawable(avatarDrawable)
+
+ is MaterialButton -> callContext.icon = avatarDrawable
+ }
+ }
+
+ override fun shouldCallGeneratedCallback(tag: String?, callContext: Any?): Boolean {
+ val contextTag: Any? = when (callContext) {
+ is MenuItem -> callContext.title
+ is ImageView -> callContext.tag
+ is MaterialButton -> callContext.tag
+ else -> return false
+ }
+ return contextTag.toString() == tag
+ }
+
+ companion object {
+ private const val TAG = "DrawerAvatarGenerationListener"
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/back/DrawerOnBackPressedCallback.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/back/DrawerOnBackPressedCallback.kt
new file mode 100644
index 000000000000..f2ba7366a83e
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/back/DrawerOnBackPressedCallback.kt
@@ -0,0 +1,30 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.back
+
+import androidx.activity.OnBackPressedCallback
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment
+
+class DrawerOnBackPressedCallback(private val activity: DrawerActivity) : OnBackPressedCallback(true) {
+
+ override fun handleOnBackPressed() {
+ if (activity.isDrawerOpen) {
+ activity.closeDrawer()
+ return
+ }
+
+ val fragment = activity.supportFragmentManager.findFragmentByTag(FileDetailsSharingProcessFragment.TAG)
+ if (fragment is FileDetailsSharingProcessFragment) {
+ fragment.onBackPressed()
+ return
+ }
+
+ isEnabled = false
+ activity.onBackPressedDispatcher.onBackPressed()
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/DrawerExternalLinksMenu.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/DrawerExternalLinksMenu.kt
new file mode 100644
index 000000000000..f6c0cf3c6bba
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/DrawerExternalLinksMenu.kt
@@ -0,0 +1,88 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.externallinks
+
+import android.graphics.drawable.Drawable
+import android.view.MenuItem
+import androidx.core.content.ContextCompat
+import com.google.android.material.navigation.NavigationView
+import com.nextcloud.utils.GlideHelper
+import com.nextcloud.utils.mdm.MDMConfig
+import com.owncloud.android.R
+import com.owncloud.android.lib.common.ExternalLink
+import com.owncloud.android.lib.common.ExternalLinkType
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.utils.DrawableUtil
+
+class DrawerExternalLinksMenu(
+ private val activity: DrawerActivity,
+ private val externalLinkOpener: ExternalLinkOpener
+) {
+
+ fun update() {
+ val navigationView = activity.drawerNavigationView ?: return
+ if (!MDMConfig.externalSiteSupport(activity)) return
+
+ navigationView.menu.removeGroup(R.id.drawer_menu_external_links)
+
+ val greyColor = ContextCompat.getColor(activity, R.color.drawer_menu_icon)
+ activity.externalLinksProvider.getExternalLink(ExternalLinkType.LINK) { externalLinks ->
+ externalLinks.forEach { addMenuItem(navigationView, it, greyColor) }
+ }
+ }
+
+ fun onExternalLinkClicked(menuItem: MenuItem) {
+ val linkId = menuItem.itemId - MENU_ITEM_EXTERNAL_LINK
+ activity.externalLinksProvider.getExternalLink(ExternalLinkType.LINK) { externalLinks ->
+ val link = externalLinks.firstOrNull { it.id == linkId }
+ if (link == null) {
+ Log_OC.w(TAG, "No external link found for menu item: " + menuItem.title)
+ } else {
+ externalLinkOpener.open(link)
+ }
+ }
+ }
+
+ private fun addMenuItem(navigationView: NavigationView, link: ExternalLink, tintColor: Int) {
+ val menuItemId = navigationView.menu
+ .add(
+ R.id.drawer_menu_external_links,
+ MENU_ITEM_EXTERNAL_LINK + link.id,
+ MENU_ORDER_EXTERNAL_LINKS,
+ link.name
+ )
+ .setCheckable(true)
+ .itemId
+
+ GlideHelper.loadIntoTarget(
+ activity,
+ activity.userAccountManager.currentOwnCloudAccount,
+ link.iconUrl,
+ ExternalLinkMenuIconTarget { setMenuItemIcon(navigationView, menuItemId, it, tintColor) },
+ R.drawable.ic_link
+ )
+ }
+
+ private fun setMenuItemIcon(navigationView: NavigationView, menuItemId: Int, drawable: Drawable?, tintColor: Int) {
+ val menuItem = navigationView.menu.findItem(menuItemId) ?: return
+ if (drawable == null) {
+ menuItem.setIcon(R.drawable.ic_link)
+ return
+ }
+
+ val resizedDrawable = DrawableUtil.getResizedDrawable(activity, drawable, ICON_SIZE_PX)
+ menuItem.icon = activity.viewThemeUtils.platform.colorDrawable(resizedDrawable, tintColor)
+ }
+
+ companion object {
+ private const val TAG = "DrawerExternalLinksMenu"
+ private const val MENU_ORDER_EXTERNAL_LINKS = 3
+ private const val MENU_ITEM_EXTERNAL_LINK = 111
+ private const val ICON_SIZE_PX = 32
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinkMenuIconTarget.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinkMenuIconTarget.kt
new file mode 100644
index 000000000000..a9aae89ea41c
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinkMenuIconTarget.kt
@@ -0,0 +1,24 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.externallinks
+
+import android.graphics.drawable.Drawable
+import com.bumptech.glide.request.target.CustomTarget
+import com.bumptech.glide.request.transition.Transition
+
+class ExternalLinkMenuIconTarget(private val onIconLoaded: (Drawable?) -> Unit) : CustomTarget() {
+
+ override fun onResourceReady(resource: Drawable, transition: Transition?) {
+ onIconLoaded(resource)
+ }
+
+ override fun onLoadFailed(errorDrawable: Drawable?) {
+ onIconLoaded(errorDrawable)
+ }
+
+ override fun onLoadCleared(placeholder: Drawable?) = Unit
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinkOpener.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinkOpener.kt
new file mode 100644
index 000000000000..3c103de60c85
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinkOpener.kt
@@ -0,0 +1,32 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.externallinks
+
+import android.app.Activity
+import android.content.Intent
+import com.nextcloud.client.utils.IntentUtil
+import com.owncloud.android.lib.common.ExternalLink
+import com.owncloud.android.ui.activity.ExternalSiteWebView
+
+class ExternalLinkOpener(private val activity: Activity) {
+
+ fun open(link: ExternalLink) {
+ if (link.redirect) {
+ IntentUtil.startLinkIntent(activity, link.url)
+ return
+ }
+ openInWebView(link)
+ }
+
+ fun openInWebView(link: ExternalLink) {
+ val intent = Intent(activity.applicationContext, ExternalSiteWebView::class.java)
+ .putExtra(ExternalSiteWebView.EXTRA_TITLE, link.name)
+ .putExtra(ExternalSiteWebView.EXTRA_URL, link.url)
+ .putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true)
+ activity.startActivity(intent)
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinksSynchronizer.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinksSynchronizer.kt
new file mode 100644
index 000000000000..a0974e1dd1d0
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/externallinks/ExternalLinksSynchronizer.kt
@@ -0,0 +1,84 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.externallinks
+
+import androidx.annotation.WorkerThread
+import com.nextcloud.client.account.User
+import com.owncloud.android.datamodel.ArbitraryDataProvider
+import com.owncloud.android.datamodel.ExternalLinksProvider
+import com.owncloud.android.lib.common.ExternalLink
+import com.owncloud.android.lib.common.accounts.ExternalLinksOperation
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.operations.GetCapabilitiesOperation
+import com.owncloud.android.ui.activity.BaseActivity
+import com.owncloud.android.ui.activity.FileActivity
+import com.owncloud.android.utils.FilesSyncHelper
+import com.owncloud.android.utils.theme.CapabilityUtils
+import kotlin.jvm.optionals.getOrNull
+
+class ExternalLinksSynchronizer(
+ private val activity: BaseActivity,
+ private val externalLinksProvider: ExternalLinksProvider,
+ private val appOpenedCountProvider: ArbitraryDataProvider
+) {
+
+ @WorkerThread
+ fun sync(user: User, force: Boolean) {
+ refreshCapabilities()
+ syncExternalLinks(user, force)
+ }
+
+ private fun refreshCapabilities() {
+ val capability = activity.capabilities.getOrNull() ?: return
+ if (capability.accountName?.isEmpty() == true) return
+ val storageManager = activity.storageManager ?: return
+ GetCapabilitiesOperation(storageManager).execute(activity.baseContext)
+ }
+
+ private fun syncExternalLinks(user: User, force: Boolean) {
+ val isExternalLinksEnabled =
+ activity.storageManager != null && CapabilityUtils.getCapability(user, activity).externalLinks.isTrue
+ if (!isExternalLinksEnabled) {
+ externalLinksProvider.deleteAllExternalLinks()
+ Log_OC.d(TAG, "links disabled")
+ return
+ }
+
+ val appOpenedCount =
+ appOpenedCountProvider.getIntegerValue(FilesSyncHelper.GLOBAL, FileActivity.APP_OPENED_COUNT)
+ val shouldUpdate = force ||
+ appOpenedCount == APP_OPENED_COUNT_NOT_SET ||
+ appOpenedCount > APP_OPENED_COUNT_UPDATE_THRESHOLD
+ if (!shouldUpdate) {
+ storeAppOpenedCount(appOpenedCount + 1)
+ return
+ }
+
+ if (force) {
+ Log_OC.d(TAG, "force update")
+ }
+ storeAppOpenedCount(0)
+
+ Log_OC.d(TAG, "update via api")
+ val result = ExternalLinksOperation().execute(user, activity)
+ if (!result.isSuccess) return
+ val externalLinks = result.data ?: return
+
+ externalLinksProvider.deleteAllExternalLinks()
+ externalLinks.filterIsInstance().forEach(externalLinksProvider::storeExternalLink)
+ }
+
+ private fun storeAppOpenedCount(count: Int) {
+ appOpenedCountProvider.storeOrUpdateKeyValue(FilesSyncHelper.GLOBAL, FileActivity.APP_OPENED_COUNT, "$count")
+ }
+
+ companion object {
+ private const val TAG = "ExternalLinks"
+ private const val APP_OPENED_COUNT_NOT_SET = -1
+ private const val APP_OPENED_COUNT_UPDATE_THRESHOLD = 10
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/fragment/DrawerFragmentFinder.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/fragment/DrawerFragmentFinder.kt
new file mode 100644
index 000000000000..ef319a13ea82
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/fragment/DrawerFragmentFinder.kt
@@ -0,0 +1,41 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.fragment
+
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import com.nextcloud.utils.extensions.lastFragment
+import com.owncloud.android.ui.activity.FileDisplayActivity
+import com.owncloud.android.ui.fragment.OCFileListFragment
+import com.owncloud.android.ui.fragment.albums.AlbumItemsFragment
+import com.owncloud.android.ui.fragment.albums.AlbumsFragment
+import java.util.Optional
+
+class DrawerFragmentFinder(private val activity: AppCompatActivity) {
+
+ private val fragmentManager: FragmentManager
+ get() = activity.supportFragmentManager
+
+ val ocFileListFragment: OCFileListFragment?
+ get() = activity.lastFragment() as? OCFileListFragment
+ ?: fragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) as? OCFileListFragment
+
+ val isAlbumsFragmentVisible: Boolean
+ get() = isFragmentVisible(AlbumsFragment.TAG)
+
+ val isAlbumItemsFragmentVisible: Boolean
+ get() = isFragmentVisible(AlbumItemsFragment.TAG)
+
+ fun getFragment(tag: String?, clazz: Class): Optional =
+ Optional.ofNullable(fragmentManager.findFragmentByTag(tag))
+ .filter(clazz::isInstance)
+ .map(clazz::cast)
+
+ private inline fun isFragmentVisible(tag: String): Boolean =
+ (fragmentManager.findFragmentByTag(tag) as? T)?.isVisible == true
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/DrawerHeaderController.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/DrawerHeaderController.kt
new file mode 100644
index 000000000000..603f70bd1e9d
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/DrawerHeaderController.kt
@@ -0,0 +1,74 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.header
+
+import android.graphics.drawable.Drawable
+import android.view.View
+import android.webkit.URLUtil
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.nextcloud.utils.GlideHelper
+import com.owncloud.android.R
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.navigation.DrawerNavigator
+import kotlin.jvm.optionals.getOrNull
+
+class DrawerHeaderController(private val activity: DrawerActivity, navigator: DrawerNavigator) {
+
+ private val brandingColors = ServerBrandingColors(activity)
+ private val ecosystemAppsBanner = EcosystemAppsBanner(activity, navigator, brandingColors)
+
+ fun update(header: View) {
+ val isClientBranded = activity.resources.getBoolean(R.bool.is_branded_client)
+ if (!isClientBranded) {
+ showServerBranding(header)
+ }
+
+ val banner = header.findViewById(R.id.drawer_ecosystem_apps)
+ if (isClientBranded || !activity.appPreferences.isShowEcosystemApps) {
+ banner.visibility = View.GONE
+ } else {
+ ecosystemAppsBanner.show(banner)
+ }
+ }
+
+ private fun showServerBranding(header: View) {
+ val account = activity.account ?: return
+ val capability = activity.capabilities.getOrNull() ?: return
+ if (capability.serverBackground == null) return
+
+ val primaryColor = brandingColors.getPrimaryColor(account)
+ header.findViewById(R.id.drawer_header_view).setBackgroundColor(primaryColor)
+
+ val serverLogoURL = capability.serverLogo
+ if (serverLogoURL.isNullOrEmpty() || !URLUtil.isValidUrl(serverLogoURL)) return
+
+ GlideHelper.loadIntoTarget(
+ activity,
+ activity.userAccountManager.currentOwnCloudAccount,
+ serverLogoURL,
+ ServerLogoTarget(activity.resources, primaryColor) { setHeaderLogo(header, it, capability.serverName) },
+ R.drawable.background
+ )
+ }
+
+ private fun setHeaderLogo(header: View, drawable: Drawable, serverName: String?) {
+ header.findViewById(R.id.drawer_header_logo).run {
+ setImageDrawable(drawable)
+ adjustViewBounds = true
+ }
+
+ if (serverName.isNullOrEmpty()) return
+ header.findViewById(R.id.drawer_header_server_name).run {
+ visibility = View.VISIBLE
+ text = serverName
+ setTextColor(brandingColors.textColor)
+ }
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/EcosystemAppsBanner.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/EcosystemAppsBanner.kt
new file mode 100644
index 000000000000..2e36c0ea8e99
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/EcosystemAppsBanner.kt
@@ -0,0 +1,72 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.header
+
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.graphics.drawable.GradientDrawable
+import android.view.View
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isVisible
+import com.nextcloud.android.common.core.utils.ecosystem.EcosystemApp
+import com.nextcloud.utils.LinkHelper
+import com.nextcloud.utils.view.ScreenMetrics
+import com.owncloud.android.R
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.navigation.DrawerNavigator
+import hct.Hct
+import kotlin.jvm.optionals.getOrNull
+
+class EcosystemAppsBanner(
+ private val activity: DrawerActivity,
+ private val navigator: DrawerNavigator,
+ private val brandingColors: ServerBrandingColors
+) {
+
+ private val iconColor: Int
+ get() {
+ val account = activity.account ?: return activity.getColor(R.color.grey_800_transparent)
+ val isServerPrimaryColorDark =
+ Hct.fromInt(brandingColors.getPrimaryColor(account)).tone < LIGHT_TONE_THRESHOLD
+ return if (isServerPrimaryColorDark) Color.WHITE else activity.getColor(R.color.grey_800_transparent)
+ }
+
+ fun show(banner: ConstraintLayout) {
+ val notesView = banner.findViewById(R.id.drawer_ecosystem_notes)
+ val talkView = banner.findViewById(R.id.drawer_ecosystem_talk)
+ val moreView = banner.findViewById(R.id.drawer_ecosystem_more)
+ val assistantView = banner.findViewById(R.id.drawer_ecosystem_assistant)
+
+ activity.user.getOrNull()?.accountName?.let { accountName ->
+ notesView.setOnClickListener { activity.ecosystemManager.openApp(EcosystemApp.NOTES, accountName) }
+ talkView.setOnClickListener { activity.ecosystemManager.openApp(EcosystemApp.TALK, accountName) }
+ }
+ moreView.setOnClickListener { LinkHelper.openAppStore(APP_STORE_QUERY, true, activity) }
+ assistantView.setOnClickListener { navigator.startAssistantScreen() }
+ assistantView.isVisible = activity.isAssistantAvailable
+
+ val color = iconColor
+ listOf(notesView, talkView, moreView, assistantView).forEach { tintAppView(it, color) }
+
+ banner.visibility = View.VISIBLE
+ }
+
+ private fun tintAppView(appView: LinearLayout, color: Int) {
+ val imageView = appView.getChildAt(0) as ImageView
+ imageView.imageTintList = ColorStateList.valueOf(color)
+ (imageView.background as GradientDrawable).setStroke(ScreenMetrics.dpToPx(1f, activity), color)
+ (appView.getChildAt(1) as TextView).setTextColor(color)
+ }
+
+ companion object {
+ private const val APP_STORE_QUERY = "Nextcloud"
+ private const val LIGHT_TONE_THRESHOLD = 80.0
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/ServerBrandingColors.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/ServerBrandingColors.kt
new file mode 100644
index 000000000000..c56a8f4d0332
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/ServerBrandingColors.kt
@@ -0,0 +1,29 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.header
+
+import android.accounts.Account
+import android.graphics.Color
+import com.nextcloud.android.common.ui.util.PlatformThemeUtil
+import com.owncloud.android.R
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.utils.theme.CapabilityUtils
+
+class ServerBrandingColors(private val activity: DrawerActivity) {
+
+ val textColor: Int
+ get() {
+ val serverTextColor = CapabilityUtils.getCapability(activity).serverTextColor
+ val fallbackColor = if (PlatformThemeUtil.isDarkMode(activity)) Color.WHITE else Color.BLACK
+ return activity.colorUtil.getNullSafeColor(serverTextColor, fallbackColor)
+ }
+
+ fun getPrimaryColor(account: Account): Int {
+ val serverColor = CapabilityUtils.getCapability(account, activity).serverColor
+ return activity.colorUtil.getNullSafeColor(serverColor, activity.getColor(R.color.primary))
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/ServerLogoTarget.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/ServerLogoTarget.kt
new file mode 100644
index 000000000000..4ff0f32fa0a0
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/header/ServerLogoTarget.kt
@@ -0,0 +1,59 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.header
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.PictureDrawable
+import androidx.core.graphics.createBitmap
+import androidx.core.graphics.drawable.toDrawable
+import com.bumptech.glide.request.target.CustomTarget
+import com.bumptech.glide.request.transition.Transition
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.utils.BitmapUtils
+import kotlin.math.max
+
+class ServerLogoTarget(
+ private val resources: Resources,
+ private val primaryColor: Int,
+ private val onLogoReady: (LayerDrawable) -> Unit
+) : CustomTarget() {
+
+ override fun onResourceReady(resource: Drawable, transition: Transition?) {
+ val bitmap = resource.toLogoBitmap() ?: return
+ val layers = arrayOf(primaryColor.toDrawable(), bitmap.scaledToLogoSize().toDrawable(resources))
+ onLogoReady(LayerDrawable(layers))
+ }
+
+ override fun onLoadCleared(placeholder: Drawable?) = Unit
+
+ private fun Drawable.toLogoBitmap(): Bitmap? = when (this) {
+ is PictureDrawable -> createBitmap(intrinsicWidth, intrinsicHeight).also { Canvas(it).drawPicture(picture) }
+
+ is BitmapDrawable -> bitmap
+
+ else -> {
+ Log_OC.e(TAG, "Unsupported drawable type: " + javaClass.name)
+ null
+ }
+ }
+
+ private fun Bitmap.scaledToLogoSize(): Bitmap {
+ val maxDimension = max(width, height)
+ if (maxDimension <= MAX_LOGO_SIZE_PX) return this
+ return BitmapUtils.scaleBitmap(this, MAX_LOGO_SIZE_PX.toFloat(), width, height, maxDimension)
+ }
+
+ companion object {
+ private const val TAG = "ServerLogoTarget"
+ private const val MAX_LOGO_SIZE_PX = 1000
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerDeepLinkHandler.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerDeepLinkHandler.kt
new file mode 100644
index 000000000000..3509ca50d9db
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerDeepLinkHandler.kt
@@ -0,0 +1,58 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.navigation
+
+import android.content.Intent
+import android.net.Uri
+import android.view.View
+import androidx.annotation.IdRes
+import androidx.core.net.toUri
+import com.nextcloud.client.files.DeepLinkConstants
+import com.nextcloud.utils.LinkHelper
+import com.nextcloud.utils.SnackbarUtil
+import com.owncloud.android.R
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.ui.navigation.NavigatorScreen
+
+class DrawerDeepLinkHandler(
+ private val activity: DrawerActivity,
+ private val navigator: DrawerNavigator,
+ private val menuItemClickHandler: DrawerMenuItemClickHandler
+) {
+
+ fun handle(uri: Uri) {
+ val path = uri.lastPathSegment ?: return
+ val deepLinkType = DeepLinkConstants.fromPath(path)
+ if (deepLinkType == null) {
+ SnackbarUtil.show(activity, activity.getString(R.string.invalid_url))
+ return
+ }
+
+ when (deepLinkType) {
+ DeepLinkConstants.OPEN_AUTO_UPLOAD -> navigator.pushFragment(NavigatorScreen.AutoUpload)
+ DeepLinkConstants.OPEN_EXTERNAL_URL -> openExternalUrl(uri)
+ DeepLinkConstants.ACTION_CREATE_NEW -> activity.findViewById(R.id.fab_main)?.callOnClick()
+ DeepLinkConstants.ACTION_APP_UPDATE -> LinkHelper.openAppStore(activity.packageName, false, activity)
+ DeepLinkConstants.OPEN_NOTIFICATIONS -> navigator.pushFragment(NavigatorScreen.Notifications)
+ else -> openNavigationItem(deepLinkType.navId)
+ }
+ }
+
+ private fun openExternalUrl(uri: Uri) {
+ val url = uri.getQueryParameter(URL_QUERY_PARAMETER) ?: return
+ activity.startActivity(Intent(Intent.ACTION_VIEW, url.toUri()))
+ }
+
+ private fun openNavigationItem(@IdRes menuItemId: Int) {
+ val menuItem = activity.drawerNavigationView?.menu?.findItem(menuItemId) ?: return
+ menuItemClickHandler.onNavigationItemClicked(menuItem)
+ }
+
+ companion object {
+ private const val URL_QUERY_PARAMETER = "url"
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerMenuFilter.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerMenuFilter.kt
new file mode 100644
index 000000000000..2abc4c4e6bc5
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerMenuFilter.kt
@@ -0,0 +1,32 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.navigation
+
+import android.content.res.Resources
+import android.view.Menu
+import com.nextcloud.client.account.User
+import com.owncloud.android.R
+import com.owncloud.android.lib.resources.status.OCCapability
+import com.owncloud.android.utils.DrawerMenuUtil
+
+object DrawerMenuFilter {
+
+ fun filter(menu: Menu, user: User, capability: OCCapability?, resources: Resources) {
+ capability?.let {
+ DrawerMenuUtil.filterTrashbinMenuItem(menu, it)
+ DrawerMenuUtil.filterActivityMenuItem(menu, it)
+ DrawerMenuUtil.filterGroupfoldersMenuItem(menu, it)
+ DrawerMenuUtil.filterAssistantMenuItem(menu, it, resources)
+ }
+
+ DrawerMenuUtil.filterSearchMenuItems(menu, user)
+ DrawerMenuUtil.setupHomeMenuItem(menu, resources)
+ DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, !resources.getBoolean(R.bool.participate_enabled))
+ DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !resources.getBoolean(R.bool.shared_enabled))
+ DrawerMenuUtil.removeMenuItem(menu, R.id.nav_logout, !resources.getBoolean(R.bool.show_drawer_logout))
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerMenuItemClickHandler.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerMenuItemClickHandler.kt
new file mode 100644
index 000000000000..0e0ef9cadf21
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerMenuItemClickHandler.kt
@@ -0,0 +1,96 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.navigation
+
+import android.view.MenuItem
+import com.nextcloud.ui.composeActivity.ComposeActivity
+import com.nextcloud.utils.extensions.navigateToAllFiles
+import com.owncloud.android.R
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.externallinks.DrawerExternalLinksMenu
+import com.owncloud.android.ui.events.ChangeMenuEvent
+import com.owncloud.android.ui.navigation.NavigatorScreen
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
+import org.greenrobot.eventbus.EventBus
+
+class DrawerMenuItemClickHandler(
+ private val activity: DrawerActivity,
+ private val navigator: DrawerNavigator,
+ private val externalLinksMenu: DrawerExternalLinksMenu
+) {
+
+ fun onNavigationItemClicked(menuItem: MenuItem) {
+ val itemId = menuItem.itemId
+ when (itemId) {
+ R.id.nav_all_files, R.id.nav_personal_files -> navigator.openAllFiles(itemId == R.id.nav_personal_files)
+ R.id.nav_favorites -> navigator.openFavoritesTab()
+ R.id.nav_gallery -> navigator.openMediaTab()
+ R.id.nav_album -> navigator.openAlbums()
+ R.id.nav_on_device -> navigator.showOnDeviceFiles()
+ in navigatorScreensByMenuItemId -> navigatorScreensByMenuItemId[itemId]?.let(navigator::openNavigatorScreen)
+ R.id.nav_settings -> navigator.openSettings()
+ R.id.nav_logout -> navigator.openAccountRemovalDialog(menuItem)
+ R.id.nav_shared -> navigator.openSharedTab()
+ R.id.nav_recent_files -> navigator.openRecentFiles()
+ R.id.nav_assistant -> navigator.openAssistant()
+ R.id.nav_groupfolders -> navigator.openGroupfolders()
+ else -> onOtherNavigationItemClicked(menuItem)
+ }
+
+ activity.ocFileListFragment?.resetFileDepth()
+ activity.highlightNavigationViewItem(itemId)
+ }
+
+ @SuppressFBWarnings("RV")
+ fun onBottomNavigationItemSelected(menuItem: MenuItem): Boolean {
+ val itemId = menuItem.itemId
+ activity.ocFileListFragment?.exitSelectionMode()
+ navigator.resetOnlyPersonalAndOnDevice()
+
+ when (itemId) {
+ R.id.nav_all_files -> {
+ activity.navigateToAllFiles()
+ EventBus.getDefault().post(ChangeMenuEvent())
+ }
+
+ R.id.nav_favorites -> navigator.openFavoritesTab()
+
+ R.id.nav_assistant -> if (activity !is ComposeActivity) navigator.startAssistantScreen()
+
+ R.id.nav_gallery -> navigator.openMediaTab()
+
+ R.id.nav_album -> navigator.replaceAlbumFragment()
+ }
+
+ activity.supportActionBar?.setIcon(null)
+ activity.ocFileListFragment?.resetFileDepth()
+ activity.highlightNavigationViewItem(itemId)
+ return false
+ }
+
+ private fun onOtherNavigationItemClicked(menuItem: MenuItem) {
+ if (menuItem.groupId == R.id.drawer_menu_external_links) {
+ externalLinksMenu.onExternalLinkClicked(menuItem)
+ return
+ }
+ Log_OC.w(TAG, "Unknown drawer menu item clicked: " + menuItem.title)
+ }
+
+ companion object {
+ private const val TAG = "DrawerMenuItemClickHandler"
+
+ private val navigatorScreensByMenuItemId: Map by lazy {
+ mapOf(
+ R.id.nav_uploads to NavigatorScreen.UploadList,
+ R.id.nav_trashbin to NavigatorScreen.Trashbin,
+ R.id.nav_activity to NavigatorScreen.Activities,
+ R.id.nav_community to NavigatorScreen.Community
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerNavigator.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerNavigator.kt
new file mode 100644
index 000000000000..3e4eda7df728
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/navigation/DrawerNavigator.kt
@@ -0,0 +1,166 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.navigation
+
+import android.content.Intent
+import android.view.MenuItem
+import androidx.fragment.app.commit
+import com.nextcloud.ui.composeActivity.ComposeActivity
+import com.nextcloud.ui.composeActivity.ComposeDestination
+import com.nextcloud.utils.extensions.navigateToAllFiles
+import com.owncloud.android.MainApp
+import com.owncloud.android.R
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.lib.resources.files.SearchRemoteOperation
+import com.owncloud.android.ui.activity.FileDisplayActivity
+import com.owncloud.android.ui.activity.SettingsActivity
+import com.owncloud.android.ui.activity.UserInfoActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.ui.events.ChangeMenuEvent
+import com.owncloud.android.ui.events.SearchEvent
+import com.owncloud.android.ui.fragment.GalleryFragment
+import com.owncloud.android.ui.fragment.OCFileListFragment
+import com.owncloud.android.ui.fragment.albums.AlbumsFragment
+import com.owncloud.android.ui.navigation.NavigatorActivity
+import com.owncloud.android.ui.navigation.NavigatorScreen
+import com.owncloud.android.ui.navigation.model.ActionBarStyle
+import org.greenrobot.eventbus.EventBus
+import kotlin.jvm.optionals.getOrNull
+
+class DrawerNavigator(private val activity: DrawerActivity) {
+
+ fun pushFragment(screen: NavigatorScreen) {
+ if (activity is NavigatorActivity) {
+ activity.push(screen)
+ return
+ }
+ activity.startActivity(NavigatorActivity.intent(activity, screen))
+ }
+
+ fun startPhotoSearch() {
+ MainApp.showOnlyFilesOnDevice(false)
+ launchActivityForSearch(SearchEvent(PHOTO_SEARCH_MIME_TYPE, SearchRemoteOperation.SearchType.PHOTO_SEARCH))
+ }
+
+ fun showFiles(onDeviceOnly: Boolean, onlyPersonalFiles: Boolean) {
+ MainApp.showOnlyFilesOnDevice(onDeviceOnly)
+ MainApp.showOnlyPersonalFiles(onlyPersonalFiles)
+ activity.startActivity(fileDisplayActivityIntent(FileDisplayActivity.ALL_FILES))
+ }
+
+ fun showOnDeviceFiles() {
+ MainApp.showOnlyFilesOnDevice(true)
+ MainApp.showOnlyPersonalFiles(false)
+ activity.startActivity(fileDisplayActivityIntent(FileDisplayActivity.ON_DEVICE))
+ }
+
+ fun replaceAlbumFragment() {
+ if (activity.isAlbumsFragment) return
+ activity.supportFragmentManager.commit {
+ addToBackStack(null)
+ replace(R.id.left_fragment_container, AlbumsFragment.newInstance(false), AlbumsFragment.TAG)
+ }
+ }
+
+ fun restartFileDisplayActivity() {
+ activity.startActivity(fileDisplayActivityIntent(FileDisplayActivity.RESTART))
+ }
+
+ fun openAllFiles(onlyPersonal: Boolean) {
+ activity.closeDrawer()
+ activity.navigateToAllFiles(onlyPersonal)
+ EventBus.getDefault().post(ChangeMenuEvent())
+ }
+
+ fun openFavoritesTab() {
+ resetOnlyPersonalAndOnDevice()
+ activity.showActionBar(ActionBarStyle.Plain)
+ launchActivityForSearch(SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH))
+ }
+
+ fun openMediaTab() {
+ GalleryFragment.clearSavedViewState()
+ resetOnlyPersonalAndOnDevice()
+ activity.showActionBar(ActionBarStyle.Plain)
+ startPhotoSearch()
+ }
+
+ fun openAlbums() {
+ if (activity is FileDisplayActivity) {
+ replaceAlbumFragment()
+ } else {
+ activity.startActivity(fileDisplayActivityIntent(FileDisplayActivity.ALBUMS))
+ }
+ }
+
+ fun openNavigatorScreen(screen: NavigatorScreen) {
+ resetOnlyPersonalAndOnDevice()
+ pushFragment(screen)
+ }
+
+ fun openSettings() {
+ resetOnlyPersonalAndOnDevice()
+ activity.startActivity(Intent(activity, SettingsActivity::class.java))
+ }
+
+ fun openAccountRemovalDialog(menuItem: MenuItem) {
+ resetOnlyPersonalAndOnDevice()
+ val uncheckedMenuItem = menuItem.setChecked(false)
+ Log_OC.d(TAG, "onNavigationItemClicked nav_logout setChecked $uncheckedMenuItem")
+ activity.user.getOrNull()?.let {
+ UserInfoActivity.openAccountRemovalDialog(it, activity.supportFragmentManager)
+ }
+ }
+
+ fun openSharedTab() {
+ resetOnlyPersonalAndOnDevice()
+ launchActivityForSearch(SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER))
+ }
+
+ fun openRecentFiles() {
+ resetOnlyPersonalAndOnDevice()
+ launchActivityForSearch(SearchEvent("", SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH))
+ }
+
+ fun openAssistant() {
+ resetOnlyPersonalAndOnDevice()
+ startAssistantScreen()
+ }
+
+ fun openGroupfolders() {
+ resetOnlyPersonalAndOnDevice()
+ activity.startActivity(fileDisplayActivityIntent(FileDisplayActivity.LIST_GROUPFOLDERS))
+ }
+
+ fun startAssistantScreen() {
+ val intent = Intent(activity.applicationContext, ComposeActivity::class.java)
+ .putExtra(ComposeActivity.DESTINATION, ComposeDestination.getAssistantScreen(activity))
+ activity.startActivity(intent)
+ }
+
+ fun resetOnlyPersonalAndOnDevice() {
+ MainApp.showOnlyFilesOnDevice(false)
+ MainApp.showOnlyPersonalFiles(false)
+ }
+
+ private fun launchActivityForSearch(searchEvent: SearchEvent) {
+ val intent = fileDisplayActivityIntent(Intent.ACTION_SEARCH)
+ .putExtra(OCFileListFragment.SEARCH_EVENT, searchEvent)
+ activity.startActivity(intent)
+ }
+
+ private fun fileDisplayActivityIntent(action: String): Intent =
+ Intent(activity.applicationContext, FileDisplayActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ this.action = action
+ }
+
+ companion object {
+ private const val TAG = "DrawerNavigator"
+ private const val PHOTO_SEARCH_MIME_TYPE = "image/%"
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/DrawerQuotaController.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/DrawerQuotaController.kt
new file mode 100644
index 000000000000..a8384d88ef1c
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/DrawerQuotaController.kt
@@ -0,0 +1,137 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.quota
+
+import android.view.View
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.annotation.IdRes
+import androidx.core.view.isVisible
+import androidx.lifecycle.lifecycleScope
+import com.google.android.material.navigation.NavigationView
+import com.google.android.material.progressindicator.LinearProgressIndicator
+import com.nextcloud.android.common.ui.theme.utils.ColorRole
+import com.nextcloud.utils.GlideHelper
+import com.nextcloud.utils.HumanReadableFormatter
+import com.nextcloud.utils.mdm.MDMConfig
+import com.owncloud.android.R
+import com.owncloud.android.lib.common.ExternalLink
+import com.owncloud.android.lib.common.ExternalLinkType
+import com.owncloud.android.lib.common.Quota
+import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.externallinks.ExternalLinkOpener
+import kotlinx.coroutines.launch
+import kotlin.math.ceil
+import kotlin.math.roundToInt
+
+class DrawerQuotaController(private val activity: DrawerActivity, private val externalLinkOpener: ExternalLinkOpener) {
+ private val quotaFetcher = UserQuotaFetcher(activity)
+
+ private var quotaView: LinearLayout? = null
+ private var quotaProgressBar: LinearProgressIndicator? = null
+ private var quotaTextPercentage: TextView? = null
+ private var quotaTextLink: TextView? = null
+
+ private val Quota.isDisplayable: Boolean
+ get() = quota > 0 ||
+ quota == GetUserInfoRemoteOperation.SPACE_UNLIMITED ||
+ quota == GetUserInfoRemoteOperation.QUOTA_LIMIT_INFO_NOT_AVAILABLE
+
+ fun setupViews(navigationView: NavigationView) {
+ quotaView = findQuotaViewById(navigationView, R.id.drawer_quota)
+ quotaProgressBar = findQuotaViewById(navigationView, R.id.drawer_quota_ProgressBar)
+ quotaTextPercentage = findQuotaViewById(navigationView, R.id.drawer_quota_percentage)
+ quotaTextLink = findQuotaViewById(navigationView, R.id.drawer_quota_link)
+
+ quotaProgressBar?.let {
+ activity.viewThemeUtils.material.colorProgressBar(it, ColorRole.PRIMARY)
+ it.trackStopIndicatorSize = 0
+ }
+ quotaView?.let { activity.viewThemeUtils.platform.colorViewBackground(it) }
+ }
+
+ fun fetchAndDisplay() {
+ activity.lifecycleScope.launch {
+ val quota = quotaFetcher.fetch() ?: return@launch
+ if (quota.isDisplayable) {
+ showQuotaInformation(quota)
+ } else {
+ quotaView?.isVisible = false
+ }
+ }
+ }
+
+ fun updateQuotaLink() {
+ val quotaTextLink = quotaTextLink ?: return
+ if (!MDMConfig.externalSiteSupport(activity)) {
+ quotaTextLink.visibility = View.GONE
+ return
+ }
+
+ activity.externalLinksProvider.getExternalLink(ExternalLinkType.QUOTA) { quotas ->
+ val firstQuota = quotas.firstOrNull()
+ if (firstQuota == null) {
+ quotaTextLink.visibility = View.GONE
+ } else {
+ showQuotaLink(quotaTextLink, firstQuota)
+ }
+ }
+ }
+
+ private fun findQuotaViewById(navigationView: NavigationView, @IdRes id: Int): T? =
+ navigationView.getHeaderView(0)?.findViewById(id) ?: activity.findViewById(id)
+
+ private fun showQuotaInformation(quota: Quota) {
+ quotaTextPercentage?.text = if (quota.quota == GetUserInfoRemoteOperation.SPACE_UNLIMITED) {
+ activity.getString(R.string.drawer_quota_unlimited, HumanReadableFormatter.formatBytes(quota.used))
+ } else {
+ activity.getString(
+ R.string.drawer_quota,
+ HumanReadableFormatter.formatBytes(quota.used),
+ HumanReadableFormatter.formatBytes(quota.total)
+ )
+ }
+
+ val relative = ceil(quota.relative).toInt()
+ quotaProgressBar?.let { progressBar ->
+ progressBar.progress = relative
+ if (relative < RELATIVE_THRESHOLD_WARNING) {
+ activity.viewThemeUtils.material.colorProgressBar(progressBar, ColorRole.PRIMARY)
+ } else {
+ activity.viewThemeUtils.material.colorProgressBar(
+ progressBar,
+ activity.resources.getColor(R.color.infolevel_warning, null)
+ )
+ }
+ }
+
+ updateQuotaLink()
+ quotaView?.isVisible = true
+ }
+
+ private fun showQuotaLink(quotaTextLink: TextView, quota: ExternalLink) {
+ quotaTextLink.text = quota.name
+ quotaTextLink.isClickable = true
+ quotaTextLink.visibility = View.VISIBLE
+ quotaTextLink.setOnClickListener { externalLinkOpener.openInWebView(quota) }
+
+ val iconSize = (LINK_ICON_SIZE_DP * activity.resources.displayMetrics.density).roundToInt()
+ GlideHelper.loadIntoTarget(
+ activity,
+ activity.userAccountManager.currentOwnCloudAccount,
+ quota.iconUrl,
+ QuotaLinkIconTarget(iconSize, quotaTextLink),
+ R.drawable.ic_link
+ )
+ }
+
+ companion object {
+ private const val RELATIVE_THRESHOLD_WARNING = 80
+ private const val LINK_ICON_SIZE_DP = 24
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/QuotaLinkIconTarget.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/QuotaLinkIconTarget.kt
new file mode 100644
index 000000000000..32e0db8a59b4
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/QuotaLinkIconTarget.kt
@@ -0,0 +1,32 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.quota
+
+import android.graphics.drawable.Drawable
+import android.widget.TextView
+import com.bumptech.glide.request.target.CustomTarget
+import com.bumptech.glide.request.transition.Transition
+
+class QuotaLinkIconTarget(private val iconSize: Int, private val quotaTextLink: TextView) :
+ CustomTarget() {
+
+ override fun onResourceReady(resource: Drawable, transition: Transition?) {
+ setIcon(resource)
+ }
+
+ override fun onLoadCleared(placeholder: Drawable?) = Unit
+
+ override fun onLoadFailed(errorDrawable: Drawable?) {
+ super.onLoadFailed(errorDrawable)
+ errorDrawable?.let(::setIcon)
+ }
+
+ private fun setIcon(drawable: Drawable) {
+ val icon = drawable.current.apply { setBounds(0, 0, iconSize, iconSize) }
+ quotaTextLink.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null)
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/UserQuotaFetcher.kt b/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/UserQuotaFetcher.kt
new file mode 100644
index 000000000000..1b6b3f6ad909
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/activity/drawer/quota/UserQuotaFetcher.kt
@@ -0,0 +1,23 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+package com.owncloud.android.ui.activity.drawer.quota
+
+import com.owncloud.android.lib.common.Quota
+import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation
+import com.owncloud.android.ui.activity.BaseActivity
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class UserQuotaFetcher(private val activity: BaseActivity) {
+
+ suspend fun fetch(): Quota? = withContext(Dispatchers.IO) {
+ if (activity.userAccountManager.user.isAnonymous) return@withContext null
+ val client = activity.clientRepository.getNextcloudClient() ?: return@withContext null
+ val result = GetUserInfoRemoteOperation().execute(client)
+ if (result.isSuccess) result.resultData?.quota else null
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/filelist/header/OCFileListHeaderBinder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/filelist/header/OCFileListHeaderBinder.kt
index 51d0e1174b3c..4bc23323f3b5 100644
--- a/app/src/main/java/com/owncloud/android/ui/adapter/filelist/header/OCFileListHeaderBinder.kt
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/filelist/header/OCFileListHeaderBinder.kt
@@ -17,7 +17,7 @@ import com.owncloud.android.databinding.ListHeaderBinding
import com.owncloud.android.databinding.ListHeaderOpenInBinding
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.lib.resources.status.OCCapability
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.adapter.filelist.recommended.RecommendedFilesAdapter
import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface
import com.owncloud.android.ui.preview.text.PreviewTextFragment
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/storagePermissionBanner/StoragePermissionBannerActionHandler.kt b/app/src/main/java/com/owncloud/android/ui/adapter/storagePermissionBanner/StoragePermissionBannerActionHandler.kt
index 0d336bfaabcc..4a5a8ad655c6 100644
--- a/app/src/main/java/com/owncloud/android/ui/adapter/storagePermissionBanner/StoragePermissionBannerActionHandler.kt
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/storagePermissionBanner/StoragePermissionBannerActionHandler.kt
@@ -14,8 +14,8 @@ import com.nextcloud.utils.extensions.openMediaPermissions
import com.nextcloud.utils.extensions.setVisibleIf
import com.owncloud.android.MainApp
import com.owncloud.android.databinding.StoragePermissionWarningBannerBinding
-import com.owncloud.android.ui.activity.DrawerActivity.REQ_ALL_FILES_ACCESS
-import com.owncloud.android.ui.activity.DrawerActivity.REQ_MEDIA_ACCESS
+import com.owncloud.android.ui.activity.drawer.DrawerActivity.Companion.REQ_ALL_FILES_ACCESS
+import com.owncloud.android.ui.activity.drawer.DrawerActivity.Companion.REQ_MEDIA_ACCESS
import com.owncloud.android.utils.PermissionUtil
fun StoragePermissionWarningBannerBinding.setup(activity: Activity, descriptionId: Int) {
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt
index 6729649ebc70..d393c0403eef 100644
--- a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt
@@ -42,7 +42,7 @@ import com.owncloud.android.datamodel.UploadsStorageManager
import com.owncloud.android.db.OCUpload
import com.owncloud.android.db.UploadResult
import com.owncloud.android.lib.common.utils.Log_OC
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.adapter.progressListener.UploadProgressListener
import com.owncloud.android.ui.adapter.uploadList.helper.UploadListAdapterHelper
import com.owncloud.android.ui.adapter.uploadList.helper.UploadListItemOnClick
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/helper/UploadListAdapterHelper.kt b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/helper/UploadListAdapterHelper.kt
index a291b551ff18..d90ba0eb1883 100644
--- a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/helper/UploadListAdapterHelper.kt
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/helper/UploadListAdapterHelper.kt
@@ -17,7 +17,7 @@ import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.db.OCUpload
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.ui.activity.ConflictsResolveActivity
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.ui.preview.media.PreviewMediaFragment
import com.owncloud.android.utils.MimeType
diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt
index 7ed558385c64..147e2793b677 100644
--- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt
+++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt
@@ -66,7 +66,7 @@ import com.owncloud.android.lib.resources.files.ToggleFavoriteRemoteOperation
import com.owncloud.android.lib.resources.shares.OCShare
import com.owncloud.android.lib.resources.shares.ShareType
import com.owncloud.android.lib.resources.tags.Tag
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.ui.activity.ToolbarActivity
import com.owncloud.android.ui.adapter.FileDetailTabAdapter
diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt
index 3aed6e84447b..e41009014b6a 100644
--- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt
+++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt
@@ -84,7 +84,7 @@ import com.owncloud.android.lib.resources.files.ToggleFavoriteRemoteOperation
import com.owncloud.android.lib.resources.status.OCCapability
import com.owncloud.android.lib.resources.status.Type
import com.owncloud.android.ui.CompletionCallback
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.activity.FileActivity
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.ui.activity.FolderPickerActivity
diff --git a/app/src/main/java/com/owncloud/android/ui/navigation/NavigatorActivity.kt b/app/src/main/java/com/owncloud/android/ui/navigation/NavigatorActivity.kt
index 567288e8aa01..f7b190cfa35e 100644
--- a/app/src/main/java/com/owncloud/android/ui/navigation/NavigatorActivity.kt
+++ b/app/src/main/java/com/owncloud/android/ui/navigation/NavigatorActivity.kt
@@ -17,7 +17,7 @@ import androidx.fragment.app.Fragment
import com.nextcloud.utils.extensions.getParcelableArgument
import com.owncloud.android.R
import com.owncloud.android.databinding.ActivityNavigatorBinding
-import com.owncloud.android.ui.activity.DrawerActivity
+import com.owncloud.android.ui.activity.drawer.DrawerActivity
import com.owncloud.android.ui.navigation.listener.NavigatorOnBackPressListener
import dagger.android.support.AndroidSupportInjection
@@ -84,10 +84,11 @@ class NavigatorActivity : DrawerActivity() {
return super.onOptionsItemSelected(item)
}
- override fun getMenuItemId(): Int {
- val screen = intent.getParcelableArgument(EXTRA_SCREEN, NavigatorScreen::class.java)
- return screen?.menuItemId() ?: super.getMenuItemId()
- }
+ override val menuItemId: Int
+ get() {
+ val screen = intent.getParcelableArgument(EXTRA_SCREEN, NavigatorScreen::class.java)
+ return screen?.menuItemId() ?: super.menuItemId
+ }
override fun onResume() {
super.onResume()
diff --git a/app/src/main/java/com/owncloud/android/ui/preview/media/PreviewMediaActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/media/PreviewMediaActivity.kt
index c695da8a0bef..154248d0014a 100644
--- a/app/src/main/java/com/owncloud/android/ui/preview/media/PreviewMediaActivity.kt
+++ b/app/src/main/java/com/owncloud/android/ui/preview/media/PreviewMediaActivity.kt
@@ -185,7 +185,8 @@ class PreviewMediaActivity :
sendShareDownloader.downloadFile(file, packageName, activityName)
}
- override fun getMenuItemId(): Int = R.id.nav_gallery
+ override val menuItemId: Int
+ get() = R.id.nav_gallery
private fun applyDisplayCutOutTopPadding() {
window.decorView.setOnApplyWindowInsetsListener { view, insets ->