Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ package com.nextcloud.utils.extensions

import android.app.Activity
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityOptionsCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
Expand All @@ -37,6 +40,19 @@ fun Activity.showShareIntent(text: String?) {
startActivity(shareIntent)
}

fun Activity.scaleUpAnimationFrom(boundsOnScreen: Rect): Bundle? {
val decorView = window.decorView
val decorLocation = IntArray(2).also { decorView.getLocationOnScreen(it) }

return ActivityOptionsCompat.makeScaleUpAnimation(
decorView,
boundsOnScreen.left - decorLocation[0],
boundsOnScreen.top - decorLocation[1],
boundsOnScreen.width(),
boundsOnScreen.height()
).toBundle()
}

fun ComponentActivity.observeWorker(onCollect: (WorkerState?) -> Unit) {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.graphics.Outline
import android.graphics.Rect
import android.util.TypedValue
import android.view.View
import android.view.ViewOutlineProvider
Expand All @@ -20,6 +21,11 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import com.google.android.material.behavior.HideViewOnScrollBehavior

fun View.boundsOnScreen(): Rect {
val location = IntArray(2).also { getLocationOnScreen(it) }
return Rect(location[0], location[1], location[0] + width, location[1] + height)
}

fun View?.setVisibleIf(condition: Boolean) {
if (this == null) return
visibility = if (condition) View.VISIBLE else View.GONE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Rect
import android.net.Uri
import android.os.Build
import android.os.Bundle
Expand Down Expand Up @@ -80,13 +81,15 @@ import com.nextcloud.model.OCUploadLocalPathData
import com.nextcloud.model.WorkerState.OfflineOperationsCompleted
import com.nextcloud.ui.composeActivity.ComposeProcessTextAlias
import com.nextcloud.utils.SnackbarUtil
import com.nextcloud.utils.extensions.boundsOnScreen
import com.nextcloud.utils.extensions.getParcelableArgument
import com.nextcloud.utils.extensions.getSerializableArgument
import com.nextcloud.utils.extensions.isActive
import com.nextcloud.utils.extensions.isDialogFragmentReady
import com.nextcloud.utils.extensions.lastFragment
import com.nextcloud.utils.extensions.navigateToAllFiles
import com.nextcloud.utils.extensions.observeWorker
import com.nextcloud.utils.extensions.scaleUpAnimationFrom
import com.nextcloud.utils.extensions.setVisibleIf
import com.nextcloud.utils.fileNameValidator.FileNameValidator.checkFolderPath
import com.nextcloud.utils.view.FastScrollUtils
Expand Down Expand Up @@ -152,6 +155,7 @@ import com.owncloud.android.ui.helpers.FileOperationsHelper
import com.owncloud.android.ui.helpers.UriUploader
import com.owncloud.android.ui.interfaces.TransactionInterface
import com.owncloud.android.ui.navigation.NavigatorScreen
import com.owncloud.android.ui.navigation.animator.NavigationAnimator
import com.owncloud.android.ui.preview.PreviewImageActivity
import com.owncloud.android.ui.preview.PreviewImageFragment
import com.owncloud.android.ui.preview.PreviewTextFileFragment
Expand Down Expand Up @@ -280,6 +284,8 @@ class FileDisplayActivity :
*/
private var fileIDForImmediatePreview: Long = -1

private var isShowingDetailsFromPreview = false

private lateinit var folderRefreshScheduler: FolderRefreshScheduler

fun setFileIDForImmediatePreview(fileIDForImmediatePreview: Long) {
Expand Down Expand Up @@ -446,6 +452,10 @@ class FileDisplayActivity :
createMinFragments(savedInstanceState)
}

if (savedInstanceState == null && intent.getBooleanExtra(EXTRA_RETURN_TO_PREVIEW, false)) {
handleSpecialIntents(intent)
}

upgradeNotificationForInstantUpload()
checkOutdatedServer()
checkNotifications()
Expand Down Expand Up @@ -616,6 +626,7 @@ class FileDisplayActivity :
val file = getFileFromIntent(intent)
setFile(file)
showDetails(file)
isShowingDetailsFromPreview = intent.getBooleanExtra(EXTRA_RETURN_TO_PREVIEW, false)
}

Intent.ACTION_SEARCH == action -> handleSearchIntent(intent)
Expand Down Expand Up @@ -752,6 +763,7 @@ class FileDisplayActivity :
return
}

isShowingDetailsFromPreview = false
prepareFragmentBeforeCommit(showSortListGroup)
commitFragment(fragment)
}
Expand Down Expand Up @@ -1268,6 +1280,12 @@ class FileDisplayActivity :
after()
}

isShowingDetailsFromPreview && leftFragment is FileDetailFragment -> {
before()
NavigationAnimator(this).finishWithSlideDown()
after()
}

// pop back if current fragment is AlbumItemsFragment
isAlbumItemsFragment -> {
before()
Expand Down Expand Up @@ -2061,7 +2079,12 @@ class FileDisplayActivity :
fun canPreviewInMediaPager(file: OCFile?): Boolean =
PreviewImageFragment.canBePreviewed(file) || (file != null && MimeTypeUtil.isVideo(file))

fun previewImageWithSearchContext(file: OCFile, searchFragment: Boolean, currentSearchType: SearchType?) {
fun previewImageWithSearchContext(
file: OCFile,
searchFragment: Boolean,
currentSearchType: SearchType?,
sourceView: View?
) {
val type = if (searchFragment) {
when (currentSearchType) {
SearchType.FAVORITE_SEARCH -> VirtualFolderType.FAVORITE
Expand All @@ -2079,7 +2102,7 @@ class FileDisplayActivity :
}

val showPreview = file.isDown || MimeTypeUtil.isVideo(file)
startImagePreview(file, showPreview, type, mediaState)
startImagePreview(file, showPreview, type, mediaState, sourceView?.boundsOnScreen())
}

fun previewFile(file: OCFile, setFabVisible: CompletionCallback?) {
Expand Down Expand Up @@ -2711,23 +2734,14 @@ class FileDisplayActivity :
file: OCFile,
showPreview: Boolean,
type: VirtualFolderType? = null,
mediaState: MediaState? = null
mediaState: MediaState? = null,
sourceBounds: Rect? = null
) {
if (user.isEmpty) {
Log_OC.e(TAG, "cannot start image preview")
return
}

val intent = Intent(this, PreviewImageActivity::class.java).apply {
putExtra(EXTRA_FILE, file)
putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo)
putExtra(EXTRA_USER, user.get())
type?.let { putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, it) }
mediaState?.let { putExtra(PreviewImageActivity.EXTRA_MEDIA_STATE, it) }
}
val intent = imagePreviewIntent(file, type, mediaState) ?: return
intent.sourceBounds = sourceBounds

if (showPreview) {
startActivity(intent)
startActivity(intent, sourceBounds?.let { scaleUpAnimationFrom(it) })
} else {
val helper = FileOperationsHelper(
this,
Expand All @@ -2739,6 +2753,21 @@ class FileDisplayActivity :
}
}

private fun imagePreviewIntent(file: OCFile, type: VirtualFolderType?, mediaState: MediaState?): Intent? {
if (user.isEmpty) {
Log_OC.e(TAG, "cannot start image preview")
return null
}

return Intent(this, PreviewImageActivity::class.java).apply {
putExtra(EXTRA_FILE, file)
putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo)
putExtra(EXTRA_USER, user.get())
type?.let { putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, it) }
mediaState?.let { putExtra(PreviewImageActivity.EXTRA_MEDIA_STATE, it) }
}
}

/**
* Starts the preview of an audio [OCFile], synchronizing it first when it is not available yet.
*/
Expand Down Expand Up @@ -2966,10 +2995,11 @@ class FileDisplayActivity :
file,
true,
virtualType,
bundle.getSerializableArgument(PreviewImageActivity.EXTRA_MEDIA_STATE, MediaState::class.java)
bundle.getSerializableArgument(PreviewImageActivity.EXTRA_MEDIA_STATE, MediaState::class.java),
event.intent.sourceBounds
)
} else {
startImagePreview(file, true)
startImagePreview(file, true, sourceBounds = event.intent.sourceBounds)
}
}

Expand Down Expand Up @@ -3320,6 +3350,7 @@ class FileDisplayActivity :
private const val SEARCH_VIEW_FOCUS_DELAY = 100L

const val ACTION_DETAILS: String = "com.owncloud.android.ui.activity.action.DETAILS"
const val EXTRA_RETURN_TO_PREVIEW: String = "RETURN_TO_PREVIEW"

@JvmField
val REQUEST_CODE__SELECT_CONTENT_FROM_APPS: Int = REQUEST_CODE__LAST_SHARED + 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ class OCFileListDelegate(
file
)
} else {
ocFileListFragmentInterface.onItemClicked(file)
ocFileListFragmentInterface.onItemClicked(file, imageView)
AlbumItemsFragment.lastMediaItemPosition = galleryRowHolder.absoluteAdapterPosition
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ private Integer checkFileBeforeOpen(OCFile file) {
}
}

private void fileOnItemClick(OCFile file) {
private void fileOnItemClick(OCFile file, @Nullable View sourceView) {
Integer errorMessageId = checkFileBeforeOpen(file);
final var recyclerView = getRecyclerView();
if (recyclerView != null && errorMessageId != null) {
Expand All @@ -1196,7 +1196,7 @@ private void fileOnItemClick(OCFile file) {
}

if (mContainerActivity instanceof FileDisplayActivity fda && fda.canPreviewInMediaPager(file)) {
fda.previewImageWithSearchContext(file, searchFragment, currentSearchType);
fda.previewImageWithSearchContext(file, searchFragment, currentSearchType, sourceView);
} else if (file.isDown() && mContainerActivity instanceof FileDisplayActivity fda) {
fda.previewFile(file, this::setFabVisible);
} else {
Expand Down Expand Up @@ -1257,8 +1257,13 @@ private void handlePendingDownloadFile(OCFile file) {
}

@Override
@OptIn(markerClass = UnstableApi.class)
public void onItemClicked(OCFile file) {
onItemClicked(file, null);
}

@Override
@OptIn(markerClass = UnstableApi.class)
public void onItemClicked(OCFile file, @Nullable View sourceView) {
if (getCommonAdapter() != null && getCommonAdapter().isMultiSelect()) {
toggleItemToCheckedList(file);
} else {
Expand All @@ -1276,7 +1281,7 @@ public void onItemClicked(OCFile file) {
requireActivity().setResult(Activity.RESULT_OK, intent);
requireActivity().finish();
} else if (!mOnlyFoldersClickable) {
fileOnItemClick(file);
fileOnItemClick(file, sourceView);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public interface OCFileListFragmentInterface {

void onItemClicked(OCFile file);

default void onItemClicked(OCFile file, View sourceView) {
onItemClicked(file);
}

boolean onLongItemClicked(OCFile file);

boolean isLoading();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.ui.navigation.animator

import android.app.Activity
import android.content.Intent
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityOptionsCompat
import com.owncloud.android.R

class NavigationAnimator(private val activity: AppCompatActivity) {

fun slideUp(intent: Intent) {
val options = ActivityOptionsCompat.makeCustomAnimation(activity, R.anim.slide_up, R.anim.hold)
activity.startActivity(intent, options.toBundle())
}

fun finishWithSlideDown() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
activity.overrideActivityTransition(Activity.OVERRIDE_TRANSITION_CLOSE, R.anim.hold, R.anim.slide_down)
activity.finish()
return
}

activity.finish()
@Suppress("DEPRECATION")
activity.overridePendingTransition(R.anim.hold, R.anim.slide_down)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import com.owncloud.android.ui.dialog.SendShareDialog
import com.owncloud.android.ui.fragment.FileFragment
import com.owncloud.android.ui.fragment.GalleryFragment
import com.owncloud.android.ui.fragment.GalleryFragmentBottomSheetDialog.MediaState
import com.owncloud.android.ui.navigation.animator.NavigationAnimator
import com.owncloud.android.ui.preview.model.PreviewImageActivityState
import com.owncloud.android.utils.MimeTypeUtil
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
Expand Down Expand Up @@ -490,14 +491,14 @@ class PreviewImageActivity :

@SuppressFBWarnings("DLS")
override fun showDetails(file: OCFile) {
val intent = Intent(this, FileDisplayActivity::class.java).apply {
val detailsIntent = Intent(this, FileDisplayActivity::class.java).apply {
setAction(FileDisplayActivity.ACTION_DETAILS)
putExtra(EXTRA_FILE, file)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
putExtra(FileDisplayActivity.EXTRA_RETURN_TO_PREVIEW, true)
}

startActivity(intent)
finish()
val navigationAnimator = NavigationAnimator(this)
navigationAnimator.slideUp(detailsIntent)
}

override fun showDetails(file: OCFile, activeTab: Int) {
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/res/anim/hold.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromYDelta="0"
android:toYDelta="0" />
12 changes: 12 additions & 0 deletions app/src/main/res/anim/slide_down.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="250"
android:fromYDelta="0"
android:interpolator="@android:interpolator/fast_out_linear_in"
android:toYDelta="100%p" />
12 changes: 12 additions & 0 deletions app/src/main/res/anim/slide_up.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromYDelta="100%p"
android:interpolator="@android:interpolator/linear_out_slow_in"
android:toYDelta="0" />
Loading