diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index babb910f0..84558a8e5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,7 +57,7 @@ material3-adaptive = "1.1.0" material3-adaptive-navigation-suite = "1.3.2" media3 = "1.6.1" # @keep -minSdk = "21" +minSdk = "31" okHttp = "4.12.0" playServicesWearable = "19.0.0" protolayout = "1.3.0-beta02" @@ -75,6 +75,7 @@ wearComposeMaterial = "1.5.0-beta01" wearComposeMaterial3 = "1.5.0-beta01" wearToolingPreview = "1.0.0" webkit = "1.13.0" +pdfViewerFragment = "1.0.0-alpha09" [libraries] accompanist-adaptive = "com.google.accompanist:accompanist-adaptive:0.37.3" @@ -182,6 +183,7 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okHttp" } play-services-wearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "playServicesWearable" } wear-compose-material3 = { module = "androidx.wear.compose:compose-material3", version.ref = "wearComposeMaterial3" } +androidx-pdf-viewer-fragment = {module = "androidx.pdf:pdf-viewer-fragment", version.ref = "pdfViewerFragment" } [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } diff --git a/misc/build.gradle.kts b/misc/build.gradle.kts index e5cc3cc1d..b5fe04dfe 100644 --- a/misc/build.gradle.kts +++ b/misc/build.gradle.kts @@ -63,6 +63,9 @@ dependencies { implementation(libs.kotlinx.serialization.json) ksp(libs.hilt.compiler) + implementation(libs.google.android.material) + implementation(libs.androidx.pdf.viewer.fragment) + implementation(libs.androidx.constraintlayout) implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.window) diff --git a/misc/src/main/java/com/example/snippets/PdfViewerKotlinSnippets.kt b/misc/src/main/java/com/example/snippets/PdfViewerKotlinSnippets.kt new file mode 100644 index 000000000..7f4da37b4 --- /dev/null +++ b/misc/src/main/java/com/example/snippets/PdfViewerKotlinSnippets.kt @@ -0,0 +1,305 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.snippets + +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.ext.SdkExtensions +import androidx.activity.result.contract.ActivityResultContracts.GetContent +import androidx.annotation.RequiresApi +import androidx.annotation.RequiresExtension +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction +import androidx.pdf.viewer.fragment.PdfStylingOptions +import androidx.pdf.viewer.fragment.PdfViewerFragment +import com.google.android.material.button.MaterialButton +import java.util.logging.Level.INFO +import java.util.logging.Level.SEVERE +import java.util.logging.Logger + +class PdfViewerKotlinSnippets { + + @RequiresApi(Build.VERSION_CODES.R) + fun onCreate(savedInstanceState: Bundle?) { + + // [START android_pdf_viewer_extension_version_kotlin] + if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) { + // Load the fragment and document. + } + // [END android_pdf_viewer_extension_version_kotlin] + } + + // [START android_pdf_viewer_create_compat_activity_kotlin] + class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + val getContentButton: MaterialButton = findViewById(R.id.launch_button) + val searchButton: MaterialButton = findViewById(R.id.search_button) + } + } + // [END android_pdf_viewer_create_compat_activity_kotlin] + + // [START android_pdf_viewer_extend_fragment_kotlin] + @RequiresExtension(extension = Build.VERSION_CODES.S, version = 13) + class PdfViewerFragmentExtended : PdfViewerFragment() { + private val myLogger: Logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME) + + override fun onLoadDocumentSuccess() { + myLogger.log(INFO, "// Log document success.") + } + + override fun onLoadDocumentError(error: Throwable) { + myLogger.log(SEVERE, "// Log document error.") + } + } + // [END android_pdf_viewer_extend_fragment_kotlin] + + /** Enable nested classes. **/ + class ClassHolder { + + // [START android_pdf_viewer_create_fragment_kotlin] + class MainActivity : AppCompatActivity() { + private var pdfViewerFragment: PdfViewerFragment? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // ... + + if (pdfViewerFragment == null) { + pdfViewerFragment = + supportFragmentManager + .findFragmentByTag(PDF_VIEWER_FRAGMENT_TAG) as PdfViewerFragment? + } + } + + // Used to instantiate and commit the fragment. + @RequiresExtension(extension = Build.VERSION_CODES.S, version = 13) + private fun initialisePdfViewerFragment() { + // This condition can be skipped if you want to create a new fragment everytime. + if (pdfViewerFragment == null) { + val fragmentManager: FragmentManager = supportFragmentManager + + // Fragment initialization. + pdfViewerFragment = PdfViewerFragmentExtended() + val transaction: FragmentTransaction = fragmentManager.beginTransaction() + + // Replace an existing fragment in a container with an instance of a new fragment. + transaction.replace( + R.id.fragment_container_view, + pdfViewerFragment!!, + PDF_VIEWER_FRAGMENT_TAG + ) + transaction.commitAllowingStateLoss() + fragmentManager.executePendingTransactions() + } + } + + companion object { + private const val MIME_TYPE_PDF = "application/pdf" + private const val PDF_VIEWER_FRAGMENT_TAG = "pdf_viewer_fragment_tag" + } + } + // [END android_pdf_viewer_create_fragment_kotlin] + } + + /** Enable nested classes. **/ + class ClassHolder2 { + + // [START android_pdf_viewer_enable_document_search_kotlin] + class MainActivity : AppCompatActivity() { + @RequiresExtension(extension = Build.VERSION_CODES.S, version = 13) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + var pdfViewerFragment: PdfViewerFragment? = null + val searchButton: MaterialButton = findViewById(R.id.search_button) + searchButton.setOnClickListener { + pdfViewerFragment?.isTextSearchActive = pdfViewerFragment?.isTextSearchActive == false + } + + // Ensure WindowInsetsCompat are passed to content views without being consumed by the decor + // view. These insets are used to calculate the position of the search view. + WindowCompat.setDecorFitsSystemWindows(window, false) + } + } + // [END android_pdf_viewer_enable_document_search_kotlin] + } + + /** Enable nested classes. **/ + class ClassHolder3 : AppCompatActivity() { + + private var pdfViewerFragment: PdfViewerFragment? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // ... + + if (pdfViewerFragment == null) { + pdfViewerFragment = + supportFragmentManager + .findFragmentByTag(PDF_VIEWER_FRAGMENT_TAG) as PdfViewerFragment? + } + } + + // [START android_pdf_viewer_launch_file_picker_kotlin] + @RequiresExtension(extension = Build.VERSION_CODES.S, version = 13) + class MainActivity : AppCompatActivity() { + private var pdfViewerFragment: PdfViewerFragment? = null + private var filePicker = + registerForActivityResult(GetContent()) { uri: Uri? -> + uri?.let { + initialisePdfViewerFragment() + pdfViewerFragment?.documentUri = uri + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // ... + val getContentButton: MaterialButton = findViewById(R.id.launch_button) + getContentButton.setOnClickListener { filePicker.launch(MIME_TYPE_PDF, null) } + } + + private fun initialisePdfViewerFragment() { + // ... + } + + companion object { + private const val MIME_TYPE_PDF = "application/pdf" + // ... + } + } + // [END android_pdf_viewer_launch_file_picker_kotlin] + + // [START android_pdf_viewer_style_fragment_kotlin] + @RequiresExtension(extension = Build.VERSION_CODES.S, version = 13) + private fun initialisePdfViewerFragment() { + // This condition can be skipped if you want to create a new fragment everytime + if (pdfViewerFragment == null) { + val fragmentManager: FragmentManager = supportFragmentManager + + // Create styling options + val stylingOptions = PdfStylingOptions(R.style.pdfContainerStyle) + + // Fragment initialization + pdfViewerFragment = PdfViewerFragment.newInstance(stylingOptions) + + // .. execute fragment transaction + } + } + // [END android_pdf_viewer_style_fragment_kotlin] + + companion object { + private const val PDF_VIEWER_FRAGMENT_TAG = "pdf_viewer_fragment_tag" + } + } + + // [START android_pdf_viewer_style_fragment_constructor_kotlin] + @RequiresExtension(extension = Build.VERSION_CODES.S, version = 13) + class StyledPdfViewerFragment : PdfViewerFragment { + + constructor() : super() + + private constructor(pdfStylingOptions: PdfStylingOptions) : super(pdfStylingOptions) + + companion object { + fun newInstance(): StyledPdfViewerFragment { + val stylingOptions = PdfStylingOptions(R.style.pdfContainerStyle) + return StyledPdfViewerFragment(stylingOptions) + } + } + } + // [END android_pdf_viewer_style_fragment_constructor_kotlin] + + /** Enable nested classes. **/ + class ClassHolder4 { + + // [START android_pdf_viewer_full_code_kotlin] + @RequiresExtension(extension = Build.VERSION_CODES.S, version = 13) + class MainActivity : AppCompatActivity() { + + private var pdfViewerFragment: PdfViewerFragment? = null + private var filePicker = + registerForActivityResult(GetContent()) { uri: Uri? -> + uri?.let { + initialisePdfViewerFragment() + pdfViewerFragment?.documentUri = uri + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + if (pdfViewerFragment == null) { + pdfViewerFragment = + supportFragmentManager + .findFragmentByTag(PDF_VIEWER_FRAGMENT_TAG) as PdfViewerFragment? + } + + val getContentButton: MaterialButton = findViewById(R.id.launch_button) + val searchButton: MaterialButton = findViewById(R.id.search_button) + + getContentButton.setOnClickListener { filePicker.launch(MIME_TYPE_PDF) } + searchButton.setOnClickListener { + pdfViewerFragment?.isTextSearchActive = pdfViewerFragment?.isTextSearchActive == false + } + } + + private fun initialisePdfViewerFragment() { + // This condition can be skipped if you want to create a new fragment everytime + if (pdfViewerFragment == null) { + val fragmentManager: FragmentManager = supportFragmentManager + + // Create styling options + // val stylingOptions = PdfStylingOptions(R.style.pdfContainerStyle) + + // Fragment initialization + // For customization + // pdfViewerFragment = PdfViewerFragment.newInstance(stylingOptions) + pdfViewerFragment = PdfViewerFragmentExtended() + val transaction: FragmentTransaction = fragmentManager.beginTransaction() + + // Replace an existing fragment in a container with an instance of a new fragment + transaction.replace( + R.id.fragment_container_view, + pdfViewerFragment!!, + PDF_VIEWER_FRAGMENT_TAG + ) + transaction.commitAllowingStateLoss() + fragmentManager.executePendingTransactions() + } + } + + companion object { + private const val MIME_TYPE_PDF = "application/pdf" + private const val PDF_VIEWER_FRAGMENT_TAG = "pdf_viewer_fragment_tag" + } + } + // [END android_pdf_viewer_full_code_kotlin] + + companion object { + private const val PDF_VIEWER_FRAGMENT_TAG = "pdf_viewer_fragment_tag" + } + } +} diff --git a/misc/src/main/res/layout/activity_main.xml b/misc/src/main/res/layout/activity_main.xml index 59cbc1b45..62939132f 100644 --- a/misc/src/main/res/layout/activity_main.xml +++ b/misc/src/main/res/layout/activity_main.xml @@ -6,24 +6,58 @@ android:layout_width="match_parent" android:layout_height="match_parent"> -