diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a4dddb70c..10127eed3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -209,7 +209,9 @@ dependencies { implementation("com.google.accompanist:accompanist-themeadapter-material3:0.30.1") implementation("com.google.android.material:material:1.9.0") - implementation("com.louiscad.splitties:splitties-appctx:3.0.0") + val splitties = "3.0.0" + implementation("com.louiscad.splitties:splitties-appctx:$splitties") + implementation("com.louiscad.splitties:splitties-systemservices:$splitties") // https://square.github.io/okhttp/changelogs/changelog/ implementation(platform("com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11")) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.kt b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.kt index 2e819148f..743d62c73 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.kt @@ -37,7 +37,6 @@ import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi import androidx.annotation.StringRes -import androidx.core.content.getSystemService import androidx.core.view.GravityCompat import androidx.customview.widget.Openable import androidx.drawerlayout.widget.DrawerLayout @@ -69,7 +68,6 @@ import com.hippo.ehviewer.ui.scene.GalleryListScene import com.hippo.ehviewer.ui.scene.GalleryListScene.Companion.toStartArgs import com.hippo.ehviewer.ui.scene.ProgressScene import com.hippo.ehviewer.util.addTextToClipboard -import com.hippo.ehviewer.util.getClipboardManager import com.hippo.ehviewer.util.getParcelableExtraCompat import com.hippo.ehviewer.util.getUrlFromClipboard import com.hippo.ehviewer.yorozuya.IOUtils @@ -77,6 +75,8 @@ import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import splitties.systemservices.clipboardManager +import splitties.systemservices.connectivityManager import java.io.File import java.io.FileOutputStream import java.io.IOException @@ -85,7 +85,6 @@ import java.io.OutputStream class MainActivity : EhActivity() { private lateinit var binding: ActivityMainBinding private lateinit var navController: NavController - private val connectivityManager by lazy { getSystemService()!! } private val availableNetworks = mutableListOf() private fun saveImageToTempFile(uri: Uri): File? { @@ -354,7 +353,7 @@ class MainActivity : EhActivity() { } private suspend fun checkClipboardUrl() { - val text = this.getClipboardManager().getUrlFromClipboard(this) + val text = clipboardManager.getUrlFromClipboard(this) val hashCode = text?.hashCode() ?: 0 if (text != null && hashCode != 0 && Settings.clipboardTextHashCode != hashCode) { val result1 = GalleryDetailUrlParser.parse(text, false) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt index 1869d8ad0..31653fb79 100755 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt @@ -20,7 +20,6 @@ import android.annotation.SuppressLint import android.app.Activity import android.app.Dialog import android.app.DownloadManager -import android.content.Context import android.content.DialogInterface import android.content.pm.PackageManager import android.net.Uri @@ -109,7 +108,6 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import arrow.core.partially1 -import coil.Coil.imageLoader import coil.annotation.ExperimentalCoilApi import coil.imageLoader import com.google.android.material.progressindicator.CircularProgressIndicator @@ -173,7 +171,6 @@ import com.hippo.ehviewer.util.ExceptionUtils import com.hippo.ehviewer.util.ReadableTime import com.hippo.ehviewer.util.addTextToClipboard import com.hippo.ehviewer.util.getParcelableCompat -import com.hippo.ehviewer.util.getSystemService import com.hippo.ehviewer.yorozuya.FileUtils import com.hippo.ehviewer.yorozuya.ViewUtils import com.hippo.ehviewer.yorozuya.collect.IntList @@ -183,8 +180,9 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job import moe.tarsin.coroutines.runSuspendCatching import okhttp3.HttpUrl.Companion.toHttpUrl +import splitties.systemservices.downloadManager import kotlin.math.roundToInt -import com.hippo.ehviewer.download.DownloadManager as downloadManager +import com.hippo.ehviewer.download.DownloadManager as ehDownloadManager class GalleryDetailScene : BaseScene(), DownloadInfoListener { private var mDownloadState = 0 @@ -385,7 +383,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { // Get download state val gid = gid mDownloadState = if (gid != -1L) { - downloadManager.getDownloadState(gid) + ehDownloadManager.getDownloadState(gid) } else { DownloadInfo.STATE_INVALID } @@ -403,7 +401,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { } else { getDetailError = getString(R.string.error_cannot_find_gallery) } - downloadManager.addDownloadInfoListener(this) + ehDownloadManager.addDownloadInfoListener(this) (requireActivity() as MainActivity).mShareUrl = galleryDetailUrl return ComposeView(requireContext()).apply { setMD3Content { @@ -828,7 +826,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { private fun onDownloadButtonClick() { val galleryDetail = composeBindingGD ?: return - if (downloadManager.getDownloadState(galleryDetail.gid) == DownloadInfo.STATE_INVALID) { + if (ehDownloadManager.getDownloadState(galleryDetail.gid) == DownloadInfo.STATE_INVALID) { CommonOperations.startDownload( activity as MainActivity, galleryDetail, @@ -845,7 +843,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { Settings.removeImageFiles, ) val helper = DeleteDialogHelper( - downloadManager, + ehDownloadManager, galleryDetail, builder, ) @@ -859,7 +857,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { override fun onDestroyView() { super.onDestroyView() - downloadManager.removeDownloadInfoListener(this) + ehDownloadManager.removeDownloadInfoListener(this) (requireActivity() as MainActivity).mShareUrl = null } @@ -1197,7 +1195,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { @OptIn(ExperimentalCoilApi::class) private fun showCoverGalleryList() { - val context = context ?: return + context ?: return val gid = gid if (-1L == gid) { return @@ -1621,7 +1619,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { r.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, FileUtils.sanitizeFilename(name)) r.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) runCatching { - requireContext().getSystemService()!!.enqueue(r) + downloadManager.enqueue(r) }.onFailure { it.printStackTrace() } @@ -1726,9 +1724,8 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { ) r.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) r.addRequestHeader("Cookie", EhCookieStore.getCookieHeader(url.toHttpUrl())) - val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager try { - dm.enqueue(r) + downloadManager.enqueue(r) showTip(R.string.download_torrent_started, LENGTH_SHORT) } catch (e: Throwable) { e.printStackTrace() diff --git a/app/src/main/java/com/hippo/ehviewer/util/AppHelper.kt b/app/src/main/java/com/hippo/ehviewer/util/AppHelper.kt index d80c2787c..55bc4db4d 100644 --- a/app/src/main/java/com/hippo/ehviewer/util/AppHelper.kt +++ b/app/src/main/java/com/hippo/ehviewer/util/AppHelper.kt @@ -16,10 +16,8 @@ package com.hippo.ehviewer.util import android.app.Activity -import android.content.Context import android.content.Intent import android.widget.Toast -import androidx.core.content.ContextCompat import com.hippo.ehviewer.R object AppHelper { @@ -39,5 +37,3 @@ object AppHelper { } } } - -inline fun Context.getSystemService(): T? = ContextCompat.getSystemService(this, T::class.java) diff --git a/app/src/main/java/com/hippo/ehviewer/util/ClipboardUtil.kt b/app/src/main/java/com/hippo/ehviewer/util/ClipboardUtil.kt index 4458f6cd2..ffad7eaab 100644 --- a/app/src/main/java/com/hippo/ehviewer/util/ClipboardUtil.kt +++ b/app/src/main/java/com/hippo/ehviewer/util/ClipboardUtil.kt @@ -29,14 +29,7 @@ import com.hippo.ehviewer.R import com.hippo.ehviewer.ui.MainActivity import com.hippo.ehviewer.ui.SettingsActivity import com.hippo.ehviewer.ui.scene.BaseScene - -fun Context.getClipboardManager(): ClipboardManager { - return getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager -} - -infix fun Context.tellClipboard(text: String?) { - addTextToClipboard(text, false) -} +import splitties.systemservices.clipboardManager infix fun Context.tellClipboardWithToast(text: String?) { addTextToClipboard(text, false, true) @@ -44,7 +37,7 @@ infix fun Context.tellClipboardWithToast(text: String?) { @JvmOverloads fun Context.addTextToClipboard(text: CharSequence?, isSensitive: Boolean, useToast: Boolean = false) { - getClipboardManager().apply { + clipboardManager.apply { setPrimaryClip( ClipData.newPlainText(null, text).apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && isSensitive) { @@ -65,21 +58,9 @@ fun Context.addTextToClipboard(text: CharSequence?, isSensitive: Boolean, useToa } } -fun ClipboardManager.getTextFromClipboard(context: Context): String? { - val item = primaryClip?.getItemAt(0) - val string = item?.coerceToText(context).toString() - return if (!TextUtils.isEmpty(string)) string else null -} - fun ClipboardManager.getUrlFromClipboard(context: Context): String? { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && primaryClipDescription?.classificationStatus == ClipDescription.CLASSIFICATION_COMPLETE) { - if (( - primaryClipDescription?.getConfidenceScore(TextClassifier.TYPE_URL) - ?.let { it <= 0 } - ) == true - ) { - return null - } + if ((primaryClipDescription?.getConfidenceScore(TextClassifier.TYPE_URL)?.let { it <= 0 }) == true) return null } val item = primaryClip?.getItemAt(0) val string = item?.coerceToText(context).toString() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 746bb86b1..eb2e2dd20 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -20,7 +20,6 @@ import android.Manifest import android.annotation.SuppressLint import android.app.assist.AssistContent import android.content.ClipData -import android.content.ClipboardManager import android.content.ContentValues import android.content.Intent import android.content.pm.ActivityInfo @@ -104,6 +103,7 @@ import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.sample import kotlinx.coroutines.suspendCancellableCoroutine +import splitties.systemservices.clipboardManager import java.io.File import java.io.IOException import java.util.concurrent.atomic.AtomicReference @@ -458,12 +458,9 @@ class ReaderActivity : EhActivity() { File(dir, filename), ) - val clipboardManager = getSystemService(ClipboardManager::class.java) - if (clipboardManager != null) { - val clipData = ClipData.newUri(contentResolver, "ehviewer", uri) - clipboardManager.setPrimaryClip(clipData) - Toast.makeText(this, getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show() - } + val clipData = ClipData.newUri(contentResolver, "ehviewer", uri) + clipboardManager.setPrimaryClip(clipData) + Toast.makeText(this, getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show() } fun saveImage(page: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index ef1564f39..21f467cc0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -1,45 +1,23 @@ package eu.kanade.tachiyomi.util.system -import android.app.ActivityManager -import android.app.NotificationManager import android.content.Context -import android.content.pm.PackageManager import android.content.res.Configuration import android.content.res.Resources import android.graphics.Color -import android.net.ConnectivityManager -import android.net.NetworkCapabilities -import android.net.Uri -import android.net.wifi.WifiManager import android.os.Build -import android.os.PowerManager import android.provider.Settings import android.util.Log -import android.util.TypedValue import android.view.Display import android.view.View -import android.view.WindowManager import androidx.annotation.AttrRes import androidx.annotation.ColorInt -import androidx.core.content.ContextCompat -import androidx.core.content.getSystemService import androidx.core.graphics.alpha import androidx.core.graphics.blue import androidx.core.graphics.green import androidx.core.graphics.red -import com.hippo.unifile.UniFile -import java.io.File -import kotlin.math.max +import splitties.systemservices.windowManager import kotlin.math.roundToInt -/** - * Checks if the give permission is granted. - * - * @param permission the permission to check. - * @return true if it has permissions. - */ -fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED - /** * Returns the color for the given attribute. * @@ -59,22 +37,6 @@ fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermissio return color } -@ColorInt fun Context.getThemeColor(attr: Int): Int { - val tv = TypedValue() - return if (this.theme.resolveAttribute(attr, tv, true)) { - if (tv.resourceId != 0) { - getColor(tv.resourceId) - } else { - tv.data - } - } else { - 0 - } -} - -val getDisplayMaxHeightInPx: Int - get() = Resources.getSystem().displayMetrics.let { max(it.heightPixels, it.widthPixels) } - /** * Converts to px. */ @@ -84,36 +46,15 @@ val Int.dpToPx: Int val Int.pxToDp: Float get() = (this / Resources.getSystem().displayMetrics.density) -/** - * Converts to px and takes into account LTR/RTL layout. - */ -val Float.dpToPxEnd: Float - get() = ( - this * Resources.getSystem().displayMetrics.density * - if (Resources.getSystem().isLTR) 1 else -1 - ) - val Resources.isLTR get() = configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR -val Context.notificationManager: NotificationManager - get() = getSystemService()!! - -val Context.connectivityManager: ConnectivityManager - get() = getSystemService()!! - -val Context.wifiManager: WifiManager - get() = getSystemService()!! - -val Context.powerManager: PowerManager - get() = getSystemService()!! - val Context.displayCompat: Display? get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { display } else { @Suppress("DEPRECATION") - getSystemService()?.defaultDisplay + windowManager.defaultDisplay } /** Gets the duration multiplier for general animations on the device @@ -122,35 +63,6 @@ val Context.displayCompat: Display? val Context.animatorDurationScale: Float get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f) -/** - * Convenience method to acquire a partial wake lock. - */ -fun Context.acquireWakeLock(tag: String): PowerManager.WakeLock { - val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "$tag:WakeLock") - wakeLock.acquire() - return wakeLock -} - -/** - * Returns true if the given service class is running. - */ -fun Context.isServiceRunning(serviceClass: Class<*>): Boolean { - val className = serviceClass.name - val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - @Suppress("DEPRECATION") - return manager.getRunningServices(Integer.MAX_VALUE) - .any { className == it.service.className } -} - -fun Context.createFileInCacheDir(name: String): File { - val file = File(externalCacheDir, name) - if (file.exists()) { - file.delete() - } - file.createNewFile() - return file -} - private const val TABLET_UI_REQUIRED_SCREEN_WIDTH_DP = 720 // some tablets have screen width like 711dp = 1600px / 2.25 @@ -178,44 +90,6 @@ fun Context.isNightMode(): Boolean { return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES } -fun Context.isOnline(): Boolean { - val activeNetwork = connectivityManager.activeNetwork ?: return false - val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false - val maxTransport = when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> NetworkCapabilities.TRANSPORT_LOWPAN - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> NetworkCapabilities.TRANSPORT_WIFI_AWARE - else -> NetworkCapabilities.TRANSPORT_VPN - } - return (NetworkCapabilities.TRANSPORT_CELLULAR..maxTransport).any(networkCapabilities::hasTransport) -} - -/** - * Returns true if device is connected to Wifi. - */ -fun Context.isConnectedToWifi(): Boolean { - if (!wifiManager.isWifiEnabled) return false - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - val activeNetwork = connectivityManager.activeNetwork ?: return false - val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false - - networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && - networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - } else { - @Suppress("DEPRECATION") - wifiManager.connectionInfo.bssid != null - } -} - -/** - * Gets document size of provided [Uri] - * - * @return document size of [uri] or null if size can't be obtained - */ -fun Context.getUriSize(uri: Uri): Long? { - return UniFile.fromUri(this, uri).length().takeIf { it >= 0 } -} - /** * Gets system's config_navBarNeedsScrim boolean flag added in Android 10, defaults to true. */