433 lines
14 KiB
Kotlin
433 lines
14 KiB
Kotlin
@file:Suppress("unused", "UnusedReceiverParameter")
|
|
|
|
package io.legato.kazusa.utils
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.app.Activity
|
|
import android.app.PendingIntent
|
|
import android.app.PendingIntent.FLAG_MUTABLE
|
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
|
import android.app.PendingIntent.getActivity
|
|
import android.app.PendingIntent.getBroadcast
|
|
import android.app.PendingIntent.getService
|
|
import android.app.Service
|
|
import android.content.BroadcastReceiver
|
|
import android.content.ClipData
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.content.IntentFilter
|
|
import android.content.SharedPreferences
|
|
import android.content.pm.ApplicationInfo
|
|
import android.content.pm.PackageManager
|
|
import android.content.res.ColorStateList
|
|
import android.content.res.Configuration
|
|
import android.graphics.Bitmap
|
|
import android.graphics.drawable.Drawable
|
|
import android.net.ConnectivityManager
|
|
import android.net.Uri
|
|
import android.os.BatteryManager
|
|
import android.os.Build
|
|
import android.os.Process
|
|
import android.provider.Settings
|
|
import android.util.TypedValue
|
|
import androidx.annotation.ColorRes
|
|
import androidx.annotation.DrawableRes
|
|
import androidx.core.content.ContextCompat
|
|
import androidx.core.content.FileProvider
|
|
import androidx.core.content.edit
|
|
import androidx.datastore.preferences.preferencesDataStore
|
|
import androidx.preference.PreferenceManager
|
|
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
|
import io.legato.kazusa.R
|
|
import io.legato.kazusa.constant.AppConst
|
|
import io.legato.kazusa.data.entities.Book
|
|
import io.legato.kazusa.help.IntentHelp
|
|
import io.legato.kazusa.help.book.isAudio
|
|
import io.legato.kazusa.help.book.isImage
|
|
import io.legato.kazusa.help.config.AppConfig
|
|
import io.legato.kazusa.ui.book.audio.AudioPlayActivity
|
|
import io.legato.kazusa.ui.book.manga.ReadMangaActivity
|
|
import io.legato.kazusa.ui.book.read.ReadBookActivity
|
|
import splitties.systemservices.clipboardManager
|
|
import splitties.systemservices.connectivityManager
|
|
import splitties.systemservices.uiModeManager
|
|
import java.io.File
|
|
import java.io.FileOutputStream
|
|
import kotlin.system.exitProcess
|
|
|
|
inline fun <reified A : Activity> Context.startActivity(configIntent: Intent.() -> Unit = {}) {
|
|
val intent = Intent(this, A::class.java)
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
intent.apply(configIntent)
|
|
startActivity(intent)
|
|
}
|
|
|
|
fun Context.startActivityForBook(
|
|
book: Book,
|
|
configIntent: Intent.() -> Unit = {},
|
|
) {
|
|
val cls = when {
|
|
book.isAudio -> AudioPlayActivity::class.java
|
|
book.isImage && AppConfig.showMangaUi -> ReadMangaActivity::class.java
|
|
else -> ReadBookActivity::class.java
|
|
}
|
|
val intent = Intent(this, cls)
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
intent.putExtra("bookUrl", book.bookUrl)
|
|
intent.apply(configIntent)
|
|
startActivity(intent)
|
|
}
|
|
|
|
inline fun <reified T : Service> Context.startService(configIntent: Intent.() -> Unit = {}) {
|
|
startService(Intent(this, T::class.java).apply(configIntent))
|
|
}
|
|
|
|
inline fun <reified T : Service> Context.stopService() {
|
|
stopService(Intent(this, T::class.java))
|
|
}
|
|
|
|
@SuppressLint("UnspecifiedImmutableFlag")
|
|
inline fun <reified T : Service> Context.servicePendingIntent(
|
|
action: String,
|
|
requestCode: Int = 0,
|
|
configIntent: Intent.() -> Unit = {},
|
|
): PendingIntent? {
|
|
val intent = Intent(this, T::class.java)
|
|
intent.action = action
|
|
configIntent.invoke(intent)
|
|
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
FLAG_UPDATE_CURRENT or FLAG_MUTABLE
|
|
} else {
|
|
FLAG_UPDATE_CURRENT
|
|
}
|
|
return getService(this, requestCode, intent, flags)
|
|
}
|
|
|
|
@SuppressLint("UnspecifiedImmutableFlag")
|
|
fun Context.activityPendingIntent(
|
|
intent: Intent,
|
|
action: String,
|
|
): PendingIntent? {
|
|
intent.action = action
|
|
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
FLAG_UPDATE_CURRENT or FLAG_MUTABLE
|
|
} else {
|
|
FLAG_UPDATE_CURRENT
|
|
}
|
|
return getActivity(this, 0, intent, flags)
|
|
}
|
|
|
|
@SuppressLint("UnspecifiedImmutableFlag")
|
|
inline fun <reified T : Activity> Context.activityPendingIntent(
|
|
action: String,
|
|
configIntent: Intent.() -> Unit = {},
|
|
): PendingIntent? {
|
|
val intent = Intent(this, T::class.java)
|
|
intent.action = action
|
|
configIntent.invoke(intent)
|
|
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
FLAG_UPDATE_CURRENT or FLAG_MUTABLE
|
|
} else {
|
|
FLAG_UPDATE_CURRENT
|
|
}
|
|
return getActivity(this, 0, intent, flags)
|
|
}
|
|
|
|
@SuppressLint("UnspecifiedImmutableFlag")
|
|
inline fun <reified T : BroadcastReceiver> Context.broadcastPendingIntent(
|
|
action: String,
|
|
configIntent: Intent.() -> Unit = {},
|
|
): PendingIntent? {
|
|
val intent = Intent(this, T::class.java)
|
|
intent.action = action
|
|
configIntent.invoke(intent)
|
|
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
FLAG_UPDATE_CURRENT or FLAG_MUTABLE
|
|
} else {
|
|
FLAG_UPDATE_CURRENT
|
|
}
|
|
return getBroadcast(this, 0, intent, flags)
|
|
}
|
|
|
|
fun Context.startForegroundServiceCompat(intent: Intent) {
|
|
try {
|
|
startService(intent)
|
|
} catch (e: IllegalStateException) {
|
|
ContextCompat.startForegroundService(this, intent)
|
|
}
|
|
}
|
|
|
|
val Context.defaultSharedPreferences: SharedPreferences
|
|
get() = PreferenceManager.getDefaultSharedPreferences(this)
|
|
|
|
fun Context.getPrefBoolean(key: String, defValue: Boolean = false) =
|
|
defaultSharedPreferences.getBoolean(key, defValue)
|
|
|
|
fun Context.putPrefBoolean(key: String, value: Boolean = false) =
|
|
defaultSharedPreferences.edit { putBoolean(key, value) }
|
|
|
|
fun Context.getPrefInt(key: String, defValue: Int = 0) =
|
|
defaultSharedPreferences.getInt(key, defValue)
|
|
|
|
fun Context.putPrefInt(key: String, value: Int) =
|
|
defaultSharedPreferences.edit { putInt(key, value) }
|
|
|
|
fun Context.getPrefLong(key: String, defValue: Long = 0L) =
|
|
defaultSharedPreferences.getLong(key, defValue)
|
|
|
|
fun Context.putPrefLong(key: String, value: Long) =
|
|
defaultSharedPreferences.edit { putLong(key, value) }
|
|
|
|
fun Context.getPrefString(key: String, defValue: String? = null) =
|
|
defaultSharedPreferences.getString(key, defValue)
|
|
|
|
fun Context.putPrefString(key: String, value: String?) =
|
|
defaultSharedPreferences.edit { putString(key, value) }
|
|
|
|
fun Context.getPrefStringSet(
|
|
key: String,
|
|
defValue: MutableSet<String>? = null,
|
|
): MutableSet<String>? = defaultSharedPreferences.getStringSet(key, defValue)
|
|
|
|
fun Context.putPrefStringSet(key: String, value: MutableSet<String>) =
|
|
defaultSharedPreferences.edit { putStringSet(key, value) }
|
|
|
|
fun Context.removePref(key: String) =
|
|
defaultSharedPreferences.edit { remove(key) }
|
|
|
|
|
|
fun Context.getCompatColor(@ColorRes id: Int): Int = ContextCompat.getColor(this, id)
|
|
|
|
fun Context.getCompatDrawable(@DrawableRes id: Int): Drawable? = ContextCompat.getDrawable(this, id)
|
|
|
|
fun Context.getCompatColorStateList(@ColorRes id: Int): ColorStateList? =
|
|
ContextCompat.getColorStateList(this, id)
|
|
|
|
fun Context.checkSelfUriPermission(uri: Uri, modeFlags: Int): Int =
|
|
checkUriPermission(uri, Process.myPid(), Process.myUid(), modeFlags)
|
|
|
|
fun Context.restart() {
|
|
val intent: Intent? = packageManager.getLaunchIntentForPackage(packageName)
|
|
intent?.let {
|
|
intent.addFlags(
|
|
Intent.FLAG_ACTIVITY_NEW_TASK
|
|
or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
|
)
|
|
startActivity(intent)
|
|
//杀掉以前进程
|
|
Process.killProcess(Process.myPid())
|
|
exitProcess(0)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 系统息屏时间
|
|
*/
|
|
val Context.sysScreenOffTime: Int
|
|
get() {
|
|
return kotlin.runCatching {
|
|
Settings.System.getInt(contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
|
|
}.onFailure {
|
|
it.printOnDebug()
|
|
}.getOrDefault(0)
|
|
}
|
|
|
|
val Context.statusBarHeight: Int
|
|
@SuppressLint("DiscouragedApi", "InternalInsetResource")
|
|
get() {
|
|
if (Build.BOARD == "windows") {
|
|
return 0
|
|
}
|
|
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
|
|
return resources.getDimensionPixelSize(resourceId)
|
|
}
|
|
|
|
val Context.navigationBarHeight: Int
|
|
@SuppressLint("DiscouragedApi", "InternalInsetResource")
|
|
get() {
|
|
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
|
|
return resources.getDimensionPixelSize(resourceId)
|
|
}
|
|
|
|
fun Context.share(text: String, title: String = getString(R.string.share)) {
|
|
kotlin.runCatching {
|
|
val intent = Intent(Intent.ACTION_SEND)
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
intent.putExtra(Intent.EXTRA_SUBJECT, title)
|
|
intent.putExtra(Intent.EXTRA_TEXT, text)
|
|
intent.type = "text/plain"
|
|
startActivity(Intent.createChooser(intent, title))
|
|
}
|
|
}
|
|
|
|
fun Context.share(file: File, type: String = "text/*") {
|
|
val fileUri = FileProvider.getUriForFile(this, AppConst.authority, file)
|
|
val intent = Intent(Intent.ACTION_SEND)
|
|
intent.type = type
|
|
intent.putExtra(Intent.EXTRA_STREAM, fileUri)
|
|
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
startActivity(
|
|
Intent.createChooser(
|
|
intent,
|
|
getString(R.string.share_selected_source)
|
|
)
|
|
)
|
|
}
|
|
|
|
@SuppressLint("SetWorldReadable")
|
|
fun Context.shareWithQr(
|
|
text: String,
|
|
title: String = getString(R.string.share),
|
|
errorCorrectionLevel: ErrorCorrectionLevel = ErrorCorrectionLevel.H,
|
|
) {
|
|
val bitmap = QRCodeUtils.createQRCode(text, errorCorrectionLevel = errorCorrectionLevel)
|
|
if (bitmap == null) {
|
|
toastOnUi(R.string.text_too_long_qr_error)
|
|
} else {
|
|
try {
|
|
val file = File(externalCacheDir, "qr.png")
|
|
val fOut = FileOutputStream(file)
|
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut)
|
|
fOut.flush()
|
|
fOut.close()
|
|
file.setReadable(true, false)
|
|
val contentUri = FileProvider.getUriForFile(this, AppConst.authority, file)
|
|
val intent = Intent(Intent.ACTION_SEND)
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
intent.putExtra(Intent.EXTRA_STREAM, contentUri)
|
|
intent.type = "image/png"
|
|
startActivity(Intent.createChooser(intent, title))
|
|
} catch (e: Exception) {
|
|
toastOnUi(e.localizedMessage ?: "ERROR")
|
|
}
|
|
}
|
|
}
|
|
|
|
fun Context.sendToClip(text: String) {
|
|
val clipData = ClipData.newPlainText(null, text)
|
|
clipboardManager.setPrimaryClip(clipData)
|
|
longToastOnUi(R.string.copy_complete)
|
|
}
|
|
|
|
fun Context.getClipText(): String? {
|
|
clipboardManager.primaryClip?.let {
|
|
if (it.itemCount > 0) {
|
|
return it.getItemAt(0).text.toString().trim()
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
fun Context.sendMail(mail: String) {
|
|
try {
|
|
val intent = Intent(Intent.ACTION_SENDTO)
|
|
intent.data = Uri.parse("mailto:$mail")
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
startActivity(intent)
|
|
} catch (e: Exception) {
|
|
toastOnUi(e.localizedMessage ?: "Error")
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取电量
|
|
*/
|
|
val Context.sysBattery: Int
|
|
get() {
|
|
val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
|
|
val batteryStatus = registerReceiver(null, iFilter)
|
|
return batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
|
|
}
|
|
|
|
val Context.externalFiles: File
|
|
get() = this.getExternalFilesDir(null) ?: this.filesDir
|
|
|
|
val Context.externalCache: File
|
|
get() = this.externalCacheDir ?: this.cacheDir
|
|
|
|
fun Context.openUrl(url: String) {
|
|
try {
|
|
startActivity(IntentHelp.getBrowserIntent(url))
|
|
} catch (e: Exception) {
|
|
toastOnUi(e.localizedMessage ?: "open url error")
|
|
e.printOnDebug()
|
|
}
|
|
}
|
|
|
|
fun Context.openUrl(uri: Uri) {
|
|
try {
|
|
startActivity(IntentHelp.getBrowserIntent(uri))
|
|
} catch (e: Exception) {
|
|
toastOnUi(e.localizedMessage ?: "open url error")
|
|
e.printOnDebug()
|
|
}
|
|
}
|
|
|
|
@SuppressLint("ObsoleteSdkInt")
|
|
fun Context.openFileUri(uri: Uri, type: String? = null) {
|
|
val intent = Intent()
|
|
intent.action = Intent.ACTION_VIEW
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
//7.0版本以上
|
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
}
|
|
val uri = if (uri.isContentScheme()) uri
|
|
else FileProvider.getUriForFile(this, AppConst.authority, File(uri.path!!))
|
|
intent.setDataAndType(uri, type ?: IntentType.from(uri))
|
|
try {
|
|
startActivity(intent)
|
|
} catch (e: Exception) {
|
|
toastOnUi(e.stackTraceStr)
|
|
e.printOnDebug()
|
|
}
|
|
}
|
|
|
|
@Suppress("DEPRECATION")
|
|
val Context.isWifiConnect: Boolean
|
|
@SuppressLint("MissingPermission")
|
|
get() {
|
|
val info = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
|
|
return info?.isConnected == true
|
|
}
|
|
|
|
val Context.isPad: Boolean
|
|
get() {
|
|
return (resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
|
|
}
|
|
|
|
val Context.isTv: Boolean
|
|
get() = uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION
|
|
|
|
val Context.channel: String
|
|
get() {
|
|
try {
|
|
val pm = packageManager
|
|
val appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
|
|
return appInfo.metaData.getString("channel") ?: ""
|
|
} catch (e: Exception) {
|
|
e.printOnDebug()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
val Context.isDebuggable: Boolean
|
|
get() = applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0
|
|
|
|
val Context.dataStore by preferencesDataStore(name = "settings")
|
|
|
|
val Context.bookshelfLayout: Int
|
|
get() = if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
AppConfig.bookshelfLayoutLandscape
|
|
} else {
|
|
AppConfig.bookshelfLayoutPortrait
|
|
}
|
|
|
|
fun Context.themeColor(attr: Int): Int {
|
|
val typedValue = TypedValue()
|
|
theme.resolveAttribute(attr, typedValue, true)
|
|
return ContextCompat.getColor(this, typedValue.resourceId)
|
|
}
|