Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature to open an external PDF with kDrive using kDrive PDF viewer #1179

Merged
merged 68 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
6eb83f4
Add feature to open an external PDF with kDrive
tevincent Jan 31, 2024
5172c2f
Add empty permission to avoid sonar error. We don't need to have spec…
tevincent Jan 31, 2024
1a4e566
Remove strings
tevincent Jan 31, 2024
79fdfad
Clean code
tevincent Feb 1, 2024
fbcf215
Add share to kdrive and improve UI
tevincent Feb 21, 2024
8c899fd
Remove unused extension
tevincent Feb 21, 2024
9e1680b
Add bottomsheet to print, share, add to kDrive and open with
tevincent Feb 26, 2024
675111a
Fix conflict
tevincent Feb 26, 2024
aff67aa
Remove unused import
tevincent Feb 26, 2024
455483b
Extract PDFDOcumentAdapter to its own file
tevincent Feb 27, 2024
5192a85
Add strings
tevincent Feb 27, 2024
115ec44
Remove unused variable
tevincent Feb 27, 2024
51c0f02
Use MenuItemView
tevincent Feb 27, 2024
254aab5
Clean PreviewPDFActivity
tevincent Feb 27, 2024
eff8260
Reformat PreviewSliderFragment
tevincent Feb 27, 2024
2edf013
Code cleaning
tevincent Feb 27, 2024
0507d3b
Extract sendCopy action to method
tevincent Feb 27, 2024
3901436
Remove unused drawable
tevincent Feb 27, 2024
c0060cb
Remove ConstraintLayout
tevincent Feb 27, 2024
9a0530c
Remove unused parameter
tevincent Feb 27, 2024
5023709
Remove share string and use "Send a copy" string instead
tevincent Feb 29, 2024
e6cde4a
Exclude kDrive in openWith with the PDF reader to avoid a loop
tevincent Mar 14, 2024
f5fc7b3
Hide back button and bottomsheet when we click on the pdf
tevincent Mar 15, 2024
8c5c30a
Add uppercase to string
tevincent Mar 15, 2024
499e70c
Change header
tevincent Mar 15, 2024
9ca8489
Avoid having kdrive as a possible aplication when we try to "openWith…
tevincent Mar 15, 2024
9ef8c4c
Clean code
tevincent Mar 15, 2024
32174b0
Add extension to handle bottomsheet configuration
tevincent Mar 18, 2024
4c32d83
Readd missing strings
tevincent Mar 18, 2024
0908807
Fix crash
tevincent Mar 19, 2024
fa1d21a
Fix statusbar behavior in default PDF reader view
tevincent Mar 19, 2024
5fe8afe
Remove unused import
tevincent Mar 19, 2024
4a24d0d
Code review
tevincent Mar 20, 2024
e77a625
Add openWith button and page number at the top of the default reader …
tevincent Mar 21, 2024
ec611dc
Improve the way we init the ConstraintSet
tevincent Mar 21, 2024
22255b1
Format xml
tevincent Mar 21, 2024
653ffae
Create PreviewHeaderView to centralize code between default reader an…
tevincent Mar 25, 2024
04f5afb
Fix default peek height
tevincent Mar 25, 2024
393c1bc
Remove permission tag and add a comment to explain why we don't have …
tevincent Mar 27, 2024
4e809c5
Modify AndroidManifest
tevincent Mar 27, 2024
6b5d690
Code review
tevincent Mar 27, 2024
c82dca7
Remove unused import
tevincent Apr 2, 2024
ccf06de
Code review
tevincent Apr 3, 2024
5752ba5
Fix bottomsheet click through
tevincent Apr 3, 2024
3d9c6c8
Use the same interface for click listener
tevincent Apr 3, 2024
aa205b8
Remove unused import
tevincent Apr 3, 2024
afe779d
Fix conflict
tevincent Apr 4, 2024
e93b81f
Reformat file
tevincent Apr 4, 2024
f58ced6
Code review
tevincent Apr 4, 2024
8f316cf
Code review
tevincent Apr 8, 2024
a0bc07e
Update Core
tevincent Apr 8, 2024
3e23e13
Use a by lazy to initialize context
tevincent Apr 8, 2024
a2db03e
Rename openWith to tell that with filter out kDrive
tevincent Apr 8, 2024
d13102f
Remove unused import
tevincent Apr 8, 2024
8eee22d
Add copyright to print icon
tevincent Apr 8, 2024
f9d4b7b
Fix context
tevincent Apr 8, 2024
09ceae3
Update comment for animation in PreviewHeaderView
tevincent Apr 8, 2024
066e144
Remove unused variable
tevincent Apr 8, 2024
68b54ff
Add comment for the bug where the BottomSheet can be a TopSheet
tevincent Apr 9, 2024
a13d10a
Update comment
tevincent Apr 9, 2024
052c842
Format string in PreviewHeaderView
tevincent Apr 9, 2024
e35df3e
Extract method outside function because RequiresApi annotation seems …
tevincent Apr 9, 2024
e4e0a5c
Add CallSuper for print
tevincent Apr 9, 2024
a1f5c28
Remove Unit
tevincent Apr 9, 2024
d8a521e
Update comment
tevincent Apr 9, 2024
d2adbf8
Add a more explicit Sentry
tevincent Apr 9, 2024
973d2c3
Clean print svg
tevincent Apr 10, 2024
bf67be1
Update comment with TODO
tevincent Apr 10, 2024
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
55 changes: 55 additions & 0 deletions .idea/navEditor.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,26 @@

<activity android:name=".ui.OnlyOfficeActivity" />

<!-- This Activity is meant to be opened by everyone and don't need any permission protection-->
<activity
android:name=".ui.fileList.preview.PreviewPDFActivity"
android:exported="true"
android:label="@string/kDrivePdfViewer">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="file" />
<data android:scheme="content" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="*" />
<data android:mimeType="application/pdf" />
</intent-filter>
</activity>

<activity
android:name=".ui.SaveExternalFilesActivity"
android:configChanges="orientation|screenSize|layoutDirection"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import com.infomaniak.drive.ui.fileList.fileDetails.SelectCategoriesFragment
import com.infomaniak.drive.ui.fileList.fileDetails.SelectCategoriesFragmentArgs
import com.infomaniak.drive.utils.*
import com.infomaniak.drive.utils.Utils.openWith
import com.infomaniak.drive.utils.Utils.openWithIntent
import com.infomaniak.drive.views.FileInfoActionsView
import com.infomaniak.lib.core.models.ApiResponse
import com.infomaniak.lib.core.utils.*
Expand All @@ -63,8 +62,9 @@ class FileInfoActionsBottomSheetDialog : BottomSheetDialogFragment(), FileInfoAc
private val mainViewModel: MainViewModel by activityViewModels()
private val navigationArgs: FileInfoActionsBottomSheetDialogArgs by navArgs()

override lateinit var currentFile: File
override val ownerFragment = this
override val currentContext by lazy { requireContext() }
override lateinit var currentFile: File

private val selectFolderResultLauncher = registerForActivityResult(StartActivityForResult()) {
it.whenResultIsOk { data -> onSelectFolderResult(data) }
Expand Down Expand Up @@ -317,17 +317,13 @@ class FileInfoActionsBottomSheetDialog : BottomSheetDialogFragment(), FileInfoAc
}
}

override fun openWithClicked() {
super.openWithClicked()
if (requireContext().openWithIntent(currentFile).resolveActivity(requireContext().packageManager) == null) {
showSnackbar(R.string.errorNoSupportingAppFound, showAboveFab = true)
findNavController().popBackStack()
} else {
override fun openWith() {
context?.openWith(ownerFragment = ownerFragment, currentFile = currentFile) {
safeNavigate(
FileInfoActionsBottomSheetDialogDirections.actionFileInfoActionsBottomSheetDialogToDownloadProgressDialog(
fileId = currentFile.id,
fileName = currentFile.name,
userDrive = navigationArgs.userDrive
userDrive = navigationArgs.userDrive,
)
)
}
Expand Down Expand Up @@ -387,6 +383,9 @@ class FileInfoActionsBottomSheetDialog : BottomSheetDialogFragment(), FileInfoAc
setBackNavigationResult(CANCELLABLE_MAIN_KEY, bundle)
}

override fun shareFile() = Unit
override fun saveToKDrive() = Unit

companion object {

fun Fragment.openManageCategoriesBottomSheetDialog(filesIds: IntArray, userDrive: UserDrive? = null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.ui.fileList.preview

import android.os.Bundle
import android.os.CancellationSignal
import android.os.ParcelFileDescriptor
import android.print.PageRange
import android.print.PrintAttributes
import android.print.PrintDocumentAdapter
import android.print.PrintDocumentInfo
import com.infomaniak.drive.utils.IOFile
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream

class PDFDocumentAdapter(
private val fileName: String,
private val file: IOFile,
) : PrintDocumentAdapter() {

override fun onLayout(
oldAttributes: PrintAttributes?,
newAttributes: PrintAttributes?,
cancellationSignal: CancellationSignal,
callback: LayoutResultCallback,
extras: Bundle?
) {
if (cancellationSignal.isCanceled) {
callback.onLayoutCancelled()
return
}

val info = PrintDocumentInfo.Builder(fileName)
.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
.setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
.build()

callback.onLayoutFinished(info, oldAttributes != newAttributes)
}

override fun onWrite(
pages: Array<out PageRange>,
destination: ParcelFileDescriptor,
cancellationSignal: CancellationSignal,
callback: WriteResultCallback
) {
var inputStream: InputStream? = null
var outputStream: OutputStream? = null

try {
inputStream = FileInputStream(file)
outputStream = FileOutputStream(destination.fileDescriptor)

inputStream.copyTo(outputStream)

if (cancellationSignal.isCanceled) {
callback.onWriteCancelled()
} else {
callback.onWriteFinished(arrayOf(PageRange.ALL_PAGES))
}
} catch (ex: Exception) {
callback.onWriteFailed(ex.message)
} finally {
inputStream?.close()
outputStream?.close()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.ui.fileList.preview

import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.print.PrintAttributes
import android.print.PrintManager
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.infomaniak.drive.R
import com.infomaniak.drive.data.models.ExtensionType
import com.infomaniak.drive.data.models.File
import com.infomaniak.drive.databinding.ActivityPreviewPdfBinding
import com.infomaniak.drive.utils.*
import com.infomaniak.drive.utils.SyncUtils.uploadFolder
import com.infomaniak.drive.utils.Utils.ROOT_ID
import com.infomaniak.drive.views.FileInfoActionsView.OnItemClickListener
import com.infomaniak.lib.core.utils.SnackbarUtils.showSnackbar
import com.infomaniak.lib.core.utils.getFileNameAndSize
import io.sentry.Sentry
import io.sentry.SentryLevel

class PreviewPDFActivity : AppCompatActivity(), OnItemClickListener {

val binding: ActivityPreviewPdfBinding by lazy { ActivityPreviewPdfBinding.inflate(layoutInflater) }

override val ownerFragment = null
override val currentContext by lazy { this }
override val currentFile = null

private val navController by lazy { setupNavController() }
private val navHostFragment by lazy { supportFragmentManager.findFragmentById(R.id.hostFragment) as NavHostFragment }

private val externalFileUri by lazy { Uri.parse(intent.dataString) }
private val fileNameAndSize: Pair<String, Long>? by lazy { getFileNameAndSize(externalFileUri) }
private val fileName: String by lazy { fileNameAndSize?.first ?: "" }
private val fileSize: Long by lazy { fileNameAndSize?.second ?: 0 }

private val bottomSheetBehavior: BottomSheetBehavior<View>
get() = BottomSheetBehavior.from(binding.bottomSheetFileInfos)

private var isOverlayShown = true

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

with(binding) {
setContentView(root)

navController.navigate(R.id.previewPDFFragment)

header.setup(
onBackClicked = { finish() },
onOpenWithClicked = { openWith(externalFileUri = externalFileUri) },
)
}

initBottomSheet()
}

private fun initBottomSheet() = with(binding) {
setupBottomSheetFileBehavior(bottomSheetBehavior, isDraggable = true, isFitToContents = true)
bottomSheetFileInfos.updateWithExternalFile(getFakeFile())
bottomSheetFileInfos.initOnClickListener(this@PreviewPDFActivity)
}

override fun onStart() {
super.onStart()
binding.header.setupWindowInsetsListener(rootView = binding.root, bottomSheetView = binding.bottomSheetFileInfos)
setupStatusBarForPreview()
}

override fun shareFile() {
shareFile { externalFileUri }
}

override fun saveToKDrive() {
saveToKDrive(externalFileUri)
}

override fun openWith() {
openWith(externalFileUri = externalFileUri)
}

override fun printClicked() {
super.printClicked()
getFileForPrint(externalFileUri)?.let { fileToPrint ->
val printManager = getSystemService(Context.PRINT_SERVICE) as PrintManager
val printAdapter = PDFDocumentAdapter(fileName, fileToPrint)
printManager.print(fileName, printAdapter, PrintAttributes.Builder().build())
}
}

private fun getFileForPrint(uri: Uri): IOFile? {
return runCatching {
IOFile(uploadFolder, uri.hashCode().toString()).apply {
if (exists()) delete()
createNewFile()
contentResolver?.openInputStream(uri)?.use { inputStream ->
outputStream().use { inputStream.copyTo(it) }
}
}
}.onFailure {
showSnackbar(R.string.errorFileNotFound)
Sentry.withScope { scope ->
scope.level = SentryLevel.ERROR
scope.setExtra("exception", it.stackTraceToString())
Sentry.captureMessage("Exception while printing a PDF")
}
}.getOrNull()
}

fun toggleFullscreen() {
isOverlayShown = !isOverlayShown
binding.header.toggleVisibility(isOverlayShown)
bottomSheetBehavior.state = if (isOverlayShown) BottomSheetBehavior.STATE_COLLAPSED else BottomSheetBehavior.STATE_HIDDEN
toggleSystemBar(isOverlayShown)
}

// This is necessary to be able to use the same view details we have in kDrive (file name, file type and size)
private fun getFakeFile(): File {
return File(
name = fileName,
size = fileSize,
id = ROOT_ID,
extensionType = ExtensionType.PDF.value,
type = "",
)
}

private fun setupNavController(): NavController {
return navHostFragment.navController.apply {
setGraph(R.navigation.view_pdf, PreviewPDFFragmentArgs(fileUri = externalFileUri).toBundle())
}
}

override fun displayInfoClicked() = Unit
override fun fileRightsClicked() = Unit
override fun goToFolder() = Unit
override fun manageCategoriesClicked(fileId: Int) = Unit
override fun onCacheAddedToOffline() = Unit
override fun onDeleteFile(onApiResponse: () -> Unit) = Unit
override fun onDuplicateFile(result: String, onApiResponse: () -> Unit) = Unit
override fun onLeaveShare(onApiResponse: () -> Unit) = Unit
override fun onMoveFile(destinationFolder: File) = Unit
override fun onRenameFile(newName: String, onApiResponse: () -> Unit) = Unit
override fun removeOfflineFile(offlineLocalPath: IOFile, cacheFile: IOFile) = Unit
}
Loading
Loading