Переглянути джерело

Merge branch 'export-pdf-dialog' into share-cover-dialog

Wayne 6 роки тому
батько
коміт
2ae0e71c0a

+ 3 - 0
build.gradle

@@ -48,6 +48,9 @@ dependencies {
     implementation project(':Bomo_for_Android_cloudModule')
     implementation project(':Bomo_for_Android_encodeModule')
     implementation project(':uikit')
+    implementation project(':pdfexport')
+
+    implementation 'com.squareup.picasso:picasso:2.71828'
 }
 repositories {
     mavenCentral()

+ 3 - 0
src/main/java/com/bomostory/sceneeditmodule/Config.kt

@@ -4,10 +4,13 @@ import android.os.Environment
 import java.io.File
 
 object Config {
+    private val ROOT = File(Environment.getExternalStorageDirectory(), "Bomo")
     private const val ASSETS_FOLDER_PATH = "/Android/yuheng"
     private const val PROJECTS_FOLDER_PATH = "/Android/yuheng/project"
     private const val IMAGE_FOLDER_PATH = "/Android/yuheng/image"
     val IMAGE_FOLDER = File(Environment.getExternalStorageDirectory(), IMAGE_FOLDER_PATH)
     val ASSETS_FOLDER = File(Environment.getExternalStorageDirectory(), ASSETS_FOLDER_PATH)
     val PROJECTS_FOLDER = File(Environment.getExternalStorageDirectory(), PROJECTS_FOLDER_PATH)
+
+    val PDF_FOLDER = File(ROOT, "pdf")
 }

+ 84 - 2
src/main/java/com/bomostory/sceneeditmodule/MovieEditActivity.kt

@@ -19,6 +19,7 @@ import com.bomostory.sceneeditmodule.basicdata.DataParser
 import com.bomostory.sceneeditmodule.basicdata.Music
 import com.bomostory.sceneeditmodule.basicdata.Project
 import com.bomostory.sceneeditmodule.basicdata.Scene
+import com.bomostory.sceneeditmodule.share.ExportPdfDialog
 import com.bomostory.sceneeditmodule.share.ShareDialog
 import com.bomostory.sceneeditmodule.utils.MoviePlayer
 import com.bomostory.sceneeditmodule.utils.MusicPlayer
@@ -27,6 +28,7 @@ import com.bomostory.sceneeditmodule.view.MovieSelectView
 import com.example.exportmedia.MediaHelper
 import com.example.tfat.myapplication.R
 import com.google.gson.Gson
+import io.reactivex.Observable
 import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.schedulers.Schedulers
 import kotlinx.android.synthetic.main.activity_movie_edit.*
@@ -272,8 +274,45 @@ class MovieEditActivity : AppCompatActivity(),
             name = project?.name ?: ""
             author = project?.author ?: ""
             onClickMore = Runnable { saveMovie() }
-        }
-                .show(supportFragmentManager)
+            onClickExportPdf = Runnable { onClickExportPdf() }
+        }.show(supportFragmentManager)
+    }
+
+    private fun onClickExportPdf() {
+        ExportPdfDialog().also { exportPdfDialog ->
+            exportPdfDialog.image1Path = project?.scene1File?.path ?: ""
+            exportPdfDialog.image2Path = project?.scene2File?.path ?: ""
+            exportPdfDialog.onClickPrint = Runnable {
+                when (exportPdfDialog.type) {
+                    ExportPdfDialog.Type.Standard -> {
+                        val name = "standard_${project?.name}_${System.currentTimeMillis()}.pdf"
+                        if (!Config.PDF_FOLDER.exists()) {
+                            Config.PDF_FOLDER.mkdirs()
+                        }
+                        val file = File(Config.PDF_FOLDER, name)
+                        file.createNewFile()
+                        val observable = PdfMaker.makeStandard(this@MovieEditActivity, project!!, file)
+                        Pair(file, observable)
+                    }
+                    ExportPdfDialog.Type.Booklet -> {
+                        val name = "booklet_${project?.name}_${System.currentTimeMillis()}.pdf"
+                        if (!Config.PDF_FOLDER.exists()) {
+                            Config.PDF_FOLDER.mkdirs()
+                        }
+                        val file = File(Config.PDF_FOLDER, name)
+                        file.createNewFile()
+                        val observable = PdfMaker.makeBooklet(this@MovieEditActivity, project!!, file)
+                        Pair(file, observable)
+                    }
+                }.apply {
+                    exportPdfFile(second, Runnable {
+                        exportPdfDialog.dismiss()
+                    }, Runnable {
+                        showPdfExportCompleteDialog(first)
+                    })
+                }
+            }
+        }.show(supportFragmentManager)
     }
 
     private fun saveMovie() {
@@ -334,4 +373,47 @@ class MovieEditActivity : AppCompatActivity(),
                 })
     }
 
+    private fun exportPdfFile(observable: Observable<Float>, onFinally: Runnable, onComplete: Runnable) {
+        val pd = ProgressDialog(this@MovieEditActivity).apply {
+            setCancelable(false)
+        }
+        observable.subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .doOnSubscribe { pd.show() }
+                .doFinally {
+                    pd.dismiss()
+                    onFinally.run()
+                }
+                .subscribe({
+                    val progress = (it * 100).toInt()
+                    val msg = if (progress >= 100) {
+                        "processing ..."
+                    } else {
+                        "$progress%"
+                    }
+                    pd.setMessage(msg)
+                }, {
+                }, {
+                    onComplete.run()
+                })
+    }
+
+    private fun showPdfExportCompleteDialog(file: File) {
+        AlertDialog.Builder(this@MovieEditActivity)
+                .setMessage("Complete")
+                .setPositiveButton("View") { _, _ ->
+                    val uri = Uri.parse(file.absolutePath)
+                    val intent = Intent(Intent.ACTION_VIEW, uri).apply {
+                        setDataAndType(uri, "application/pdf")
+                    }
+                    if (intent.resolveActivity(packageManager) != null) {
+                        Intent.createChooser(intent, "").apply {
+                            startActivity(this)
+                        }
+                    } else {
+                        Toast.makeText(this@MovieEditActivity, "No app can view pdf", Toast.LENGTH_SHORT).show()
+                    }
+                }.show()
+    }
+
 }

+ 103 - 0
src/main/java/com/bomostory/sceneeditmodule/PdfMaker.kt

@@ -0,0 +1,103 @@
+package com.bomostory.sceneeditmodule
+
+import android.content.Context
+import android.graphics.*
+import com.bomostory.pdfexport.PdfBookWriter
+import com.bomostory.pdfexport.StandardPdfWriter
+import com.bomostory.sceneeditmodule.basicdata.Project
+import com.example.tfat.myapplication.R
+import io.reactivex.Observable
+import java.io.File
+
+object PdfMaker {
+    fun makeStandard(context: Context, project: Project, file: File): Observable<Float> {
+        return Observable.create { emitter ->
+            val total = (2 + project.story?.scenes?.size!!).toFloat()
+            var progress = 0
+            val width = 1920
+            val height = 1080
+            val coverWidth = width / 2
+            emitter.onNext(progress++ / total)
+            val front = drawFrontCover(coverWidth, height, project.name ?: "")
+            emitter.onNext(progress++ / total)
+            val back = drawBackCover(context, coverWidth, height)
+            emitter.onNext(progress++ / total)
+            val standardPdfWriter = StandardPdfWriter(file, front, back)
+            project.story?.scenes?.forEach {
+                val bitmap = SceneDrawer.drawScene(it, 0, width, height) ?: return@forEach
+                standardPdfWriter.addImage(bitmap)
+                emitter.onNext(progress++ / total)
+            }
+            standardPdfWriter.finish()
+            emitter.onComplete()
+        }
+    }
+
+    fun makeBooklet(context: Context, project: Project, file: File): Observable<Float> {
+        return Observable.create { emitter ->
+            val total = (2 + project.story?.scenes?.size!!).toFloat()
+            var progress = 0
+            val width = 1920 / 2
+            val height = 1080
+            val name = project?.name ?: ""
+            val author = project?.author ?: ""
+            emitter.onNext(progress++ / total)
+            /** front **/
+            val front = drawFrontCover(width, height, name)
+            emitter.onNext(progress++ / total)
+            /** back **/
+            val back = drawBackCover(context, width, height)
+            emitter.onNext(progress++ / total)
+            /** pages **/
+            val pdfBookWriter = PdfBookWriter(file, front, back)
+            project.story?.scenes?.forEach {
+                val bitmap = SceneDrawer.drawScene(it, 0, width, height) ?: return@forEach
+                pdfBookWriter.addImage(bitmap)
+                emitter.onNext(progress++ / total)
+            }
+            pdfBookWriter.finish()
+            emitter.onComplete()
+        }
+    }
+
+    private fun drawFrontCover(width: Int, height: Int, name: String): Bitmap {
+        val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+            color = Color.WHITE
+            textSize = 100f
+            textAlign = Paint.Align.CENTER
+            flags += Paint.FAKE_BOLD_TEXT_FLAG
+        }
+        return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444).apply {
+            Canvas(this).apply {
+                drawColor(Color.parseColor("#faa600"))
+                val marginBottom = height / 20
+                val rect = Rect(0, height - height / 3 - marginBottom, width, height - marginBottom)
+                val p = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+                    color = Color.parseColor("#66ffffff")
+                    style = Paint.Style.FILL_AND_STROKE
+                }
+                drawRect(rect, p)
+                drawText(name, rect.centerX().toFloat(), rect.centerY().toFloat(), paint)
+            }
+        }
+    }
+
+    private fun drawBackCover(context: Context, width: Int, height: Int): Bitmap {
+        return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444).apply {
+            Canvas(this).apply {
+                drawColor(Color.parseColor("#faa600"))
+                val drawable = context.getDrawable(R.drawable.ic_logo_full_white)
+                val marginBottom = this.height / 10
+                val w = this.width / 3
+                val scale = w.toFloat() / drawable.intrinsicWidth.toFloat()
+                val h = drawable.intrinsicHeight * scale
+                val left = (this.width - w) / 2
+                val top = (this.height - h) - marginBottom
+                val right = (this.width + w) / 2
+                val bottom = (this.height) - marginBottom
+                drawable.setBounds(left, top.toInt(), right, bottom)
+                drawable.draw(this)
+            }
+        }
+    }
+}

+ 35 - 0
src/main/java/com/bomostory/sceneeditmodule/SceneDrawer.kt

@@ -0,0 +1,35 @@
+package com.bomostory.sceneeditmodule
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.Paint
+import com.bomostory.sceneeditmodule.basicdata.Scene
+import kotlin.math.pow
+
+object SceneDrawer {
+    fun drawScene(scene: Scene, trackX: Int, scaleWidth: Int, scaleHeight: Int): Bitmap? {
+        var sceneBitmap: Bitmap? = null
+
+        scene?.apply {
+            sceneBitmap = BitmapFactory.decodeFile(backgroundPath)
+            sceneBitmap = Bitmap.createScaledBitmap(sceneBitmap, scaleWidth, scaleHeight, true)
+
+            val canvas = Canvas(sceneBitmap)
+            for (layer in layers) {
+                for (actor in layer.actors) {
+                    var actorBitmap = BitmapFactory.decodeFile(actor.resourcePath)
+                    actorBitmap = Bitmap.createScaledBitmap(actorBitmap, actor.sideLength, actor.sideLength, true)
+
+                    canvas.save()
+                    canvas.translate(trackX / 2f.pow(layers.indexOf(layer)), 0f)
+                    canvas.drawBitmap(actorBitmap, actor.positionX.toFloat(), actor.positionY.toFloat(), Paint())
+                    canvas.restore()
+
+                    actorBitmap.recycle()
+                }
+            }
+        }
+        return sceneBitmap
+    }
+}

+ 3 - 31
src/main/java/com/bomostory/sceneeditmodule/SuperMovieMaker.kt

@@ -1,9 +1,6 @@
 package com.bomostory.sceneeditmodule
 
 import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.graphics.Canvas
-import android.graphics.Paint
 import android.util.Log
 import com.bomostory.sceneeditmodule.MovieEditActivity.Companion.FPS
 import com.bomostory.sceneeditmodule.basicdata.Music
@@ -21,7 +18,6 @@ import java.util.concurrent.Semaphore
 import kotlin.collections.ArrayList
 import kotlin.collections.LinkedHashMap
 import kotlin.collections.set
-import kotlin.math.pow
 
 class SuperMovieMaker {
 
@@ -53,7 +49,7 @@ class SuperMovieMaker {
                 Completable.create {
                     project?.story?.apply {
                         val scene = scenes[frameData.sceneIndex]
-                        val bitmap = drawScene(scene, frameData.x, scaleWidth, scaleHeight)
+                        val bitmap = SceneDrawer.drawScene(scene, frameData.x, scaleWidth, scaleHeight)
                         movieBuilder.addImage(i, bitmap!!).blockingSubscribe()
                     }
                     it.onComplete()
@@ -128,7 +124,7 @@ class SuperMovieMaker {
         return Observable.create<AbstractMap.SimpleEntry<Int, Bitmap>> {
             scene?.apply {
                 record?.apply {
-                    var bitmap = drawScene(scene, 0, scaleWidth, scaleHeight)
+                    var bitmap = SceneDrawer.drawScene(scene, 0, scaleWidth, scaleHeight)
                     var bitmapIndex = 0
                     var trackPosition = -1
 
@@ -138,7 +134,7 @@ class SuperMovieMaker {
                                 preBitmap = bitmap
                                 preBitmap?.recycle()
 
-                                bitmap = drawScene(scene, track.positionX, scaleWidth, scaleHeight)
+                                bitmap = SceneDrawer.drawScene(scene, track.positionX, scaleWidth, scaleHeight)
                                 trackPosition = tracks.indexOf(track)
                             }
                         }
@@ -153,30 +149,6 @@ class SuperMovieMaker {
         }
     }
 
-    private fun drawScene(scene: Scene, trackX: Int, scaleWidth: Int, scaleHeight: Int): Bitmap? {
-        var sceneBitmap: Bitmap? = null
-
-        scene?.apply {
-            sceneBitmap = BitmapFactory.decodeFile(backgroundPath)
-            sceneBitmap = Bitmap.createScaledBitmap(sceneBitmap, scaleWidth, scaleHeight, true)
-
-            val canvas = Canvas(sceneBitmap)
-            for (layer in layers) {
-                for (actor in layer.actors) {
-                    var actorBitmap = BitmapFactory.decodeFile(actor.resourcePath)
-                    actorBitmap = Bitmap.createScaledBitmap(actorBitmap, actor.sideLength, actor.sideLength, true)
-
-                    canvas.save()
-                    canvas.translate(trackX / 2f.pow(layers.indexOf(layer)), 0f)
-                    canvas.drawBitmap(actorBitmap, actor.positionX.toFloat(), actor.positionY.toFloat(), Paint())
-                    canvas.restore()
-
-                    actorBitmap.recycle()
-                }
-            }
-        }
-        return sceneBitmap
-    }
 
     data class FrameData(val sceneIndex: Int, val x: Int, var repeat: Int)
 }

+ 65 - 0
src/main/java/com/bomostory/sceneeditmodule/share/ExportPdfDialog.kt

@@ -0,0 +1,65 @@
+package com.bomostory.sceneeditmodule.share
+
+import android.os.Bundle
+import android.support.v4.app.DialogFragment
+import android.support.v4.app.FragmentManager
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.example.tfat.myapplication.R
+import com.squareup.picasso.Picasso
+import kotlinx.android.synthetic.main.dialog_export_pdf.view.*
+
+class ExportPdfDialog : DialogFragment() {
+    private val dialogTag = this::class.java.simpleName
+
+    enum class Type {
+        Standard,
+        Booklet,
+    }
+
+    var image1Path = ""
+    var image2Path = ""
+    var image3Path = ""
+    var onClickPrint = Runnable { }
+    var type = Type.Standard
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        return inflater.inflate(R.layout.dialog_export_pdf, container, false).apply {
+            rb_exportPdfDialog_foldableBooklet.setOnCheckedChangeListener { _, b ->
+                if (b) {
+                    rb_exportPdfDialog_standard.isChecked = false
+                    type = Type.Booklet
+                }
+            }
+            rb_exportPdfDialog_standard.setOnCheckedChangeListener { _, b ->
+                if (b) {
+                    rb_exportPdfDialog_foldableBooklet.isChecked = false
+                    type = Type.Standard
+                }
+            }
+            btn_exportPdfDialog_cancel.setOnClickListener { this@ExportPdfDialog.dismiss() }
+            btn_exportPdfDialog_print.setOnClickListener { onClickPrint.run() }
+            if (image1Path != "") {
+                Picasso.get().load(image1Path).into(iv_exportPdfDialog_standard_1)
+                Picasso.get().load(image1Path).into(iv_exportPdfDialog_foldableBooklet_1)
+            }
+            if (image2Path != "") {
+                Picasso.get().load(image2Path).into(iv_exportPdfDialog_standard_2)
+            }
+            if (image3Path != "") {
+                Picasso.get().load(image3Path).into(iv_exportPdfDialog_standard_3)
+            }
+        }
+    }
+
+    fun show(manager: FragmentManager?) {
+        manager?.apply {
+            val ft = beginTransaction()
+            findFragmentByTag(dialogTag)?.let { ft.remove(it) }
+            ft.addToBackStack(null)
+            ft.commit()
+        }
+        super.show(manager, dialogTag)
+    }
+}

Різницю між файлами не показано, бо вона завелика
+ 475 - 0
src/main/res/drawable/ic_img_foldable.xml


Різницю між файлами не показано, бо вона завелика
+ 76 - 0
src/main/res/drawable/ic_logo_full_white.xml


+ 144 - 0
src/main/res/layout/dialog_export_pdf.xml

@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="800dp"
+    android:layout_height="544dp"
+    android:background="@drawable/bg_rounded_8dp"
+    android:elevation="8dp">
+
+    <View
+        android:id="@+id/view_exportPdfDialog_header"
+        android:layout_width="0dp"
+        android:layout_height="64dp"
+        android:background="@color/pale_peach"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/tv_exportPdfDialog_name"
+        style="@style/Title"
+        android:text="@string/export_pdf_dialog_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/cocoa"
+        app:layout_constraintBottom_toBottomOf="@id/view_exportPdfDialog_header"
+        app:layout_constraintLeft_toLeftOf="@id/view_exportPdfDialog_header"
+        app:layout_constraintRight_toRightOf="@id/view_exportPdfDialog_header"
+        app:layout_constraintTop_toTopOf="@id/view_exportPdfDialog_header" />
+
+    <RadioButton
+        android:id="@+id/rb_exportPdfDialog_standard"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="52dp"
+        android:layout_marginTop="48dp"
+        android:buttonTint="@color/cocoa"
+        android:checked="true"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/view_exportPdfDialog_header" />
+
+    <TextView
+        android:id="@+id/tv_exportPdfDialog_standard"
+        style="@style/Body1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="36dp"
+        android:text="@string/export_pdf_dialog_radio_standard"
+        android:textColor="@color/cocoa"
+        app:layout_constraintBottom_toBottomOf="@id/rb_exportPdfDialog_standard"
+        app:layout_constraintLeft_toRightOf="@id/rb_exportPdfDialog_standard"
+        app:layout_constraintTop_toTopOf="@id/rb_exportPdfDialog_standard" />
+
+    <ImageView
+        android:id="@+id/iv_exportPdfDialog_standard_1"
+        android:layout_width="144dp"
+        android:layout_height="72dp"
+        android:layout_marginTop="24dp"
+        android:scaleType="centerCrop"
+        app:layout_constraintLeft_toLeftOf="@id/tv_exportPdfDialog_standard"
+        app:layout_constraintTop_toBottomOf="@id/tv_exportPdfDialog_standard" />
+
+    <ImageView
+        android:id="@+id/iv_exportPdfDialog_standard_2"
+        android:layout_width="144dp"
+        android:layout_height="72dp"
+        android:layout_marginLeft="16dp"
+        android:scaleType="centerCrop"
+        app:layout_constraintLeft_toRightOf="@id/iv_exportPdfDialog_standard_1"
+        app:layout_constraintTop_toTopOf="@id/iv_exportPdfDialog_standard_1" />
+
+    <ImageView
+        android:id="@+id/iv_exportPdfDialog_standard_3"
+        android:layout_width="144dp"
+        android:layout_height="72dp"
+        android:layout_marginLeft="16dp"
+        android:scaleType="centerCrop"
+        app:layout_constraintLeft_toRightOf="@id/iv_exportPdfDialog_standard_2"
+        app:layout_constraintTop_toTopOf="@id/iv_exportPdfDialog_standard_2" />
+
+    <RadioButton
+        android:id="@+id/rb_exportPdfDialog_foldableBooklet"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="52dp"
+        android:layout_marginTop="136dp"
+        android:buttonTint="@color/cocoa"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/rb_exportPdfDialog_standard" />
+
+    <TextView
+        android:id="@+id/tv_exportPdfDialog_foldableBooklet"
+        style="@style/Body1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="36dp"
+        android:text="@string/export_pdf_dialog_radio_foldable_booklet"
+        android:textColor="@color/cocoa"
+        app:layout_constraintBottom_toBottomOf="@id/rb_exportPdfDialog_foldableBooklet"
+        app:layout_constraintLeft_toRightOf="@id/rb_exportPdfDialog_foldableBooklet"
+        app:layout_constraintTop_toTopOf="@id/rb_exportPdfDialog_foldableBooklet" />
+
+    <ImageView
+        android:id="@+id/iv_exportPdfDialog_foldableBooklet_1"
+        android:layout_width="144dp"
+        android:layout_height="72dp"
+        android:layout_marginTop="24dp"
+        android:scaleType="centerCrop"
+        app:layout_constraintLeft_toLeftOf="@id/tv_exportPdfDialog_foldableBooklet"
+        app:layout_constraintTop_toBottomOf="@id/tv_exportPdfDialog_foldableBooklet" />
+
+    <ImageView
+        android:id="@+id/iv_exportPdfDialog_foldableBooklet_2"
+        android:layout_width="488dp"
+        android:layout_height="120dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="40dp"
+        android:scaleType="centerCrop"
+        android:src="@drawable/ic_img_foldable"
+        app:layout_constraintLeft_toRightOf="@id/iv_exportPdfDialog_foldableBooklet_1"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="@id/iv_exportPdfDialog_foldableBooklet_1" />
+    <Button
+        android:id="@+id/btn_exportPdfDialog_print"
+        style="@style/Widget.AppCompat.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/export_pdf_dialog_print"
+        android:layout_marginTop="48dp"
+        android:layout_marginRight="24dp"
+        android:layout_marginBottom="24dp"
+        app:layout_constraintTop_toBottomOf="@id/iv_exportPdfDialog_foldableBooklet_2"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintRight_toRightOf="parent" />
+
+    <Button
+        android:id="@+id/btn_exportPdfDialog_cancel"
+        style="@style/Widget.AppCompat.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginRight="8dp"
+        android:text="@string/export_pdf_dialog_cancel"
+        app:layout_constraintBottom_toBottomOf="@id/btn_exportPdfDialog_print"
+        app:layout_constraintRight_toLeftOf="@id/btn_exportPdfDialog_print" />
+</android.support.constraint.ConstraintLayout>

+ 6 - 0
src/main/res/values/strings.xml

@@ -16,4 +16,10 @@
     <string name="share_dialog_edit_cover">Edit Cover</string>
     <string name="share_dialog_export_pdf">Export PDF</string>
     <string name="share_dialog_print">Print</string>
+
+    <string name="export_pdf_dialog_title">Choose PDF format</string>
+    <string name="export_pdf_dialog_radio_standard">Standard PDF</string>
+    <string name="export_pdf_dialog_radio_foldable_booklet">Foldable Booklet</string>
+    <string name="export_pdf_dialog_print">Print</string>
+    <string name="export_pdf_dialog_cancel">Cancel</string>
 </resources>