Commit 1091d128 authored by wanglei's avatar wanglei

init

parent e05502c6
Pipeline #1230 failed with stages
*.iml
.gradle
/local.properties
/.idea/caches
/.idea
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
# PDF Viewer Scanner White 1.Vision识别文本
[ML Kit Vision 快速入门示例应用](https://github.com/googlesamples/mlkit/tree/master/android/vision-quickstart)
pdf浏览扫描器 \ No newline at end of file
\ No newline at end of file
/build
\ No newline at end of file
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
}
android {
namespace = "com.base.pdfviewerscannerwhite"
compileSdk = 34
defaultConfig {
applicationId = "com.base.pdfviewerscannerwhite"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
buildConfig = true
}
sourceSets {
getByName("main") {
assets {
srcDirs("src\\main\\assets", "src\\main\\assets")
}
}
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
//第三方UI
implementation("io.github.cymchad:BaseRecyclerViewAdapterHelper4:4.1.4")
//mlkit
implementation("com.google.android.gms:play-services-mlkit-document-scanner:16.0.0-beta1")
//Pdf库
implementation("com.tom-roush:pdfbox-android:2.0.27.0")
//Word库
//Excel库
//PPT库
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
package com.base.pdfviewerscannerwhite
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.base.pdfviewerscannerwhite", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PDFViewerScannerWhite"
tools:targetApi="34">
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="document_ui" />
<activity
android:name=".ui.splash.SplashActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.main.MainActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".ui.pdf.PdfActivity"
android:exported="false"
android:screenOrientation="portrait"
android:theme="@style/Theme.PDFViewerScannerWhite"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
\ No newline at end of file
package com.base.pdfviewerscannerwhite.bean
object ConstObject {
const val MIME_TYPE_IMAGE = "image/*"
const val MIME_TYPE_VIDEO = "video/*"
const val MIME_TYPE_AUDIO = "audio/*"
const val MIME_TYPE_PDF = "application/pdf"
const val MIME_TYPE_DOC = "application/msword"
const val MIME_TYPE_DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
const val MIME_TYPE_XLS = "application/vnd.ms-excel"
const val MIME_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
const val MIME_TYPE_PPT = "application/vnd.ms-powerpoint"
const val MIME_TYPE_PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
const val MIME_TYPE_APK = "application/vnd.android.package-archive"
const val MIME_TYPE_ZIP = "application/zip"
// var ifAgreePrivacy = false
// get() {
// return AppPreferences.getInstance().getBoolean("ifAgreePrivacy", field)
// }
// set(value) {
// field = value
// AppPreferences.getInstance().put("ifAgreePrivacy", value, true)
// }
//
// var searchEngineSp = GOOGLE
// get() {
// return AppPreferences.getInstance().getString("searchEngine", field)
// }
// set(value) {
// field = value
// AppPreferences.getInstance().put("searchEngine", value, true)
// }
//
// var isFirstMainShow = true
// get() {
// return AppPreferences.getInstance().getBoolean("isFirstMainShow", field)
// }
// set(value) {
// field = value
// AppPreferences.getInstance().put("isFirstMainShow", value, true)
// }
//
// var mainShowCount = 0
// get() {
// return AppPreferences.getInstance().getInt("mainShowCount", field)
// }
// set(value) {
// field = value
// AppPreferences.getInstance().put("mainShowCount", value, true)
// }
//
// var webPrivacy = false
// get() {
// return AppPreferences.getInstance().getBoolean("webPrivacy", field)
// }
// set(value) {
// field = value
// AppPreferences.getInstance().put("webPrivacy", value, true)
// }
//
//
// var downloadDisclaimer = false
// get() {
// return AppPreferences.getInstance().getBoolean("downloadDisclaimer", field)
// }
// set(value) {
// field = value
// AppPreferences.getInstance().put("downloadDisclaimer", value, true)
// }
//
// var optimizationShow = false
// get() {
// return AppPreferences.getInstance().getBoolean("optimizationShow", field)
// }
// set(value) {
// field = value
// AppPreferences.getInstance().put("optimizationShow", value, true)
// }
//
// var shortcutShowSp = false
// get() {
// return AppPreferences.getInstance().getBoolean("shortcutShow", field)
// }
// set(value) {
// field = value
// AppPreferences.getInstance().put("shortcutShow", value, true)
// }
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.bean
data class DocumentBean(
val path: String = "",
val type: String = ""
) {
companion object {
const val TYPE_PDF = "type_pdf"
}
}
package com.base.pdfviewerscannerwhite.bean
import android.net.Uri
data class MediaBean(
val path: String = "",
val uri: Uri = Uri.EMPTY,
val mimeType: String = ""
)
package com.base.pdfviewerscannerwhite.bean
import android.graphics.drawable.Drawable
data class PdfPageBean(
val pageIndex: Int = 0,
val pageDrawable: Drawable? = null
)
\ No newline at end of file
package com.base.pdfviewerscannerwhite.helper
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.base.pdfviewerscannerwhite.ui.main.MainActivity
import com.base.pdfviewerscannerwhite.utils.ActivityLauncher
import com.base.pdfviewerscannerwhite.utils.ActivityManagerUtils
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
protected abstract val binding: T
lateinit var launcher: ActivityLauncher
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launcher = ActivityLauncher(this)
setContentView(binding.root)
// EventUtils.event("page_${javaClass.simpleName}")
initView()
initListener()
}
protected abstract fun initView()
protected open fun initListener() {}
fun finishToMain() {
if (this !is MainActivity && !ActivityManagerUtils.getInstance().isActivityInStack(MainActivity::class.java)) {
startActivity(Intent(this, MainActivity::class.java))
}
finish()
}
var dialog: Dialog? = null
override fun onDestroy() {
super.onDestroy()
ActivityManagerUtils.getInstance().removeActivity(this)
if (dialog != null) {
dialog?.dismiss()
dialog = null
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.helper
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
abstract class BaseFragment<T : ViewBinding> : Fragment() {
protected abstract val binding: T
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setView()
setListener()
}
protected abstract fun setView()
protected open fun setListener() {}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.bean.DocumentBean
import com.base.pdfviewerscannerwhite.bean.DocumentBean.Companion.TYPE_PDF
import com.base.pdfviewerscannerwhite.databinding.ItemDocmentBinding
import com.base.pdfviewerscannerwhite.utils.KotlinExt.toFormatSize
import com.base.pdfviewerscannerwhite.utils.KotlinExt.toFormatTime
import com.base.pdfviewerscannerwhite.utils.XmlEx.inflate
import com.chad.library.adapter4.BaseQuickAdapter
import java.io.File
class DocumentAdapter : BaseQuickAdapter<DocumentBean, DocumentAdapter.DocumentViewHolder>() {
inner class DocumentViewHolder(view: View) : ViewHolder(view)
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: DocumentViewHolder, position: Int, item: DocumentBean?) {
if (item == null) return
val binding = ItemDocmentBinding.bind(holder.itemView)
if (item.type == TYPE_PDF) {
binding.iv.setImageResource(R.mipmap.h_pdfiocn)
}
val file = File(item.path)
binding.tvName.text = file.name
binding.tvInfo.text = file.lastModified().toFormatTime() + " " + file.length().toFormatSize()
}
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): DocumentViewHolder {
return DocumentViewHolder(R.layout.item_docment.inflate(parent))
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.main
import android.annotation.SuppressLint
import android.net.Uri
import android.view.View
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.base.pdfviewerscannerwhite.BuildConfig
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.databinding.ActivityMainBinding
import com.base.pdfviewerscannerwhite.helper.BaseActivity
import com.base.pdfviewerscannerwhite.ui.pdf.PdfFragment
import com.base.pdfviewerscannerwhite.utils.PermissionUtils.requestStorePermission
import com.base.pdfviewerscannerwhite.utils.ToastUtils.toast
class MainActivity : BaseActivity<ActivityMainBinding>(), MainView {
private val TAG = "MainActivity"
private lateinit var mainPresenter: MainPresenter
override val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val pdfFragment: PdfFragment by lazy {
PdfFragment()
}
private val fragments by lazy {
mutableListOf(pdfFragment)
}
override fun initView() {
// startActivity(Intent(this, PdfActivity::class.java))
mainPresenter = MainPresenter(this)
mainPresenter.initScannerLauncher(this)
binding.viewPager2.run {
isUserInputEnabled = false
adapter = object : FragmentStateAdapter(this@MainActivity) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
}
binding.viewPager2.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
}
})
}
@SuppressLint("ClickableViewAccessibility")
override fun initListener() {
super.initListener()
binding.llDocument.setOnClickListener {
changeTabSelect(it)
}
binding.llRecent.setOnClickListener {
changeTabSelect(it)
}
binding.llBookmark.setOnClickListener {
changeTabSelect(it)
}
binding.llTool.setOnClickListener {
changeTabSelect(it)
}
binding.ivScan.setOnClickListener {
mainPresenter.starGmsScan(this)
}
binding.tvTittle.setOnClickListener {
requestStorePermission(launcher){}
}
}
private fun changeTabSelect(selectView: View) {
binding.llDocument.isSelected = selectView == binding.llDocument
binding.llRecent.isSelected = selectView == binding.llRecent
binding.llBookmark.isSelected = selectView == binding.llBookmark
binding.llTool.isSelected = selectView == binding.llTool
}
fun changeRipple() = binding.root.postDelayed({
val selectRipple = ContextCompat.getDrawable(this@MainActivity, R.drawable.ripple_select)
val normalRipple = ContextCompat.getDrawable(this@MainActivity, R.drawable.ripple_normal)
if (binding.llDocument.isSelected) {
binding.llDocument.background = selectRipple
} else {
binding.llDocument.background = normalRipple
}
if (binding.llRecent.isSelected) {
binding.llRecent.background = selectRipple
} else {
binding.llRecent.background = normalRipple
}
if (binding.llBookmark.isSelected) {
binding.llBookmark.background = selectRipple
} else {
binding.llBookmark.background = normalRipple
}
if (binding.llTool.isSelected) {
binding.llTool.background = selectRipple
} else {
binding.llTool.background = normalRipple
}
}, 200)
@SuppressLint("SetTextI18n")
override fun handleActivityGmsScanResult(imageUri: Uri, pdfUri: Uri) {
toast("handleActivityGmsScanResult")
if (BuildConfig.DEBUG) {
binding.tvDebugLog.text = "imageUri=$imageUri\npdfUri=$pdfUri"
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.main
import android.app.Activity
import android.content.IntentSender
import android.net.Uri
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.base.pdfviewerscannerwhite.utils.ToastUtils.toast
import com.google.mlkit.vision.documentscanner.GmsDocumentScannerOptions
import com.google.mlkit.vision.documentscanner.GmsDocumentScanning
import com.google.mlkit.vision.documentscanner.GmsDocumentScanningResult
class MainPresenter(
val mainView: MainView
) {
private lateinit var scannerLauncher: ActivityResultLauncher<IntentSenderRequest>
fun initScannerLauncher(appCompatActivity: AppCompatActivity) {
scannerLauncher =
appCompatActivity.registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
val resultCode = activityResult.resultCode
val result = GmsDocumentScanningResult.fromActivityResultIntent(activityResult.data)
appCompatActivity.toast("回调1")
if (resultCode == Activity.RESULT_OK && result != null) {
val pages = result.pages
appCompatActivity.toast("回调2 ${pages?.size}")
var imageUri = Uri.EMPTY
if (!pages.isNullOrEmpty()) {
appCompatActivity.toast("回调3")
imageUri = pages[0].imageUri
}
val pdfUri = result.pdf?.uri ?: Uri.EMPTY
mainView.handleActivityGmsScanResult(imageUri, pdfUri)
}
}
}
fun starGmsScan(activity: Activity) {
val options =
GmsDocumentScannerOptions.Builder()
.setScannerMode(GmsDocumentScannerOptions.SCANNER_MODE_BASE)
.setResultFormats(GmsDocumentScannerOptions.RESULT_FORMAT_PDF)
.setGalleryImportAllowed(true)
options.setScannerMode(GmsDocumentScannerOptions.SCANNER_MODE_FULL)
val pageLimit = 1
options.setPageLimit(pageLimit)
GmsDocumentScanning.getClient(options.build())
.getStartScanIntent(activity)
.addOnSuccessListener { intentSender: IntentSender ->
scannerLauncher.launch(IntentSenderRequest.Builder(intentSender).build())
}
.addOnFailureListener() { e: Exception ->
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.main
import android.net.Uri
interface MainView {
fun handleActivityGmsScanResult(imageUri: Uri, pdfUri: Uri)
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.pdf
import com.base.pdfviewerscannerwhite.bean.PdfPageBean
import com.base.pdfviewerscannerwhite.databinding.ActivityPdfBinding
import com.base.pdfviewerscannerwhite.helper.BaseActivity
import com.base.pdfviewerscannerwhite.utils.PermissionUtils.checkStorePermission
import com.base.pdfviewerscannerwhite.utils.PermissionUtils.requestStorePermission
class PdfActivity : BaseActivity<ActivityPdfBinding>(), PdfView {
private lateinit var pdfPresenter: PdfPresenter
override val binding: ActivityPdfBinding by lazy {
ActivityPdfBinding.inflate(layoutInflater)
}
override fun initView() {
pdfPresenter = PdfPresenter(this, this)
if (checkStorePermission()){
pdfPresenter.splitPdf("")
}else{
requestStorePermission(launcher) {
pdfPresenter.splitPdf("")
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
pdfPresenter.handler = binding.root.handler
}
override fun initPdfPageRv(items: List<PdfPageBean>) {
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.pdf
import androidx.lifecycle.lifecycleScope
import com.base.pdfviewerscannerwhite.bean.ConstObject
import com.base.pdfviewerscannerwhite.bean.DocumentBean
import com.base.pdfviewerscannerwhite.bean.DocumentBean.Companion.TYPE_PDF
import com.base.pdfviewerscannerwhite.databinding.FragmentPdfBinding
import com.base.pdfviewerscannerwhite.helper.BaseFragment
import com.base.pdfviewerscannerwhite.ui.adapter.DocumentAdapter
import com.base.pdfviewerscannerwhite.utils.PermissionUtils.checkStorePermission
import com.base.pdfviewerscannerwhite.utils.getMediaFile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class PdfFragment() : BaseFragment<FragmentPdfBinding>() {
private var type = TYPE_PDF
private var documentList: List<DocumentBean>? = null
private lateinit var adapter: DocumentAdapter
constructor(type: String) : this() {
this.type = type
}
override val binding: FragmentPdfBinding by lazy {
FragmentPdfBinding.inflate(layoutInflater)
}
override fun setView() {
adapter = DocumentAdapter()
binding.rv.adapter = adapter
if (documentList != null) {
adapter.submitList(documentList)
} else {
if (requireContext().checkStorePermission()) {
initData()
}
}
}
private fun initData() = lifecycleScope.launch(Dispatchers.IO) {
val list = requireContext().getMediaFile(selectionArgs = arrayOf(ConstObject.MIME_TYPE_PDF))
val pdfList = list.map {
DocumentBean(it.path, type)
}
launch(Dispatchers.Main) {
adapter.submitList(pdfList)
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.pdf
import android.content.Context
import android.os.Environment
import android.os.Handler
import com.base.pdfviewerscannerwhite.bean.PdfPageBean
import com.base.pdfviewerscannerwhite.utils.PdfUtils
import com.base.pdfviewerscannerwhite.utils.ToastUtils.toast
import java.io.File
class PdfPresenter(val context: Context, val pdfView: PdfView) {
var handler: Handler? = null
fun splitPdf(pdfPath: String) = Thread {
val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "DEMO.pdf")
val drawableList = PdfUtils.getPdfDrawables(context, file.absolutePath)
handler?.post {
context.toast("size=${drawableList.size}")
}
}.start()
fun iniPdfPage(filePath: String) {
val list = arrayListOf<PdfPageBean>()
val number = PdfUtils.getNumberOfPages(filePath)
repeat(number) {
list.add(PdfPageBean(it))
}
pdfView.initPdfPageRv(list)
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.pdf
import com.base.pdfviewerscannerwhite.bean.PdfPageBean
interface PdfView {
abstract fun initPdfPageRv(items: List<PdfPageBean>)
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.splash
import android.annotation.SuppressLint
import android.content.Intent
import com.base.pdfviewerscannerwhite.databinding.ActivitySplashBinding
import com.base.pdfviewerscannerwhite.helper.BaseActivity
import com.base.pdfviewerscannerwhite.ui.main.MainActivity
@SuppressLint("CustomSplashScreen")
class SplashActivity : BaseActivity<ActivitySplashBinding>(), SplashView {
override val binding: ActivitySplashBinding by lazy {
ActivitySplashBinding.inflate(layoutInflater)
}
override fun initView() {
showAd()
}
override fun showAd() {
startActivity(Intent(this, MainActivity::class.java))
}
override fun agreePrivacy() {
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.splash
class SplashPresenter(
val splashView: SplashView
) {
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.splash
interface SplashView {
abstract fun showAd()
abstract fun agreePrivacy()
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.utils
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultCaller
import androidx.activity.result.contract.ActivityResultContracts
class ActivityLauncher(activityResultCaller: ActivityResultCaller) {
//region 权限
private var permissionCallback: ActivityResultCallback<Map<String, Boolean>>? = null
private val permissionLauncher =
activityResultCaller.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result: Map<String, Boolean> ->
permissionCallback?.onActivityResult(result)
}
fun launch(
permissionArray: Array<String>,
permissionCallback: ActivityResultCallback<Map<String, Boolean>>?
) {
this.permissionCallback = permissionCallback
permissionLauncher.launch(permissionArray)
}
//endregion
//region intent跳转
private var activityResultCallback: ActivityResultCallback<ActivityResult>? = null
private val intentLauncher =
activityResultCaller.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->
activityResultCallback?.onActivityResult(activityResult)
}
/**
* it.resultCode == Activity.RESULT_OK
*/
fun launch(
intent: Intent,
activityResultCallback: ActivityResultCallback<ActivityResult>? = null
) {
this.activityResultCallback = activityResultCallback
intentLauncher.launch(intent)
}
//endregion
//region saf
// private var safResultCallback: ActivityResultCallback<Uri?>? = null
// private val safLauncher =
// activityResultCaller.registerForActivityResult(
// ActivityResultContracts.OpenDocument(),
// ) { uri ->
// safResultCallback?.onActivityResult(uri)
// }
//
// fun launch(array: Array<String>, safResultCallback: ActivityResultCallback<Uri?>?) {
// this.safResultCallback = safResultCallback
// safLauncher.launch(array)
// }
//end region
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.utils;
import android.app.Activity;
import java.util.Stack;
public class ActivityManagerUtils {
private static ActivityManagerUtils instance;
private Stack<Activity> activityStack = new Stack<>();
private ActivityManagerUtils() {
}
public static ActivityManagerUtils getInstance() {
if (instance == null) {
instance = new ActivityManagerUtils();
}
return instance;
}
/**
* 添加Activity到堆栈
*/
public void addActivity(Activity activity) {
activityStack.add(activity);
}
/**
* 移除Activity从堆栈
*/
public void removeActivity(Activity activity) {
activityStack.remove(activity);
}
/**
* 获取当前Activity(堆栈中最后一个压入的)
*/
public Activity getCurrentActivity() {
return activityStack.lastElement();
}
/**
* 结束指定的Activity
*/
public void finishActivity(Activity activity) {
if (activity != null) {
activityStack.remove(activity);
activity.finish();
}
}
/**
* 结束所有Activity
*/
public void finishAllActivity() {
for (Activity activity : activityStack) {
if (activity != null) {
activity.finish();
}
}
activityStack.clear();
}
/**
* 检查Activity是否存在于堆栈中
*/
public boolean isActivityInStack(Class<?> cls) {
boolean isExist = false;
if (cls != null) {
for (Activity activity : activityStack) {
if (cls.equals(activity.getClass())) {
isExist = true;
break;
}
}
}
return isExist;
}
public Activity getTopActivity() {
if (activityStack.empty()) {
return null;
} else {
return activityStack.peek();
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.utils
import java.text.SimpleDateFormat
import java.util.Locale
object KotlinExt {
fun Number.toFormatSize(count: Int = 1): String {
var suffix = "B"
var fSize = this.toDouble()
if (fSize > 1024) {
suffix = "KB"
fSize /= 1024.0
}
if (fSize > 1024) {
suffix = "MB"
fSize /= 1024.0
}
if (fSize > 1024) {
suffix = "GB"
fSize /= 1024.0
}
return String.format("%.${count}f %s", fSize, suffix)
}
fun Long.toFormatTime(): String {
return SimpleDateFormat("MMM dd,yyyy", Locale.ENGLISH).format(this)
}
fun Array<String>.array2String(): String {
val stringBuilder = StringBuilder()
forEach {
stringBuilder.append(it)
}
return stringBuilder.toString()
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.utils
import android.util.Log
import com.base.pdfviewerscannerwhite.BuildConfig
object LogEx {
val isOpen = true
val filterTAG = arrayOf(
"",
)
fun logDebug(tag: String, content: String, isMust: Boolean = false) {
if (isMust) {
Log.e(tag, content)
} else {
if (!isOpen) return
if (filterTAG.contains(tag)) return
if (BuildConfig.DEBUG) {
Log.e(tag, content)
}
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.utils
import android.annotation.SuppressLint
import android.content.Context
import android.database.Cursor
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_APK
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_DOC
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_DOCX
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_PDF
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_PPT
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_PPTX
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_XLS
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_XLSX
import com.base.pdfviewerscannerwhite.bean.ConstObject.MIME_TYPE_ZIP
import com.base.pdfviewerscannerwhite.bean.MediaBean
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import java.io.File
private val TAG = "MediaStoreUtils"
private val commonMediaDir = arrayOf(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).absolutePath,
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).absolutePath,
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath,
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).absolutePath,
)
fun Context.updateMediaStore(
filePath: Array<String> = commonMediaDir
) {
MediaScannerConnection.scanFile(
this, filePath, null
) { path: String?, uri: Uri? -> }
}
//图片视频音频不用这个查
fun Context.getMediaFile(selectionArgs: Array<String>? = null): ArrayList<MediaBean> {
val list = arrayListOf<MediaBean>()
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATA,
// MediaStore.Files.FileColumns.TITLE,
MediaStore.Files.FileColumns.MIME_TYPE,
// MediaStore.Files.FileColumns.SIZE,
)
val mimeTypeSelectionArgs = selectionArgs ?: arrayOf(
MIME_TYPE_PDF,
MIME_TYPE_DOC,
MIME_TYPE_DOCX,
MIME_TYPE_XLS,
MIME_TYPE_XLSX,
MIME_TYPE_PPT,
MIME_TYPE_PPTX,
MIME_TYPE_APK
)
val mimeTypeSelection = if (mimeTypeSelectionArgs.size == 1) {
"${MediaStore.Files.FileColumns.MIME_TYPE} = ?"
} else {
"${MediaStore.Files.FileColumns.MIME_TYPE} IN (" + mimeTypeSelectionArgs.joinToString(",") { "?" } + ")"
}
// val selection = "$timeSelection AND $mimeTypeSelection"
val selection = mimeTypeSelection
LogEx.logDebug(TAG, "selection=$selection")
var cursor: Cursor? = null
try {
// 进行查询,并设置排序方式,按照文件添加时间降序排序
cursor = this.contentResolver.query(
MediaStore.Files.getContentUri("external"),
projection,
selection,
selectionArgs ?: mimeTypeSelectionArgs,
MediaStore.Files.FileColumns.DATE_ADDED + " DESC"
)
if (cursor != null) {
while (cursor.moveToNext()) {
runCatching {
val id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID))
val uri = Uri.withAppendedPath(MediaStore.Files.getContentUri("external"), id.toString())
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
// val title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.TITLE))
val mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MIME_TYPE))
// val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE))
LogEx.logDebug(TAG, "path=$path mimeType=$mimeType")
list.add(
MediaBean(path = path, uri = uri, mimeType = mimeType)
)
}
}
}
} catch (e: Exception) {
LogEx.logDebug(TAG, "Exception $e")
} finally {
cursor?.close()
}
return list
}
@SuppressLint("Range")
fun Context.getMediaPhotoCountSize(): Pair<Int, Long> {
var count = 0
var totalSize = 0L
runCatching {
val contentResolver = this.contentResolver
// 定义查询的Uri和列
val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.SIZE)
// 执行查询
val cursor = contentResolver.query(uri, projection, null, null, null)
// 检查cursor是否包含数据
count = cursor?.count ?: 0
while (cursor?.moveToNext() == true) {
totalSize += cursor.getInt(cursor.getColumnIndex(MediaStore.Video.Media.SIZE))
}
// 关闭cursor
cursor?.close()
}
return Pair(count, totalSize)
}
fun Context.getMediaPhoto(): List<MediaBean> {
val list = arrayListOf<MediaBean>()
val contentResolver = this.contentResolver
// 定义查询的Uri和列
val baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA)
// 执行查询
var cursor: Cursor? = null
try {
cursor = contentResolver.query(baseUri, projection, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID))
val uri = Uri.withAppendedPath(baseUri, id.toString())
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
list.add(MediaBean(path = path, uri = uri, mimeType = "image/*"))
}
}
} catch (e: Exception) {
} finally {
cursor?.close()
}
return list
}
fun Context.getMediaVideo(): List<MediaBean> {
val list = arrayListOf<MediaBean>()
val contentResolver = this.contentResolver
// 定义查询的Uri和列
val baseUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Video.Media._ID, MediaStore.Video.Media.DATA, MediaStore.Video.Media.MIME_TYPE)
// 执行查询
var cursor: Cursor? = null
try {
cursor = contentResolver.query(baseUri, projection, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID))
val uri = Uri.withAppendedPath(baseUri, id.toString())
LogEx.logDebug(TAG, "getMediaVideo uri=$uri")
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))
val mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.MIME_TYPE))
list.add(MediaBean(path = path, uri = uri, mimeType = mimeType))
}
}
} catch (e: Exception) {
} finally {
cursor?.close()
}
return list
}
fun Context.getMediaAudio(): List<MediaBean> {
val list = arrayListOf<MediaBean>()
val contentResolver = this.contentResolver
// 定义查询的Uri和列
val baseUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Audio.Media._ID, MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.MIME_TYPE)
// 执行查询
var cursor: Cursor? = null
try {
cursor = contentResolver.query(baseUri, projection, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID))
val uri = Uri.withAppendedPath(baseUri, id.toString())
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))
val mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.MIME_TYPE))
list.add(MediaBean(path = path, uri = uri, mimeType = mimeType))
}
}
} catch (e: Exception) {
} finally {
cursor?.close()
}
return list
}
@SuppressLint("Range")
fun Context.getMediaVideoCountSize(): Pair<Int, Long> {
var count = 0
var totalSize: Long = 0
runCatching {
val contentResolver = this.contentResolver
// 定义查询的Uri和列
val uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Video.Media._ID, MediaStore.Video.Media.SIZE)
// 执行查询
val cursor = contentResolver.query(uri, projection, null, null, null)
// 检查cursor是否包含数据
count = cursor?.count ?: 0
while (cursor?.moveToNext() == true) {
totalSize += cursor.getInt(cursor.getColumnIndex(MediaStore.Video.Media.SIZE))
}
// 关闭cursor
cursor?.close()
}
return Pair(count, totalSize)
}
@SuppressLint("Range")
fun Context.getMediaAudioCountSize(): Pair<Int, Long> {
var count = 0
var totalSize: Long = 0
runCatching {
val contentResolver = this.contentResolver
// 定义查询的Uri和列
val uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.SIZE,
)
// 执行查询
val cursor = contentResolver.query(uri, projection, null, null, null)
// 检查cursor是否包含数据
count = cursor?.count ?: 0
while (cursor?.moveToNext() == true) {
totalSize += cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE))
}
// 关闭cursor
cursor?.close()
}
return Pair(count, totalSize)
}
fun Context.getMediaDocumentCountSize(): Int {
var count = 0
runCatching {
val contentResolver = this.contentResolver
val mimeTypeSelectionArgs = arrayOf(
MIME_TYPE_PDF,
MIME_TYPE_DOC,
MIME_TYPE_DOCX,
MIME_TYPE_XLS,
MIME_TYPE_XLSX,
MIME_TYPE_PPT,
MIME_TYPE_PPTX,
)
val mimeTypeSelection =
"${MediaStore.Files.FileColumns.MIME_TYPE} IN (" + mimeTypeSelectionArgs.joinToString(",") { "?" } + ")"
// 定义查询的Uri和列
val uri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(MediaStore.Files.FileColumns._ID)
// 执行查询
val cursor = contentResolver.query(uri, projection, mimeTypeSelection, mimeTypeSelectionArgs, null)
// 检查cursor是否包含数据
count = cursor?.count ?: 0
// 关闭cursor
cursor?.close()
}
return count
}
fun Context.getMediaApkCountSize(): Int {
var count = 0
runCatching {
val contentResolver = this.contentResolver
val mimeTypeSelectionArgs = arrayOf(
MIME_TYPE_APK
)
val mimeTypeSelection = "${MediaStore.Files.FileColumns.MIME_TYPE} = ?"
// 定义查询的Uri和列
val uri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(MediaStore.Files.FileColumns._ID)
// 执行查询
val cursor = contentResolver.query(uri, projection, mimeTypeSelection, mimeTypeSelectionArgs, null)
// 检查cursor是否包含数据
count = cursor?.count ?: 0
// 关闭cursor
cursor?.close()
}
return count
}
fun Context.getMediaZipCountSize(): Int {
var count = 0
runCatching {
val contentResolver = this.contentResolver
val mimeTypeSelectionArgs = arrayOf(
MIME_TYPE_ZIP
)
val mimeTypeSelection = "${MediaStore.Files.FileColumns.MIME_TYPE} = ?"
// 定义查询的Uri和列
val uri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(MediaStore.Files.FileColumns._ID)
// 执行查询
val cursor = contentResolver.query(uri, projection, mimeTypeSelection, mimeTypeSelectionArgs, null)
// 检查cursor是否包含数据
count = cursor?.count ?: 0
// 关闭cursor
cursor?.close()
}
return count
}
private fun getFileTypeSelection(fileSuffix: String): String {
return (MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_NONE + " AND "
+ MediaStore.Files.FileColumns.DATA + " LIKE '%" + fileSuffix + "'")
}
fun CoroutineScope.queryFiles(context: Context, fileSuffix: String, flow: MutableSharedFlow<String>? = null) =
async(Dispatchers.IO) {
val selection = getFileTypeSelection(fileSuffix)
val fileList: MutableList<File> = ArrayList()
val projection = arrayOf(
MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DATA,
)
val sortOrder = MediaStore.Files.FileColumns._ID + " DESC"
val queryUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
MediaStore.Files.getContentUri("external")
}
val cursor = context.contentResolver.query(queryUri, projection, selection, null, sortOrder)
if (cursor != null) {
while (cursor.moveToNext()) {
delay(150)
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
flow?.emit(path)
fileList.add(File(path))
}
cursor.close()
}
fileList
}
fun queryFiles(context: Context, fileSuffix: String): MutableList<File> {
val selection = getFileTypeSelection(fileSuffix)
val fileList: MutableList<File> = ArrayList()
val projection = arrayOf(
MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DATA,
)
val sortOrder = MediaStore.Files.FileColumns._ID + " DESC"
val queryUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
MediaStore.Files.getContentUri("external")
}
val cursor = context.contentResolver.query(queryUri, projection, selection, null, sortOrder)
if (cursor != null) {
while (cursor.moveToNext()) {
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
fileList.add(File(path))
}
cursor.close()
}
return fileList
}
package com.base.pdfviewerscannerwhite.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import com.tom_roush.pdfbox.pdmodel.PDDocument
import com.tom_roush.pdfbox.rendering.ImageType
import com.tom_roush.pdfbox.rendering.PDFRenderer
import com.tom_roush.pdfbox.rendering.RenderDestination
import java.io.File
object PdfUtils {
private val TAG = "PdfUtils"
fun getPdfDrawables(context: Context, filePath: String, scale: Float = 1f): List<Drawable> {
val drawableList = arrayListOf<Drawable>()
val document = PDDocument.load(File(filePath))
try {
val renderer = PDFRenderer(document)
for (pageIndex in 0..document.numberOfPages) {
val startTime = System.currentTimeMillis()
val bitmap: Bitmap = renderer.renderImage(pageIndex, scale, ImageType.RGB, RenderDestination.EXPORT)
val byteCount = bitmap.byteCount
val endTime = System.currentTimeMillis()
LogEx.logDebug(TAG, "$pageIndex byteCount=$byteCount time=${endTime - startTime}")
val drawable = BitmapDrawable(context.resources, bitmap)
drawableList.add(drawable)
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
document.close()
}
return drawableList
}
fun getNumberOfPages(filePath: String): Int {
val document = PDDocument.load(File(filePath))
return document.numberOfPages
}
fun getPdfDrawablePage(
context: Context,
filePath: String,
pageIndex: Int,
scale: Float = 1f
): Drawable {
val document = PDDocument.load(File(filePath))
val renderer = PDFRenderer(document)
val bitmap: Bitmap = renderer.renderImage(pageIndex, scale, ImageType.RGB, RenderDestination.EXPORT)
val drawable = BitmapDrawable(context.resources, bitmap)
return drawable
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.utils
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.Settings
import androidx.core.app.ActivityCompat
object PermissionUtils {
fun Context.checkStorePermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
val readPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
val writePermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
return readPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED;
}
}
fun Context.requestStorePermission(
launcher: ActivityLauncher,
jumpAction: (() -> Unit)? = null,
result: (flag: Boolean) -> Unit
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val intent =
Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${packageName}")
jumpAction?.invoke()
launcher.launch(intent) {
result.invoke(checkStorePermission())
}
} else {
launcher.launch(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { map ->
result(map.values.all { it })
}
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.utils
import android.content.Context
import android.widget.Toast
object ToastUtils {
fun Context.toast(content: String) {
Toast.makeText(this, content, Toast.LENGTH_SHORT).show()
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.utils
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
object XmlEx {
fun Int.inflate(parent: ViewGroup, attachToRoot: Boolean = false): View {
return LayoutInflater.from(parent.context).inflate(this, parent, attachToRoot)
}
fun Int.inflate(context: Context, attachToRoot: Boolean = false): View {
return LayoutInflater.from(context).inflate(this, null, attachToRoot)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#C2C5CC" android:state_selected="false" />
<item android:color="#00B8DE" android:state_selected="true" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/tab_bookmark_n" android:state_selected="false" />
<item android:drawable="@mipmap/tab_bookmark_s" android:state_selected="true" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/tab_doc_n" android:state_selected="false" />
<item android:drawable="@mipmap/tab_doc_s" android:state_selected="true" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/tab_recent_n" android:state_selected="false" />
<item android:drawable="@mipmap/tab_recent_s" android:state_selected="true" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/tab_tool_n" android:state_selected="false" />
<item android:drawable="@mipmap/tab_tool_s" android:state_selected="true" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#CACACA">
<item
android:id="@android:id/mask"
android:width="110dp"
android:height="110dp"
android:gravity="center">
<shape android:shape="rectangle">
<solid android:color="#CACACA" />
<corners android:radius="110dp" />
</shape>
</item>
</ripple>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#99FAAB85">
<item
android:id="@android:id/mask"
android:width="110dp"
android:height="110dp"
android:gravity="center">
<shape android:shape="rectangle">
<solid android:color="#99FAAB85" />
<corners android:radius="110dp" />
</shape>
</item>
</ripple>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/white" />
<!-- <item-->
<!-- android:top="130dp"-->
<!-- android:gravity="top|center_horizontal">-->
<!-- <bitmap-->
<!-- android:src="@mipmap/qdylogo" />-->
<!-- </item>-->
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="60dp"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_tittle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:text="Document"
android:textColor="@color/black"
android:textSize="19sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<ImageView
android:id="@+id/iv_paixu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:src="@mipmap/h_paixu"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/iv_xuanze"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_xuanze"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:src="@mipmap/h_xuanze"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/iv_search"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:src="@mipmap/h_sousuo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/ll_bottom"
app:layout_constraintTop_toBottomOf="@id/cl_top" />
<ImageView
android:id="@+id/iv_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
android:layout_marginBottom="50dp"
android:src="@mipmap/saoyisao"
app:layout_constraintBottom_toTopOf="@id/ll_bottom"
app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tv_debug_log"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ll_bottom"
android:layout_width="match_parent"
android:layout_height="64dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent">
<LinearLayout
android:id="@+id/ll_document"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginHorizontal="5dp"
android:layout_weight="1"
android:background="@drawable/ripple_normal"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:splitMotionEvents="true"
app:layout_constraintEnd_toStartOf="@id/ll_recent"
app:layout_constraintStart_toStartOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="9dp"
android:src="@drawable/bg_selector_document"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2.5dp"
android:text="document"
android:textColor="@color/color_tab_selector"
android:textSize="11sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_recent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginHorizontal="5dp"
android:layout_weight="1"
android:background="@drawable/ripple_normal"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
app:layout_constraintEnd_toStartOf="@id/ll_bookmark"
app:layout_constraintStart_toEndOf="@id/ll_document">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="9dp"
android:src="@drawable/bg_selector_recent"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2.5dp"
android:text="Recent"
android:textColor="@color/color_tab_selector"
android:textSize="11sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_bookmark"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginHorizontal="5dp"
android:layout_weight="1"
android:background="@drawable/ripple_normal"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:splitMotionEvents="false"
app:layout_constraintEnd_toStartOf="@id/ll_tool"
app:layout_constraintStart_toEndOf="@id/ll_recent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="9dp"
android:src="@drawable/bg_selector_bookmark"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2.5dp"
android:text="Bookmark"
android:textColor="@color/color_tab_selector"
android:textSize="11sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_tool"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginHorizontal="5dp"
android:layout_weight="1"
android:background="@drawable/ripple_normal"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:splitMotionEvents="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ll_bookmark">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="9dp"
android:src="@drawable/bg_selector_bookmark"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="2.5dp"
android:text="Bookmark"
android:textColor="@color/color_tab_selector"
android:textSize="11sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".ui.pdf.PdfActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.splash.SplashActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.pdf.PdfFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_docment" />
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="15dp"
android:layout_marginStart="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/h_pdfiocn" />
<FrameLayout
android:id="@+id/fl_more"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginEnd="5dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@mipmap/h_genduo"
tools:ignore="ContentDescription" />
</FrameLayout>
<FrameLayout
android:id="@+id/fl_bookmark"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginEnd="5dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/fl_more"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv_bookmark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/h_soucang_n"
tools:ignore="ContentDescription" />
</FrameLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="5dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/fl_bookmark"
app:layout_constraintStart_toEndOf="@id/iv"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:includeFontPadding="false"
android:singleLine="true"
android:text="DEMO.pdf"
android:textSize="17sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:includeFontPadding="false"
android:textColor="#999999"
android:textSize="14sp"
tools:text="2024-09-10 590.23 KB" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<resources>
<dimen name="fab_margin">48dp</dimen>
</resources>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.PDFViewerScannerWhite" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.PDFViewerScannerWhite" parent="Base.Theme.PDFViewerScannerWhite">
<!-- Transparent system bars for edge-to-edge. -->
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
</style>
</resources>
\ No newline at end of file
<resources>
<dimen name="fab_margin">200dp</dimen>
</resources>
\ No newline at end of file
<resources>
<dimen name="fab_margin">48dp</dimen>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>
\ No newline at end of file
<resources>
<dimen name="fab_margin">16dp</dimen>
</resources>
\ No newline at end of file
<resources>
<string name="app_name">PDF Viewer Scanner White</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<!-- Strings used for fragments for navigation -->
</resources>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.PDFViewerScannerWhite" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.PDFViewerScannerWhite" parent="Base.Theme.PDFViewerScannerWhite" />
<style name="splash.theme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowBackground">@drawable/splash_bp</item>
<item name="android:windowFullscreen">false</item>
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache_files" path="."/>
</paths>
package com.base.pdfviewerscannerwhite
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.jetbrainsKotlinAndroid) apply false
}
\ No newline at end of file
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
\ No newline at end of file
[versions]
agp = "8.3.1"
kotlin = "1.8.0"
coreKtx = "1.8.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
appcompat = "1.6.1"
material = "1.10.0"
activity = "1.8.0"
constraintlayout = "2.1.4"
navigationFragmentKtx = "2.6.0"
navigationUiKtx = "2.6.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
#Tue Sep 10 10:46:11 CST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# 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.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
pluginManagement {
repositories {
maven("https://maven.aliyun.com/nexus/content/groups/public/")
maven("https://maven.aliyun.com/repository/public")
maven("https://maven.aliyun.com/repository/central")
maven("https://maven.aliyun.com/repository/google")
maven("https://maven.aliyun.com/repository/gradle-plugin")
maven("https://www.jitpack.io")
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
maven("https://android-sdk.is.com")
// maven("https://maven.aliyun.com/nexus/content/groups/public/")
// maven("https://maven.aliyun.com/repository/public")
// maven("https://maven.aliyun.com/repository/central")
// maven("https://maven.aliyun.com/repository/google")
// maven("https://maven.aliyun.com/repository/gradle-plugin")
// maven("https://www.jitpack.io")
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
maven("https://maven.aliyun.com/nexus/content/groups/public/")
maven("https://maven.aliyun.com/repository/public")
maven("https://maven.aliyun.com/repository/central")
maven("https://maven.aliyun.com/repository/google")
maven("https://maven.aliyun.com/repository/gradle-plugin")
maven("https://www.jitpack.io")
google()
mavenCentral()
maven("https://android-sdk.is.com")
// maven("https://maven.aliyun.com/nexus/content/groups/public/")
// maven("https://maven.aliyun.com/repository/public")
// maven("https://maven.aliyun.com/repository/central")
// maven("https://maven.aliyun.com/repository/google")
// maven("https://maven.aliyun.com/repository/gradle-plugin")
// maven("https://www.jitpack.io")
}
}
rootProject.name = "PDF Viewer Scanner White"
include(":app")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment