Commit c8da9857 authored by wanglei's avatar wanglei

初始化

parent be00b9a9
Pipeline #1287 canceled with stages
*.iml
.gradle
/local.properties
/.idea
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
/build
\ No newline at end of file
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
}
android {
namespace = "com.base.pdfreader2"
compileSdk = 34
defaultConfig {
applicationId = "com.base.pdfreader2"
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
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.swiperefreshlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
//第三方UI
implementation("io.github.cymchad:BaseRecyclerViewAdapterHelper4:4.1.4")
implementation("com.github.angcyo.DslTablayout:TabLayout:3.5.5")
implementation("com.github.angcyo.DslTablayout:ViewPager2Delegate:3.5.5")
implementation("com.airbnb.android:lottie:6.4.0")
implementation("com.github.pokercc:ExpandableRecyclerView:0.9.3")
//Pdf库
implementation("com.tom-roush:pdfbox-android:2.0.27.0")
}
\ 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
\ No newline at end of file
package com.base.pdfreader2
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.pdfreader2", 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" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".helper.MyApplication"
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:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PdfReader2"
tools:targetApi="31">
<activity
android:name=".ui.splash.SplashActivity"
android:exported="false" />
<activity
android:name=".ui.main.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
package com.base.pdfreader2.bean
import com.base.pdfreader2.utils.AppPreferences
import java.util.Locale
object ConstObject {
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"
const val DOCUMENT_DATA_TYPE = "document"
const val RECENT_DATA_TYPE = "recent"
const val BOOKMARK_DATA_TYPE = "bookmark"
var haveSaveDemo = false
get() {
return AppPreferences.getInstance().getBoolean("haveSaveDemo", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("haveSaveDemo", value, true)
}
var modeNight = false
get() {
return AppPreferences.getInstance().getBoolean("modeNight", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("modeNight", value, true)
}
var appLanguageSp = Locale.ENGLISH.language
get() {
return AppPreferences.getInstance().getString("appLanguageSp", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("appLanguageSp", value, true)
}
var changeLanguageSp = appLanguageSp
get() {
return AppPreferences.getInstance().getString("changeLanguageSp", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("changeLanguageSp", value, true)
}
var topic_number = ""
get() {
return AppPreferences.getInstance().getString("topic_number", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("topic_number", value, true)
}
}
\ No newline at end of file
package com.base.pdfreader2.bean
import android.net.Uri
data class DocumentBean(
var path: String = "",
var uri: Uri = Uri.EMPTY,
var type: String = "",
var isBookmarked: Boolean = false,
) {
var uiType: Int = 0//0首页模式 1合并选择模式 2拆分模式 3解锁加锁模式 4搜索模式
var isSelect: Boolean = false
var state: Int = -1//0正常状态 1 锁定
var password: String = ""
var lastViewTime: Long = 0
var isAd: Boolean = false
companion object {
const val TYPE_PDF = "type_pdf"
const val TYPE_WORD = "type_word"
const val TYPE_EXCEL = "type_excel"
const val TYPE_PPT = "type_ppt"
}
}
package com.base.pdfreader2.bean
import android.net.Uri
data class MediaBean(
val path: String = "",
val uri: Uri = Uri.EMPTY,
val mimeType: String = ""
)
package com.base.pdfreader2.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.pdfreader2.ui.main.MainActivity
import com.base.pdfreader2.bean.ConstObject.appLanguageSp
import com.base.pdfreader2.bean.ConstObject.changeLanguageSp
import com.base.pdfreader2.utils.ActivityLauncher
import com.base.pdfreader2.utils.ActivityManagerUtils
import com.base.pdfreader2.utils.LanguageUtils.changeAppLanguage
import com.base.pdfreader2.utils.LogEx
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
private val TAG = "BaseActivity"
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
}
}
override fun onResume() {
super.onResume()
}
fun updateAppLanguage(
activityLanguage: String,
activityTag: String? = null,
activityChangeCallback: ((language: String) -> Unit)? = null
): Boolean {
LogEx.logDebug(
TAG,
"activityTag=$activityTag " +
"appLanguageSp=$appLanguageSp " +
"changeLanguageSp=$changeLanguageSp " +
"activityLanguage=$activityLanguage"
)
var isRecreate = false
if (appLanguageSp != changeLanguageSp || activityLanguage != changeLanguageSp) {
changeAppLanguage(this, changeLanguageSp)
// Restart the app
appLanguageSp = changeLanguageSp
activityChangeCallback?.invoke(changeLanguageSp)
LogEx.logDebug(TAG, "activityTag=$activityTag recreate")
isRecreate = true
//https://stackoverflow.com/questions/63209993/getting-lifecycle-exception-while-recreating-the-activity
//MIUI recreate
this@BaseActivity.recreate()
}
return isRecreate
}
fun checkNeedRecreate(activityLanguage: String): Boolean {
return appLanguageSp != changeLanguageSp || activityLanguage != changeLanguageSp
}
}
\ No newline at end of file
package com.base.pdfreader2.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 var isInitialized = false
protected abstract val binding: T
private val TAG = "BaseFragment"
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()
isInitialized = true
}
protected abstract fun setView()
protected open fun setListener() {}
}
\ No newline at end of file
package com.base.pdfreader2.helper
object ConfigHelper {
const val privacyPolicy: String = "https://sites.google.com/view/pdf-reader-scan/pdf-reader"
const val termService: String = "https://sites.google.com/view/term-of-service-s/term-of-service"
// 正式包名
const val packageName = "com.base.pdfreader2"
// 域名
const val eventUrl = "https://"
const val apiUrl = "https://"
//admob test id
const val openAdmobIdTest = "ca-app-pub-3940256099942544/9257395921"
const val bannerAdmobIdTest = "ca-app-pub-3940256099942544/9214589741"
const val interAdmobIdTest = "ca-app-pub-3940256099942544/1033173712"
const val nativeAdmobIdTest = "ca-app-pub-3940256099942544/2247696110"
// admob广告id
const val interAdmobId = "ca-app-pub-3940256099942544/1033173111"
const val nativeAdmobId = "ca-app-pub-3940256099942544/2247696111"
const val openAdmobId = "/6499/example/app-open"
const val bannerAdmobId = "ca-app-pub-3940256099942544/9214581111"
val noLoadingActivities = listOf(
"full", // 过滤全屏广告
"adActivity",
"AdActivity",
"AppLovinFullscreenActivity",
// SplashActivity::class.java.simpleName,
"GmsDocumentScanningDelegateActivity",
// com.google.mlkit.vision.documentscanner.internal.GmsDocumentScanningDelegateActivity
// 返回前台时不跳转启动页的 activity
)
}
\ No newline at end of file
package com.base.pdfreader2.helper
import android.app.Activity
import android.app.Application
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import com.base.pdfreader2.bean.ConstObject.topic_number
import com.base.pdfreader2.ui.splash.SplashActivity
import com.base.pdfreader2.utils.ActivityManagerUtils
import com.base.pdfreader2.utils.AppPreferences
import com.base.pdfreader2.utils.KotlinExt.toFormatMinute
import com.base.pdfreader2.utils.LogEx
import java.util.Locale
import java.util.UUID
class MyApplication : Application() {
private val TAG = "MyApplication"
var uuid = ""
companion object {
lateinit var context: MyApplication
var splashLanguage: String = Locale.getDefault().language
var mainLanguage: String = Locale.getDefault().language
var setLanguage: String = Locale.getDefault().language
var feedbackLanguage: String = Locale.getDefault().language
var pptLanguage: String = Locale.getDefault().language
var pdfLanguage: String = Locale.getDefault().language
var pdfLoadingLanguage: String = Locale.getDefault().language
var pdfMergeLanguage: String = Locale.getDefault().language
var pdfSelectLanguage: String = Locale.getDefault().language
var pdfSplitLanguage: String = Locale.getDefault().language
var wordLanguage: String = Locale.getDefault().language
@JvmField
var PAUSED_VALUE = 0
}
override fun onCreate() {
super.onCreate()
context = this
initUUid()
initApp()
// PDFBoxResourceLoader.init(applicationContext)
// initWeather()
}
// private fun initWeather() {
// requestWeatherData()
// }
private fun initUUid() {
uuid = AppPreferences.getInstance().getString("uuid", "")
if (TextUtils.isEmpty(uuid)) {
uuid = UUID.randomUUID().toString() + System.currentTimeMillis()
AppPreferences.getInstance().put("uuid", uuid)
LogEx.logDebug(TAG, "uuid=$uuid")
}
}
fun initApp() {
// SolarEngineManager.getInstance().preInit(context, "81a11caa4076cd7c")
// FacebookSdk.sdkInitialize(applicationContext)
var topicNumber = System.currentTimeMillis().toFormatMinute()
LogEx.logDebug(TAG, "topicNumber=$topicNumber")
if (topic_number.isNotEmpty()) {
topicNumber = topic_number
} else {
topic_number = topicNumber
}
val topic = ConfigHelper.packageName + "_push_$topicNumber"
LogEx.logDebug(TAG, "topic=${topic}")
// FCMManager.initFirebase(this)
// FCMManager.subscribeToTopic(topic)
// InstallHelps.init()
initLifeListener()
// ScreenStatusReceiver.setupScreenStatusListener(this)
}
// fun initSolarEngine(gdprDeny: Boolean = false) {
//
// val configBuilder = SolarEngineConfig.Builder()
//
// if (BuildConfig.DEBUG) {
// configBuilder.logEnabled() //开启本地调试日志
// }
// if (gdprDeny) {
// configBuilder.isGDPRArea = true
// configBuilder.adPersonalizationEnabled = true
// configBuilder.adUserDataEnabled = true
// }
// configBuilder.isCoppaEnabled = true
// configBuilder.setKidsAppEnabled(true)
// configBuilder.fbAppID = ""
//
// val config = configBuilder.build()
//
// SolarEngineManager.getInstance().initialize(context, "81a11caa4076cd7c", config,
// OnInitializationCallback { code ->
// if (code == 0) {
// //初始化成功
// config.setOnAttributionListener(object : OnAttributionListener {
// override fun onAttributionSuccess(attribution: JSONObject) {
// //获取归因结果成功时执行的动作
// LogEx.logDebug(TAG, "attribution=$attribution")
// EventUtils.event("install_referrer", attribution.toString())
// }
//
// override fun onAttributionFail(errorCode: Int) {
// //获取归因结果失败时执行的动作
// EventUtils.event("SolarEngineManager onAttributionFail errorCode=$errorCode")
// }
// })
// } else {
// //初始化失败,具体失败原因参考下方code码释义
// EventUtils.event("SolarEngineManager init error code=$code")
// }
// })
// }
private var lastTimePause = 0L
private var lastTimeResume = 0L
private fun isHotLaunch(): Boolean {
if ((lastTimeResume - lastTimePause) > 5000) {
return true
}
return false
}
private fun initLifeListener() {
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
private var count = 0
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
ActivityManagerUtils.getInstance().addActivity(activity)
}
override fun onActivityStarted(activity: Activity) {
count++
lastTimeResume = System.currentTimeMillis()
if (count == 1 && isHotLaunch()) {
val topActivity: Activity? = ActivityManagerUtils.getInstance().topActivity
val flag = if (topActivity == null) {
true
} else {
ConfigHelper.noLoadingActivities.all { !topActivity.localClassName.contains(it, true) }
}
LogEx.logDebug(TAG, "flag=$flag" + " activity:" + activity.localClassName)
if (flag) {
topActivity?.startActivity(
Intent(
topActivity, SplashActivity::class.java
).apply {
putExtra("isHotLaunch", true)
putExtra("type", -1)
})
}
}
lastTimeResume = 0
}
override fun onActivityResumed(activity: Activity) {
// LogEx.logDebug(TAG, "onActivityResumed")
PAUSED_VALUE = 1
}
override fun onActivityPaused(activity: Activity) {
// LogEx.logDebug(TAG, "onActivityPaused")
PAUSED_VALUE = 2
lastTimePause = System.currentTimeMillis()
}
override fun onActivityStopped(activity: Activity) {
count--
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {
ActivityManagerUtils.getInstance().removeActivity(activity)
}
})
}
}
\ No newline at end of file
package com.base.pdfreader2.ui.main
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.pdfreader2.R
import com.base.pdfreader2.bean.DocumentBean
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_EXCEL
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_PDF
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_PPT
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_WORD
import com.base.pdfreader2.databinding.ItemAdBinding
import com.base.pdfreader2.databinding.ItemDocumentBinding
import com.base.pdfreader2.utils.KotlinExt.toFormatSize
import com.base.pdfreader2.utils.KotlinExt.toFormatTime
import com.base.pdfreader2.utils.XmlEx.inflate
import com.chad.library.adapter4.BaseQuickAdapter
import java.io.File
class DocumentAdapter() : BaseQuickAdapter<DocumentBean, DocumentAdapter.DocumentViewHolder>() {
private val TAG = "DocumentAdapter"
inner class DocumentViewHolder(view: View) : ViewHolder(view)
var itemClick: ((item: DocumentBean) -> Unit)? = null
var bookmarkAction: ((addRemove: Boolean, path: String) -> Unit)? = null
var moreAction: ((item: DocumentBean) -> Unit)? = null
var selectAction: ((size: Int) -> Unit)? = null
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: DocumentViewHolder, position: Int, item: DocumentBean?) {
if (item == null) return
if (item.isAd) {
val binding = ItemAdBinding.bind(holder.itemView)
// AdmobNativeUtils.showNativeAd(activity, binding.flAd, R.layout.layout_admob_document)
} else {
val binding = ItemDocumentBinding.bind(holder.itemView)
changeIcon(item, binding)
val file = File(item.path)
runCatching {
binding.tvName.text = file.name
binding.tvInfo.text = file.lastModified().toFormatTime() + " " + file.length().toFormatSize()
}
when (item.uiType) {
0 -> {//首页
binding.flSelect.visibility = View.GONE
binding.flMore.visibility = View.VISIBLE
binding.flBookmark.visibility = View.VISIBLE
if (item.isBookmarked) {
binding.ivBookmark.setImageResource(R.mipmap.h_soucang_s)
} else {
binding.ivBookmark.setImageResource(R.mipmap.h_soucang_n)
}
binding.flBookmark.setOnClickListener {
if (item.isBookmarked) {
bookmarkAction?.invoke(false, item.path)
} else {
bookmarkAction?.invoke(true, item.path)
}
item.isBookmarked = !item.isBookmarked
notifyItemChanged(position, "aaaa")
}
binding.flMore.setOnClickListener {
moreAction?.invoke(item)
}
binding.root.setOnClickListener {
itemClick?.invoke(item)
}
}
1 -> {//合并选择
binding.flMore.visibility = View.INVISIBLE
binding.flBookmark.visibility = View.INVISIBLE
binding.flSelect.visibility = View.VISIBLE
binding.ivSelector.isSelected = item.isSelect
binding.flSelect.setOnClickListener {
item.isSelect = !item.isSelect
notifyItemChanged(position, "aaa")
selectAction?.invoke(items.filter { it.isSelect }.size)
}
binding.root.setOnClickListener {
item.isSelect = !item.isSelect
notifyItemChanged(position, "aaa")
selectAction?.invoke(items.filter { it.isSelect }.size)
}
}
2 -> {//拆分选择
binding.flBookmark.visibility = View.GONE
binding.flMore.visibility = View.INVISIBLE
binding.root.setOnClickListener {
itemClick?.invoke(item)
}
}
3 -> {//解锁模式选择
binding.flBookmark.visibility = View.GONE
binding.flMore.visibility = View.INVISIBLE
binding.root.setOnClickListener {
itemClick?.invoke(item)
}
}
4 -> {//搜索选择
binding.flBookmark.visibility = View.GONE
binding.flMore.setOnClickListener {
moreAction?.invoke(item)
}
binding.root.setOnClickListener {
itemClick?.invoke(item)
}
}
}
}
}
fun changeIcon(item: DocumentBean, binding: ItemDocumentBinding) {
if (item.type == TYPE_PDF) {
if (item.state == 0) {
binding.iv.setImageResource(R.mipmap.h_pdfiocn)
}
if (item.state == 1) {
binding.iv.setImageResource(R.mipmap.souding)
}
}
if (item.type == TYPE_WORD) {
binding.iv.setImageResource(R.mipmap.wordicon)
}
if (item.type == TYPE_EXCEL) {
binding.iv.setImageResource(R.mipmap.excelicon)
}
if (item.type == TYPE_PPT) {
binding.iv.setImageResource(R.mipmap.ppticon)
}
}
override fun getItemViewType(position: Int, list: List<DocumentBean>): Int {
val item = list[position]
return if (item.isAd) 1 else 0
}
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): DocumentViewHolder {
return if (viewType == 0) {
DocumentViewHolder(R.layout.item_document.inflate(parent))
} else {
DocumentViewHolder(R.layout.item_ad.inflate(parent))
}
}
}
\ No newline at end of file
package com.base.pdfreader2.ui.main
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.base.pdfreader2.bean.ConstObject
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_PDF
import com.base.pdfreader2.databinding.FragmentDocumentBinding
import com.base.pdfreader2.helper.BaseFragment
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class DocumentFragment() : BaseFragment<FragmentDocumentBinding>() {
private val TAG = "DocumentFragment"
private var type = TYPE_PDF
private lateinit var adapter: DocumentAdapter
private lateinit var documentPresenter: DocumentPresenter
override val binding: FragmentDocumentBinding by lazy {
FragmentDocumentBinding.inflate(layoutInflater)
}
constructor(type: String) : this() {
this.type = type
}
override fun setView() {
documentPresenter = DocumentPresenter(requireContext())
adapter = DocumentAdapter()
binding.rv.adapter = adapter
binding.swipeRefreshLayout.isRefreshing = true
}
override fun setListener() {
super.setListener()
binding.swipeRefreshLayout.setOnRefreshListener {
binding.swipeRefreshLayout.isRefreshing = true
refreshData()
}
}
override fun onResume() {
super.onResume()
refreshData()
}
fun refreshData() {
if (isInitialized && !isHidden && isVisible) {
val mainActivity = (requireActivity() as MainActivity?)
mainActivity ?: return
when (mainActivity.dataType) {
ConstObject.DOCUMENT_DATA_TYPE -> refreshAllData()
ConstObject.RECENT_DATA_TYPE -> refreshRecentData()
ConstObject.BOOKMARK_DATA_TYPE -> refreshBookmarkData()
}
}
}
private fun refreshRecentData() = lifecycleScope.launch(Dispatchers.IO) {
val list = documentPresenter.getRecentDocument(type)
launch(Dispatchers.Main) {
binding.llEmpty.isVisible = list.isEmpty()
adapter.submitList(list)
binding.swipeRefreshLayout.isRefreshing = false
}
}
private fun refreshBookmarkData() = lifecycleScope.launch(Dispatchers.IO) {
val list = documentPresenter.getBookmarkDocument(type)
lifecycleScope.launch(Dispatchers.Main) {
binding.llEmpty.isVisible = list.isEmpty()
adapter.submitList(list)
binding.swipeRefreshLayout.isRefreshing = false
}
}
private fun refreshAllData() = lifecycleScope.launch(Dispatchers.IO) {
val list = documentPresenter.getAllDocument(type)
launch(Dispatchers.Main) {
binding.llEmpty.isVisible = list.isEmpty()
adapter.submitList(list)
binding.swipeRefreshLayout.isRefreshing = false
}
}
}
\ No newline at end of file
package com.base.pdfreader2.ui.main
import android.content.Context
import com.base.pdfreader2.bean.ConstObject
import com.base.pdfreader2.bean.DocumentBean
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_EXCEL
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_PDF
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_PPT
import com.base.pdfreader2.bean.DocumentBean.Companion.TYPE_WORD
import com.base.pdfreader2.utils.AssetUtils.saveAssetsFile
import com.base.pdfreader2.utils.KotlinExt.toFormatTime3
import com.base.pdfreader2.utils.LogEx
import com.base.pdfreader2.utils.PdfBoxUtils.checkPdfEncryption
import com.base.pdfreader2.utils.SpStringUtils
import com.base.pdfreader2.utils.SpStringUtils.BOOKMARK_KEY
import com.base.pdfreader2.utils.SpStringUtils.LAST_VIEW_KEY
import com.base.pdfreader2.utils.getMediaFile
import com.base.pdfreader2.utils.upDateDemoStore
import java.io.File
class DocumentPresenter(
val context: Context,
) {
private val TAG = "DocumentPresenter"
/**
* 获取所有的
*/
fun getAllDocument(type: String): MutableList<DocumentBean> {
context.upDateDemoStore()
if (!ConstObject.haveSaveDemo) {
context.saveAssetsFile()
ConstObject.haveSaveDemo = true
}
when (type) {
TYPE_PDF -> {
return getPdfData()
}
TYPE_WORD -> {
return getWordData()
}
TYPE_EXCEL -> {
return getExcelData()
}
TYPE_PPT -> {
return getPptData()
}
}
return mutableListOf()
}
/**
* 获取最近的
*/
fun getRecentDocument(type: String): List<DocumentBean> {
val recentList = SpStringUtils.getSpStringList(LAST_VIEW_KEY)
val list = getAllDocument(type)
list.forEach { recentFilter(recentList, it) }
return list.filter { it.lastViewTime != 0L }.sortedByDescending { it.lastViewTime }
}
private fun recentFilter(recentList: List<String>, documentBean: DocumentBean) {
val findLastTime = recentList.filter { it.contains(documentBean.path) }.map {
it.split("_/_")[1].toLong()
}
if (findLastTime.isNotEmpty()) {
val max = findLastTime.max()
LogEx.logDebug(TAG, "recentFilter ${documentBean.path} ${max.toFormatTime3()}")
documentBean.lastViewTime = max
}
}
/**
* 获取标签的
*/
fun getBookmarkDocument(type: String): List<DocumentBean> {
val list = getAllDocument(type)
return list.filter { it.isBookmarked }
}
private fun getPdfData(): MutableList<DocumentBean> {
val demoDocumentBean = DocumentBean()
demoDocumentBean.type = TYPE_PDF
val demoFile = File(context.filesDir, "demo")
val selectionArgs = arrayOf(ConstObject.MIME_TYPE_PDF)
demoDocumentBean.path = demoFile.listFiles()?.find { it.name.contains(".pdf") }?.absolutePath ?: ""
val list = context.getMediaFile(selectionArgs = selectionArgs)
val documentList = list.map {
DocumentBean(it.path, uri = it.uri, type = TYPE_PDF)
}
val new = documentList.toMutableList()
if (File(demoDocumentBean.path).exists()) {
new.add(0, demoDocumentBean)
}
val bookmarkList = SpStringUtils.getSpStringList(BOOKMARK_KEY)
new.map {
it.state = context.checkPdfEncryption(it.path)
it.isBookmarked = bookmarkList.contains(it.path)
}
return new
}
private fun getWordData(): MutableList<DocumentBean> {
val demoDocumentBean = DocumentBean()
demoDocumentBean.type = TYPE_WORD
val demoFile = File(context.filesDir, "demo")
val selectionArgs = arrayOf(ConstObject.MIME_TYPE_DOC, ConstObject.MIME_TYPE_DOCX)
demoDocumentBean.path = demoFile.listFiles()?.find { it.name.contains(".docx") }?.absolutePath ?: ""
val list = context.getMediaFile(selectionArgs = selectionArgs)
val documentList = list.map {
DocumentBean(it.path, uri = it.uri, type = TYPE_WORD)
}
val new = documentList.toMutableList()
if (File(demoDocumentBean.path).exists()) {
new.add(0, demoDocumentBean)
}
val bookmarkList = SpStringUtils.getSpStringList(BOOKMARK_KEY)
new.map {
it.isBookmarked = bookmarkList.contains(it.path)
}
return new
}
private fun getExcelData(): MutableList<DocumentBean> {
val demoDocumentBean = DocumentBean()
demoDocumentBean.type = TYPE_EXCEL
val demoFile = File(context.filesDir, "demo")
val selectionArgs = arrayOf(ConstObject.MIME_TYPE_XLS, ConstObject.MIME_TYPE_XLSX)
demoDocumentBean.path = demoFile.listFiles()?.find { it.name.contains(".xlsx") }?.absolutePath ?: ""
val list = context.getMediaFile(selectionArgs = selectionArgs)
val documentList = list.map {
DocumentBean(it.path, uri = it.uri, type = TYPE_EXCEL)
}
val new = documentList.toMutableList()
if (File(demoDocumentBean.path).exists()) {
new.add(0, demoDocumentBean)
}
val bookmarkList = SpStringUtils.getSpStringList(BOOKMARK_KEY)
new.map {
it.isBookmarked = bookmarkList.contains(it.path)
}
return new
}
private fun getPptData(): MutableList<DocumentBean> {
val demoDocumentBean = DocumentBean()
demoDocumentBean.type = TYPE_PPT
val demoFile = File(context.filesDir, "demo")
val selectionArgs = arrayOf(ConstObject.MIME_TYPE_PPT, ConstObject.MIME_TYPE_PPTX)
demoDocumentBean.path = demoFile.listFiles()?.find { it.name.contains(".pptx") }?.absolutePath ?: ""
val list = context.getMediaFile(selectionArgs = selectionArgs)
val documentList = list.map {
DocumentBean(it.path, uri = it.uri, type = TYPE_PPT)
}
val new = documentList.toMutableList()
if (File(demoDocumentBean.path).exists()) {
new.add(0, demoDocumentBean)
}
val bookmarkList = SpStringUtils.getSpStringList(BOOKMARK_KEY)
new.map {
it.isBookmarked = bookmarkList.contains(it.path)
}
return new
}
}
\ No newline at end of file
package com.base.pdfreader2.ui.main
import android.graphics.Color
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.angcyo.tablayout.delegate2.ViewPager2Delegate
import com.base.pdfreader2.R
import com.base.pdfreader2.bean.ConstObject
import com.base.pdfreader2.bean.ConstObject.BOOKMARK_DATA_TYPE
import com.base.pdfreader2.bean.ConstObject.DOCUMENT_DATA_TYPE
import com.base.pdfreader2.bean.ConstObject.RECENT_DATA_TYPE
import com.base.pdfreader2.bean.DocumentBean
import com.base.pdfreader2.databinding.ActivityMainBinding
import com.base.pdfreader2.helper.BaseActivity
import com.base.pdfreader2.utils.BarUtils
import com.base.pdfreader2.utils.PermissionUtils.checkStorePermission
import com.base.pdfreader2.utils.PermissionUtils.requestStoragePermission
class MainActivity : BaseActivity<ActivityMainBinding>() {
override val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val pdfPage: DocumentFragment by lazy {
DocumentFragment(DocumentBean.TYPE_PDF)
}
private val wordPage: DocumentFragment by lazy {
DocumentFragment(DocumentBean.TYPE_WORD)
}
private val excelPage: DocumentFragment by lazy {
DocumentFragment(DocumentBean.TYPE_EXCEL)
}
private val pptPage: DocumentFragment by lazy {
DocumentFragment(DocumentBean.TYPE_PPT)
}
private val fragments by lazy {
mutableListOf(pdfPage, wordPage, excelPage, pptPage)
}
var currentPage: DocumentFragment = pdfPage
var dataType: String = DOCUMENT_DATA_TYPE
private lateinit var pageAdapter: FragmentStateAdapter
override fun initView() {
BarUtils.setStatusBarLightMode(this, !ConstObject.modeNight)
if (ConstObject.modeNight) {
BarUtils.setStatusBarColor(this, Color.BLACK)
} else {
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
}
// binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
// AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
if (!checkStorePermission()) {
requestStoragePermission(launcher) {}
}
initViewPager2()
initTabLayout()
binding.llDocument.isSelected = true
}
override fun initListener() {
super.initListener()
binding.llDocument.setOnClickListener {
dataType = DOCUMENT_DATA_TYPE
changeDocumentUI()
currentPage.refreshData()
}
binding.llRecent.setOnClickListener {
dataType = RECENT_DATA_TYPE
changeRecentUI()
currentPage.refreshData()
}
binding.llBookmark.setOnClickListener {
dataType = BOOKMARK_DATA_TYPE
changeBookmarkUI()
currentPage.refreshData()
}
binding.llTool.setOnClickListener {
changeToolUI()
}
}
private fun disSelectBottomNav() {
binding.llDocument.isSelected = false
binding.llRecent.isSelected = false
binding.llBookmark.isSelected = false
binding.llTool.isSelected = false
}
private fun changeDocumentUI() {
disSelectBottomNav()
binding.llDocument.isSelected = true
binding.flTool.visibility = View.INVISIBLE
binding.clDocument.visibility = View.VISIBLE
}
private fun changeRecentUI() {
disSelectBottomNav()
binding.llRecent.isSelected = true
binding.flTool.visibility = View.INVISIBLE
binding.clDocument.visibility = View.VISIBLE
}
private fun changeBookmarkUI() {
disSelectBottomNav()
binding.llBookmark.isSelected = true
binding.flTool.visibility = View.INVISIBLE
binding.clDocument.visibility = View.VISIBLE
}
private fun changeToolUI() {
disSelectBottomNav()
binding.llTool.isSelected = true
binding.clDocument.visibility = View.INVISIBLE
binding.flTool.visibility = View.VISIBLE
}
private fun initViewPager2() {
pageAdapter = object : FragmentStateAdapter(this) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
binding.viewPager2.run {
isUserInputEnabled = true
//https://www.jianshu.com/p/f69bd30cf5b0
//FragmentStateAdapter 这里必须传人fragment
adapter = pageAdapter
}
binding.viewPager2.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
if (position == 3 && positionOffset > 0) {
binding.viewPager2.setCurrentItem(position, false)
}
}
override fun onPageSelected(position: Int) {
currentPage = fragments[position]
}
})
}
private fun initTabLayout() {
val colorPdf = ContextCompat.getColor(this, R.color.color_de202a)
val colorWord = ContextCompat.getColor(this, R.color.color_4f8df4)
val colorExcel = ContextCompat.getColor(this, R.color.color_1ea362)
val colorPpt = ContextCompat.getColor(this, R.color.color_f6b911)
val deselectColor = ContextCompat.getColor(this, R.color.color_80ffffff)
binding.tabLayoutDocument.apply {
configTabLayoutConfig {
tabSelectColor = Color.WHITE
tabDeselectColor = deselectColor
onSelectIndexChange = { fromIndex, selectIndexList, reselect, fromUser ->
val toIndex = selectIndexList.first()
binding.viewPager2.currentItem = toIndex
when (toIndex) {
0 -> {
binding.clTop.setBackgroundColor(colorPdf)
}
1 -> {
binding.clTop.setBackgroundColor(colorWord)
}
2 -> {
binding.clTop.setBackgroundColor(colorExcel)
}
3 -> {
binding.clTop.setBackgroundColor(colorPpt)
}
}
currentPage.refreshData()
}
dslSelector.updateStyle()
}
}
ViewPager2Delegate.install(binding.viewPager2, binding.tabLayoutDocument)
}
}
\ No newline at end of file
package com.base.pdfreader2.ui.main
import com.base.pdfreader2.databinding.FragmentToolBinding
import com.base.pdfreader2.helper.BaseFragment
class ToolFragment : BaseFragment<FragmentToolBinding>() {
override val binding: FragmentToolBinding by lazy {
FragmentToolBinding.inflate(layoutInflater)
}
override fun setView() {
}
}
\ No newline at end of file
package com.base.pdfreader2.ui.splash
import android.annotation.SuppressLint
import android.content.Intent
import com.base.pdfreader2.databinding.ActivitySplashBinding
import com.base.pdfreader2.helper.BaseActivity
import com.base.pdfreader2.ui.main.MainActivity
@SuppressLint("CustomSplashScreen")
class SplashActivity : BaseActivity<ActivitySplashBinding>() {
override val binding: ActivitySplashBinding by lazy {
ActivitySplashBinding.inflate(layoutInflater)
}
override fun initView() {
startActivity(Intent(this, MainActivity::class.java))
}
}
\ No newline at end of file
package com.base.pdfreader2.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.pdfreader2.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.pdfreader2.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import com.base.pdfreader2.helper.MyApplication;
import java.util.Set;
/**
* @noinspection ALL, rawtypes, rawtypes
*/
public class AppPreferences {
private static AppPreferences sInstance;
private final SharedPreferences sharedPreferences;
private static final String PREF_FILE_NAME = "app_prefs"; // 偏好文件名
private AppPreferences(Context context) {
sharedPreferences = context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE);
}
public static synchronized AppPreferences getInstance() {
if (sInstance == null) {
sInstance = new AppPreferences(MyApplication.context.getApplicationContext());
}
return sInstance;
}
// 通用 put 方法
public void put(String key, Object value) {
if (value instanceof Integer) {
sharedPreferences.edit().putInt(key, (Integer) value).apply();
} else if (value instanceof Long) {
sharedPreferences.edit().putLong(key, (Long) value).apply();
} else if (value instanceof Float) {
sharedPreferences.edit().putFloat(key, (Float) value).apply();
} else if (value instanceof Boolean) {
sharedPreferences.edit().putBoolean(key, (Boolean) value).apply();
} else if (value instanceof String) {
sharedPreferences.edit().putString(key, (String) value).apply();
} else if (value instanceof Double) {
sharedPreferences.edit().putString(key, (String) value.toString()).apply();
} else if (value instanceof Set) {
sharedPreferences.edit().putStringSet(key, (Set<String>) value).apply();
} else {
throw new IllegalArgumentException("Unsupported type: " + value.getClass());
}
}
@SuppressLint("ApplySharedPref")
public void put(String key, Object value, boolean isCommit) {
SharedPreferences.Editor editor = sharedPreferences.edit();
if (value instanceof Integer) {
editor.putInt(key, (Integer) value);
} else if (value instanceof Long) {
editor.putLong(key, (Long) value);
} else if (value instanceof Float) {
editor.putFloat(key, (Float) value);
} else if (value instanceof Boolean) {
editor.putBoolean(key, (Boolean) value);
} else if (value instanceof String) {
editor.putString(key, (String) value);
} else if (value instanceof Set) {
sharedPreferences.edit().putStringSet(key, (Set<String>) value).apply();
} else {
throw new IllegalArgumentException("Unsupported type: " + value.getClass());
}
// 根据 isCommit 参数的值决定使用 commit() 还是 apply()
if (isCommit) {
editor.commit();
} else {
editor.apply();
}
}
// 存入整数
public void putInt(String key, int value) {
sharedPreferences.edit().putInt(key, value).apply();
}
public void putStringSet(String key, Set value) {
sharedPreferences.edit().putStringSet(key, value).apply();
}
public Set<String> getStringSet(String key, Set<String> defaultValue) {
return sharedPreferences.getStringSet(key, defaultValue);
}
// 获取整数
public int getInt(String key, int defaultValue) {
return sharedPreferences.getInt(key, defaultValue);
}
// 存入长整数
public void putLong(String key, long value) {
sharedPreferences.edit().putLong(key, value).apply();
}
// 获取长整数
public long getLong(String key, long defaultValue) {
return sharedPreferences.getLong(key, defaultValue);
}
// 存入浮点数
public void putFloat(String key, float value) {
sharedPreferences.edit().putFloat(key, value).apply();
}
// 获取浮点数
public float getFloat(String key, float defaultValue) {
return sharedPreferences.getFloat(key, defaultValue);
}
// 存入布尔值
public void putBoolean(String key, boolean value) {
sharedPreferences.edit().putBoolean(key, value).apply();
}
// 获取布尔值
public boolean getBoolean(String key, boolean defaultValue) {
return sharedPreferences.getBoolean(key, defaultValue);
}
// 存入字符串
public void putString(String key, String value) {
sharedPreferences.edit().putString(key, value).apply();
}
// 获取字符串
public String getString(String key, String defaultValue) {
return sharedPreferences.getString(key, defaultValue);
}
// 检查某个键是否存在
public boolean contains(String key) {
return sharedPreferences.contains(key);
}
// 移除某个键
public void remove(String key) {
sharedPreferences.edit().remove(key).apply();
}
// 清除所有数据
public void clear() {
sharedPreferences.edit().clear().apply();
}
}
\ No newline at end of file
package com.base.pdfreader2.utils
import android.content.Context
import android.content.res.AssetManager
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
object AssetUtils {
fun Context.readByteArrayFromAsset(fileName: String): ByteArray {
val assetManager: AssetManager = this.assets
var ins: InputStream? = null
val byteStream = ByteArrayOutputStream()
try {
ins = assetManager.open(fileName)
var nextValue: Int
while (ins.read().also { nextValue = it } != -1) {
byteStream.write(nextValue)
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
try {
ins?.close()
byteStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return byteStream.toByteArray()
}
fun Context.getDemoPdf(): String {
val demoFile = File(filesDir, "demo")
demoFile.mkdirs()
val file = File(demoFile, "DEMO.pdf")
return file.absolutePath
}
fun Context.getDemoDocx(): String {
val demoFile = File(filesDir, "demo")
demoFile.mkdirs()
val file = File(demoFile, "DEMO.docx")
return file.absolutePath
}
fun Context.getDemoXlsx(): String {
val demoFile = File(filesDir, "demo")
demoFile.mkdirs()
val file = File(demoFile, "DEMO.xlsx")
return file.absolutePath
}
fun Context.getDemoPptx(): String {
val demoFile = File(filesDir, "demo")
demoFile.mkdirs()
val file = File(demoFile, "DEMO.pptx")
return file.absolutePath
}
fun Context.saveAssetsFile() {
val demoFile = File(filesDir, "demo")
demoFile.mkdirs()
arrayOf(
"DEMO.pdf",
"DEMO.docx",
"DEMO.xlsx",
"DEMO.pptx"
).forEach {
val file = File(demoFile, it)
if (file.exists() && file.length() != 0L) {
return
}
file.createNewFile()
try {
FileOutputStream(file).use { fos -> fos.write(readByteArrayFromAsset(it)) }
} catch (e: IOException) {
e.printStackTrace()
}
}
}
fun Context.readJsonFromAsset(fileName: String): String {
var json = ""
try {
val assetManager = this.assets
val inputStream = assetManager.open(fileName)
val reader = BufferedReader(InputStreamReader(inputStream))
var line: String?
val jsonString = StringBuilder()
while (reader.readLine().also { line = it } != null) {
jsonString.append(line)
}
reader.close()
inputStream.close()
json = jsonString.toString()
} catch (e: IOException) {
e.printStackTrace()
}
return json
}
}
\ No newline at end of file
package com.base.pdfreader2.utils
//noinspection SuspiciousImport
import android.app.Activity
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.os.Build
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import androidx.annotation.ColorInt
object BarUtils {
fun setStatusBarLightMode(activity: Activity, isLightMode: Boolean) {
setStatusBarLightMode(activity.window, isLightMode)
}
private fun setStatusBarLightMode(window: Window, isLightMode: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val decorView = window.decorView
var vis = decorView.systemUiVisibility
vis = if (isLightMode) {
vis or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
vis and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
decorView.systemUiVisibility = vis
}
}
fun setStatusBarColor(activity: Activity, @ColorInt color: Int): View? {
return setStatusBarColor(activity, color, false)
}
private fun setStatusBarColor(activity: Activity, @ColorInt color: Int, isDecor: Boolean): View? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return null
transparentStatusBar(activity)
return applyStatusBarColor(activity, color, isDecor)
}
private fun applyStatusBarColor(activity: Activity, color: Int, isDecor: Boolean): View {
return applyStatusBarColor(activity.window, color, isDecor)
}
private fun transparentStatusBar(activity: Activity) {
transparentStatusBar(activity.window)
}
private fun transparentStatusBar(window: Window) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
val option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
val vis = window.decorView.systemUiVisibility
window.decorView.systemUiVisibility = option or vis
window.statusBarColor = Color.TRANSPARENT
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
}
}
private fun applyStatusBarColor(window: Window, color: Int, isDecor: Boolean): View {
val parent =
if (isDecor) window.decorView as ViewGroup else (window.findViewById<View>(android.R.id.content) as ViewGroup)!!
var fakeStatusBarView =
parent.findViewWithTag<View>(TAG_STATUS_BAR)
if (fakeStatusBarView != null) {
if (fakeStatusBarView.visibility == View.GONE) {
fakeStatusBarView.visibility = View.VISIBLE
}
fakeStatusBarView.setBackgroundColor(color)
} else {
fakeStatusBarView = createStatusBarView(window.context, color)
parent.addView(fakeStatusBarView)
}
return fakeStatusBarView
}
private fun createStatusBarView(context: Context, color: Int): View {
val statusBarView = View(context)
statusBarView.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight()
)
statusBarView.setBackgroundColor(color)
statusBarView.tag = TAG_STATUS_BAR
return statusBarView
}
///////////////////////////////////////////////////////////////////////////
// status bar
///////////////////////////////////////////////////////////////////////////
private const val TAG_STATUS_BAR = "TAG_STATUS_BAR"
fun getStatusBarHeight(): Int {
val resources = Resources.getSystem()
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return resources.getDimensionPixelSize(resourceId)
}
}
\ No newline at end of file
package com.base.pdfreader2.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 Long.toFormatTime2(): String {
return SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(this)
}
fun Long.toFormatTime3(): String {
return SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH).format(this)
}
fun Long.toFormatTime4(): String {
return SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).format(this)
}
fun Long.toFormatMinute(): String {
return SimpleDateFormat("mm", 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.pdfreader2.utils
import android.content.Context
import android.os.LocaleList
import java.util.Locale
object LanguageUtils {
fun getSystemLanguage(): String {
return Locale.getDefault().language
}
val supportLanguage = listOf(
Locale.ENGLISH.language,
Locale.PRC.language,
Locale("pt", "BR").language,
Locale("es").language,
Locale.FRENCH.language,
Locale("de").language,
Locale("th", "TH").language,
Locale("in", "ID").language,
Locale("ms", "MY").language,
Locale("vi", "VN").language,
Locale("ja", "JP").language,
Locale("it", "IT").language,
Locale("ko", "KR").language,
Locale("nl", "NL").language,
Locale("zh", "TW").language,
Locale("tr", "TR").language
)
fun getSystemLanguageDefault(): String {
var systemLanguage = getSystemLanguage()
if (!supportLanguage.contains(systemLanguage)) {
systemLanguage = Locale.ENGLISH.language
}
return systemLanguage
}
fun isSupportSystemLanguage(): Boolean {
return supportLanguage.contains(getSystemLanguage())
}
fun changeAppLanguage(context: Context,language:String) {
val config = context.resources.configuration
val locale = Locale(language)
//Android 7.0以上的方法
config.setLocale(locale)
config.setLocales(LocaleList(locale))
context.createConfigurationContext(config)
context.resources.updateConfiguration(config, context.resources.displayMetrics)
}
}
\ No newline at end of file
package com.base.pdfreader2.utils
import android.util.Log
import com.base.pdfreader2.BuildConfig
object LogEx {
val isOpen = true
val filterTAG = arrayOf(
"",
"MediaStoreUtils",
"DocumentFragment",
"EventUtils",
"ReportUtils",
// "NewComUtils",
)
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
This diff is collapsed.
package com.base.pdfreader2.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import com.base.pdfreader2.utils.UriUtils.readFileToByteArray
import com.tom_roush.pdfbox.pdmodel.PDDocument
import com.tom_roush.pdfbox.pdmodel.PDPage
import com.tom_roush.pdfbox.pdmodel.PDPageContentStream
import com.tom_roush.pdfbox.pdmodel.common.PDRectangle
import com.tom_roush.pdfbox.pdmodel.encryption.AccessPermission
import com.tom_roush.pdfbox.pdmodel.encryption.StandardProtectionPolicy
import com.tom_roush.pdfbox.pdmodel.graphics.image.PDImageXObject
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
import java.io.IOException
object PdfBoxUtils {
private val TAG = "PdfUtils"
fun Context.getNumberOfPages(filePath: String, password: String? = null, uri: String? = null): Int {
val document =
loadPDDocument(path = filePath, password, uri)
return document.numberOfPages
}
fun Context.getPdfDrawablePage(
context: Context,
filePath: String,
password: String? = null,
uri: String? = null,
pageIndex: Int,
scale: Float = 1f,
): Drawable {
val document = loadPDDocument(filePath, password, uri)
val renderer = PDFRenderer(document)
val bitmap: Bitmap = renderer.renderImage(pageIndex, scale, ImageType.RGB, RenderDestination.EXPORT)
val drawable = BitmapDrawable(context.resources, bitmap)
return drawable
}
fun Context.checkPdfEncryption(filePath: String = "", uri: String? = null): Int {
var state = 0
var pdfDocument: PDDocument? = null
try {
pdfDocument = loadPDDocument(filePath, null, uri)
pdfDocument.use { document ->
if (document.isEncrypted) {
println("The PDF is encrypted.")
val ap = document.getCurrentAccessPermission()
if (ap.canExtractContent()) {
println("You are allowed to extract content.")
} else {
state = 1
println("You are not allowed to extract content.")
}
if (ap.canPrint()) {
println("You are allowed to print the document.")
} else {
println("You are not allowed to print the document.")
state = 1
}
} else {
println("The PDF is not encrypted.")
state = 0
}
}
} catch (e: IOException) {
e.printStackTrace()
state = 1
} finally {
pdfDocument?.close()
}
return state
}
fun setPassword(
sourceFilePath: String,
userPassword: String,
ownerPassword: String
): Boolean {
try {
PDDocument.load(File(sourceFilePath)).use { document ->
val ap = AccessPermission()
ap.setCanPrint(false)
ap.setCanModify(false)
ap.setCanExtractContent(false)
ap.setCanExtractForAccessibility(false)
ap.setCanFillInForm(false)
val spp = StandardProtectionPolicy(userPassword, ownerPassword, ap)
document.protect(spp)
document.save(File(sourceFilePath))
LogEx.logDebug(TAG, "setPassword finish")
}
} catch (e: Exception) {
LogEx.logDebug(TAG, "setPassword Exception ${e.printStackTrace()}")
e.printStackTrace()
return false
}
return true
}
fun clearPassword(filePath: String, password: String) {
try {
PDDocument.load(File(filePath), password).use { document ->
if (document.isEncrypted) {
val ap: AccessPermission = document.getCurrentAccessPermission()
if (ap.isOwnerPermission) {
// 创建一个新的保护策略,不设置密码
val spp = StandardProtectionPolicy(
"", "",
AccessPermission.getOwnerAccessPermission()
)
document.protect(spp)
}
}
// 保存PDF文件到新的位置,没有密码保护
document.save(File(filePath))
}
} catch (e: IOException) {
e.printStackTrace()
}
}
fun Context.checkPwd(
filePath: String,
password: String,
uri: String? = null
): Boolean {
try {
val pdfDocument = loadPDDocument(filePath, password, uri)
// 尝试使用提供的密码加载PDF文件
pdfDocument.use { document ->
// 如果没有抛出异常,说明密码正确
return true
}
} catch (e: Exception) {
// 加载文件时,如果密码保护策略异常,密码可能错误
println("Incorrect password or restricted permissions.")
} catch (e: IOException) {
// 其他I/O异常处理
println("An I/O error occurred: " + e.message)
}
return false
}
private fun Context.loadPDDocument(path: String, password: String?, uri: String? = null): PDDocument {
return if (uri == null) {
if (password == null) {
PDDocument.load(File(path))
} else {
PDDocument.load(File(path), password)
}
} else {
if (password == null) {
PDDocument.load(readFileToByteArray(this, Uri.parse(uri)) ?: byteArrayOf())
} else {
PDDocument.load(readFileToByteArray(this, Uri.parse(uri)) ?: byteArrayOf(), password)
}
}
}
fun saveNewPdf(imagePath: String, savePath: String) {
try {
PDDocument().use { document ->
// 添加一个页面
val page = PDPage(PDRectangle.A4)
document.addPage(page)
// 加载图片
val image = PDImageXObject.createFromFile(imagePath, document)
// 计算图片在页面中的位置和尺寸
val x = (PDRectangle.A4.width - image.width) / 2
val y = (PDRectangle.A4.height - image.height) / 2
PDPageContentStream(document, page).use { contentStream ->
contentStream.drawImage(
image,
x,
y,
image.width.toFloat(),
image.height.toFloat()
)
}
// 保存PDF文档
document.save(savePath)
}
} catch (e: IOException) {
e.printStackTrace()
}
}
}
\ No newline at end of file
package com.base.pdfreader2.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.checkNotificationPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
} else {
return true
}
}
fun Context.requestStoragePermission(
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.pdfreader2.utils
object SpStringUtils {
private val TAG = "SpStringUtils"
const val BOOKMARK_KEY = "bookmark_key"
//key=last_view_key value=/data/user/0/com.ttesst.gododo.redause/files/demo/DEMO.pdf_/_1728703007625
const val LAST_VIEW_KEY = "last_view_key"
fun getSpStringList(key: String): List<String> {
val sp = AppPreferences.getInstance().getString(key, "")
return if (sp.equals("")) {
listOf()
} else {
sp.split("|||")
}
}
fun addSpString(key: String, value: String) {
LogEx.logDebug(TAG, "key=$key value=$value")
val list = getSpStringList(key).toMutableList()
list.add(value)
val string = list.joinToString(separator = "|||")
AppPreferences.getInstance().put(key, string)
}
fun deleteSpString(key: String, value: String) {
val list = getSpStringList(key).toMutableList()
list.remove(value)
val string = list.joinToString(separator = "|||")
AppPreferences.getInstance().put(key, string)
}
}
\ No newline at end of file
package com.base.pdfreader2.utils
import android.content.Context
import android.net.Uri
import com.tom_roush.pdfbox.io.IOUtils
import java.io.InputStream
object UriUtils {
fun readFileToByteArray(context: Context, uri: Uri): ByteArray? {
var inputStream: InputStream? = null
return try {
inputStream = context.contentResolver.openInputStream(uri)
// 读取文件内容到字节数组
IOUtils.toByteArray(inputStream)
} catch (e: java.lang.Exception) {
e.printStackTrace()
null
} finally {
if (inputStream != null) {
try {
inputStream.close()
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
}
}
}
\ No newline at end of file
package com.base.pdfreader2.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="#58585A" android:state_selected="false" />
<item android:color="#DE202A" 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/h_bookmark_n" android:state_selected="false" />
<item android:drawable="@mipmap/h_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/h_doc_n" android:state_selected="false" />
<item android:drawable="@mipmap/h_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/h_recent_n" android:state_selected="false" />
<item android:drawable="@mipmap/h_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/xuan" android:state_selected="true" />
<item android:drawable="@mipmap/weixuan" android:state_selected="false" />
</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/h_tool_n" android:state_selected="false" />
<item android:drawable="@mipmap/h_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"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="2dp" />
<!-- <solid android:color="@color/colorPrimary" />-->
<gradient
android:endColor="#FAF8D1"
android:startColor="#FAD4E7"
android:type="linear" />
</shape>
\ 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="#20000000">
</ripple>
\ No newline at end of file
This diff is collapsed.
<?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:background="#FA6F52"
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.main.DocumentFragment">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<LinearLayout
android:id="@+id/ll_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="60dp"
android:orientation="vertical"
android:visibility="gone"
tools:ignore="UseCompoundDrawables,UselessParent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/empty"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="@string/empty"
android:textColor="#B1B4B9"
android:textSize="16sp"
tools:ignore="HardcodedText" />
</LinearLayout>
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.ToolFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Tools" />
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fl_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F4F5FA">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="8dp"
android:src="@mipmap/zhanweitu3"
tools:ignore="ContentDescription" />
</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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="15dp"
android:layout_marginStart="15dp"
android:src="@mipmap/pdficon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<FrameLayout
android:id="@+id/fl_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="15dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/bg_selector_select"
tools:ignore="ContentDescription" />
</FrameLayout>
<FrameLayout
android:id="@+id/fl_bookmark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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>
</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 xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.PdfReader2" 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
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="color_4f8df4">#4F8DF4</color>
<color name="color_de202a">#DE202A</color>
<color name="color_1ea362">#1EA362</color>
<color name="color_f6b911">#F6B911</color>
<color name="color_80ffffff" >#80FFFFFF</color>
</resources>
\ No newline at end of file
<resources>
<string name="app_name">pdf reader 2</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="document">Document</string>
<string name="bookmark">Bookmark</string>
<string name="tool">Tool</string>
<string name="recent">Recent</string>
<string name="empty">Empty</string>
</resources>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.PdfReader2" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.PdfReader2" parent="Base.Theme.PdfReader2" />
</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
package com.base.pdfreader2
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.0"
kotlin = "1.9.0"
coreKtx = "1.10.1"
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"
swiperefreshlayout = "1.1.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-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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