Skip to content
Snippets Groups Projects
Unverified Commit 13a58b29 authored by Mohamed's avatar Mohamed Committed by GitHub
Browse files

Printing option (EXPOSUREAPP-5675) (#2574)

* Create PrintingAdapter.kt

* Update PrintingAdapter.kt

* Test printing

* Update QrCodeCreationTestViewModel.kt

* Remove Charset causing crash only older versions

* Log

* Add logs and specify media size
parent 0c46cf27
No related branches found
No related tags found
No related merge requests found
package de.rki.coronawarnapp.test.eventregistration.ui
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 timber.log.Timber
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
/**
* Printing adapter for poster PDF files
*/
class PrintingAdapter(
private val file: File
) : PrintDocumentAdapter() {
override fun onLayout(
oldAttributes: PrintAttributes?,
newAttributes: PrintAttributes?,
cancellationSignal: CancellationSignal,
callback: LayoutResultCallback,
extras: Bundle?
) {
if (cancellationSignal.isCanceled) {
callback.onLayoutCancelled()
Timber.i("onLayoutCancelled")
return
}
val info = PrintDocumentInfo.Builder(file.name)
.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
.setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
.build()
Timber.i(
"onLayoutFinished(info:%s, oldAttributes:%s, newAttributes:%s)",
info,
oldAttributes,
newAttributes
)
callback.onLayoutFinished(info, oldAttributes != newAttributes)
}
override fun onWrite(
pages: Array<out PageRange>,
destination: ParcelFileDescriptor,
cancellationSignal: CancellationSignal,
callback: WriteResultCallback
) = try {
FileInputStream(file).use { input ->
FileOutputStream(destination.fileDescriptor).use { output ->
val bytesCopied = input.copyTo(output)
Timber.i("bytesCopied:$bytesCopied")
}
}
when {
cancellationSignal.isCanceled -> {
Timber.i("onWriteCancelled")
callback.onWriteCancelled()
}
else -> {
Timber.i("onWriteFinished")
callback.onWriteFinished(arrayOf(PageRange.ALL_PAGES))
}
}
} catch (e: Exception) {
callback.onWriteFailed(e.message)
Timber.e(e, "Printing $file failed")
}
}
...@@ -2,17 +2,22 @@ package de.rki.coronawarnapp.test.eventregistration.ui.qrcode ...@@ -2,17 +2,22 @@ package de.rki.coronawarnapp.test.eventregistration.ui.qrcode
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.print.PrintAttributes
import android.print.PrintManager
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.core.content.getSystemService
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import de.rki.coronawarnapp.R import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.databinding.FragmentTestQrcodeCreationBinding import de.rki.coronawarnapp.databinding.FragmentTestQrcodeCreationBinding
import de.rki.coronawarnapp.test.eventregistration.ui.PrintingAdapter
import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.di.AutoInject
import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.observe2
import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.ui.viewBindingLazy
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
import de.rki.coronawarnapp.util.viewmodel.cwaViewModels import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class QrCodeCreationTestFragment : Fragment(R.layout.fragment_test_qrcode_creation), AutoInject { class QrCodeCreationTestFragment : Fragment(R.layout.fragment_test_qrcode_creation), AutoInject {
...@@ -25,13 +30,42 @@ class QrCodeCreationTestFragment : Fragment(R.layout.fragment_test_qrcode_creati ...@@ -25,13 +30,42 @@ class QrCodeCreationTestFragment : Fragment(R.layout.fragment_test_qrcode_creati
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.sharingIntent.observe2(this) { viewModel.sharingIntent.observe2(this) { fileIntent ->
startActivity(it.get(requireActivity()))
binding.printPDF.isVisible = true
binding.printPDF.setOnClickListener {
// Context must be an Activity context
val printingManger = context?.getSystemService<PrintManager>()
Timber.i("PrintingManager: $printingManger")
printingManger?.apply {
val printingJob = print(
"CoronaWarnApp",
PrintingAdapter(fileIntent.file),
PrintAttributes
.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A3)
.build()
)
Timber.i("PrintingJob:$printingJob")
Timber.i("PrintingJob isBlocked:${printingJob.isBlocked}")
Timber.i("PrintingJob isCancelled:${printingJob.isCancelled}")
Timber.i("PrintingJob isCompleted:${printingJob.isCompleted}")
Timber.i("PrintingJob isFailed:${printingJob.isFailed}")
Timber.i("PrintingJob info:${printingJob.info}")
}
}
binding.sharePDF.isVisible = true
binding.sharePDF.setOnClickListener {
startActivity(fileIntent.intent(requireActivity()))
}
} }
viewModel.qrCodeBitmap.observe2(this) { viewModel.qrCodeBitmap.observe2(this) {
binding.qrCodeImage.setImageBitmap(it) binding.qrCodeImage.setImageBitmap(it)
binding.printPDF.isVisible = it != null if (it != null) {
viewModel.createPDF(binding.pdfPage)
}
} }
viewModel.errorMessage.observe2(this) { viewModel.errorMessage.observe2(this) {
...@@ -46,9 +80,5 @@ class QrCodeCreationTestFragment : Fragment(R.layout.fragment_test_qrcode_creati ...@@ -46,9 +80,5 @@ class QrCodeCreationTestFragment : Fragment(R.layout.fragment_test_qrcode_creati
binding.generateQrCode.setOnClickListener { binding.generateQrCode.setOnClickListener {
viewModel.createQrCode(binding.qrCodeText.text.toString()) viewModel.createQrCode(binding.qrCodeText.text.toString())
} }
binding.printPDF.setOnClickListener {
viewModel.createPDF(binding.pdfPage)
}
} }
} }
...@@ -31,7 +31,7 @@ class QrCodeCreationTestViewModel @AssistedInject constructor( ...@@ -31,7 +31,7 @@ class QrCodeCreationTestViewModel @AssistedInject constructor(
val qrCodeBitmap = SingleLiveEvent<Bitmap>() val qrCodeBitmap = SingleLiveEvent<Bitmap>()
val errorMessage = SingleLiveEvent<String>() val errorMessage = SingleLiveEvent<String>()
val sharingIntent = SingleLiveEvent<FileSharing.ShareIntentProvider>() val sharingIntent = SingleLiveEvent<FileSharing.FileIntentProvider>()
/** /**
* Creates a QR Code [Bitmap] ,result is delivered by [qrCodeBitmap] * Creates a QR Code [Bitmap] ,result is delivered by [qrCodeBitmap]
...@@ -68,7 +68,7 @@ class QrCodeCreationTestViewModel @AssistedInject constructor( ...@@ -68,7 +68,7 @@ class QrCodeCreationTestViewModel @AssistedInject constructor(
} }
sharingIntent.postValue( sharingIntent.postValue(
fileSharing.getIntentProvider(file, "Scan and Help") fileSharing.getFileIntentProvider(file, "Scan and Help")
) )
} catch (e: Exception) { } catch (e: Exception) {
errorMessage.postValue(e.localizedMessage ?: "Creating pdf failed") errorMessage.postValue(e.localizedMessage ?: "Creating pdf failed")
...@@ -85,8 +85,10 @@ class QrCodeCreationTestViewModel @AssistedInject constructor( ...@@ -85,8 +85,10 @@ class QrCodeCreationTestViewModel @AssistedInject constructor(
private fun encodeAsBitmap(input: String, size: Int = 1000): Bitmap? { private fun encodeAsBitmap(input: String, size: Int = 1000): Bitmap? {
return try { return try {
val hints = mapOf( val hints = mapOf(
EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.H, EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.H
EncodeHintType.CHARACTER_SET to Charsets.UTF_8 // This is not required in the specs and it should not be enabled
// it is causing crash on older Android versions ex:API 23
// EncodeHintType.CHARACTER_SET to Charsets.UTF_8
) )
MultiFormatWriter().encode( MultiFormatWriter().encode(
input, input,
......
...@@ -17,21 +17,33 @@ ...@@ -17,21 +17,33 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:text="QR Code" android:text="QR Code"
app:layout_constraintEnd_toStartOf="@+id/printPDF" app:layout_constraintEnd_toStartOf="@+id/sharePDF"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/sharePDF"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="Share PDF"
android:visibility="invisible"
app:layout_constraintEnd_toStartOf="@+id/printPDF"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/generateQrCode"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/printPDF" android:id="@+id/printPDF"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:text="PDF" android:text="Print PDF"
android:visibility="gone" android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/generateQrCode" app:layout_constraintStart_toEndOf="@+id/sharePDF"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
......
...@@ -50,6 +50,38 @@ class FileSharing @Inject constructor( ...@@ -50,6 +50,38 @@ class FileSharing @Inject constructor(
} }
} }
fun getFileIntentProvider(
path: File,
title: String,
@StringRes chooserTitle: Int? = null
): FileIntentProvider = object : FileIntentProvider {
override fun intent(activity: Activity): Intent {
val builder = ShareCompat.IntentBuilder.from(activity).apply {
setType(path.determineMimeType())
setStream(getFileUri(path))
setSubject(title)
chooserTitle?.let { setChooserTitle(it) }
}
val intent = if (chooserTitle != null) {
builder.createChooserIntent()
} else {
builder.intent
}
return intent.apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
Timber.tag(TAG).d("Intent created %s", this)
}
}
override val file: File = path
}
interface FileIntentProvider {
fun intent(activity: Activity): Intent
val file: File
}
interface ShareIntentProvider { interface ShareIntentProvider {
fun get(activity: Activity): Intent fun get(activity: Activity): Intent
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment