From 6e8cd1413c3caba5bf62a4e3ab902d76a6fcf6db Mon Sep 17 00:00:00 2001 From: UmbrellaalAcademy <165912201+UmbrellaalAcademy@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:52:42 +0530 Subject: [PATCH] Update Functions.kt --- app/src/main/java/ani/dantotsu/Functions.kt | 269 ++++++++++---------- 1 file changed, 136 insertions(+), 133 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt index 1b4b6e4a..0e18075b 100644 --- a/app/src/main/java/ani/dantotsu/Functions.kt +++ b/app/src/main/java/ani/dantotsu/Functions.kt @@ -17,9 +17,9 @@ import android.content.Intent import android.content.pm.PackageManager import android.content.res.Configuration import android.content.res.Resources -import android.content.res.Resources.getSystem import android.graphics.Bitmap import android.graphics.Color +import android.graphics.Rect import android.graphics.drawable.Drawable import android.media.MediaScannerConnection import android.net.ConnectivityManager @@ -45,15 +45,15 @@ import android.telephony.TelephonyManager import android.text.InputFilter import android.text.Spanned import android.util.AttributeSet +import android.util.DisplayMetrics import android.util.TypedValue import android.view.GestureDetector -import android.view.Gravity import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.ViewAnimationUtils import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.view.WindowManager import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AlphaAnimation import android.view.animation.Animation @@ -64,16 +64,13 @@ import android.view.animation.TranslateAnimation import android.widget.ArrayAdapter import android.widget.AutoCompleteTextView import android.widget.DatePicker -import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity +import androidx.annotation.AttrRes +import androidx.annotation.ColorInt import androidx.appcompat.app.AppCompatDelegate -import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat.getSystemService import androidx.core.content.FileProvider import androidx.core.math.MathUtils.clamp @@ -81,10 +78,11 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat +import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams -import androidx.core.view.updatePadding import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.RecyclerView @@ -93,10 +91,10 @@ import ani.dantotsu.BuildConfig.APPLICATION_ID import ani.dantotsu.connections.anilist.Genre import ani.dantotsu.connections.anilist.api.FuzzyDate import ani.dantotsu.connections.bakaupdates.MangaUpdates -import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.databinding.ItemCountDownBinding import ani.dantotsu.media.Media import ani.dantotsu.notifications.IncognitoNotificationClickReceiver +import ani.dantotsu.others.CustomBottomDialog import ani.dantotsu.others.SpoilerPlugin import ani.dantotsu.parsers.ShowResponse import ani.dantotsu.settings.saving.PrefManager @@ -122,7 +120,6 @@ import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.internal.ViewUtils -import com.google.android.material.snackbar.Snackbar import eu.kanade.tachiyomi.data.notification.Notifications import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.Markwon @@ -138,18 +135,17 @@ import io.noties.markwon.image.glide.GlideImagesPlugin import jp.wasabeef.glide.transformations.BlurTransformation import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.MainScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import nl.joery.animatedbottombar.AnimatedBottomBar -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.File import java.io.FileOutputStream import java.io.OutputStream import java.lang.reflect.Field +import java.text.SimpleDateFormat import java.util.Calendar +import java.util.Locale import java.util.TimeZone import java.util.Timer import java.util.TimerTask @@ -162,8 +158,30 @@ import kotlin.math.pow var statusBarHeight = 0 var navBarHeight = 0 -val Int.dp: Float get() = (this / getSystem().displayMetrics.density) -val Float.px: Int get() = (this * getSystem().displayMetrics.density).toInt() + +val Number.toPx get() = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics +).toInt() + +val Number.toDp get() = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_PX, this.toFloat(), Resources.getSystem().displayMetrics +) + +val Number.dpToColumns: Int get() { + val columns = currContext()?.run { + val metrics = DisplayMetrics() + with(getSystemService(Context.WINDOW_SERVICE) as WindowManager) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val bounds: Rect = currentWindowMetrics.bounds + ((bounds.width() / (resources.configuration.densityDpi / 160)) + 0.5)/ this@dpToColumns.toInt() + } else @Suppress("deprecation") { + defaultDisplay.getRealMetrics(metrics) + metrics.widthPixels.toDp / this@dpToColumns.toInt() + } + } + } ?: 1 + return columns.toInt() +} lateinit var bottomBar: AnimatedBottomBar var selectedOption = 1 @@ -189,10 +207,6 @@ fun currActivity(): Activity? { var loadMedia: Int? = null var loadIsMAL = false -val Int.toPx get() = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics -).toInt() - fun initActivity(a: Activity) { val window = a.window WindowCompat.setDecorFitsSystemWindows(window, false) @@ -276,6 +290,16 @@ fun Activity.setNavigationTheme() { } } +@ColorInt +fun Context.getColorFromAttr( + @AttrRes attrColor: Int, + typedValue: TypedValue = TypedValue(), + resolveRefs: Boolean = true +): Int { + theme.resolveAttribute(attrColor, typedValue, resolveRefs) + return typedValue.data +} + /** * Sets clipToPadding false and sets the combined height of navigation bars as bottom padding. * @@ -299,29 +323,48 @@ fun ViewGroup.setBaseline(navBar: AnimatedBottomBar, overlayView: View) { setPadding(paddingLeft, paddingTop, paddingRight, navBarHeight + navBar.measuredHeight + overlayView.measuredHeight) } +/** +* Finish the calling activity and launch it again within the same lifecycle scope +*/ + fun Activity.reloadActivity() { - Refresh.all() finish() startActivity(Intent(this, this::class.java)) - initActivity(this) } -fun Context.restartApp(view: View) { +/** + * Restarts the application from the launch intent and redirects to the calling activity + */ +fun Activity.restartApp() { val mainIntent = Intent.makeRestartActivityTask( packageManager.getLaunchIntentForPackage(this.packageName)!!.component ) val component = ComponentName(this@restartApp.packageName, this@restartApp::class.qualifiedName!!) - Snackbar.make(view, R.string.restart_app, Snackbar.LENGTH_INDEFINITE).apply { - setAction(R.string.do_it) { - this.dismiss() - try { - startActivity(Intent().setComponent(component)) - } catch (anything: Exception) { - startActivity(mainIntent) + try { + startActivity(Intent().setComponent(component)) + } catch (anything: Exception) { + startActivity(mainIntent) + } + finishAndRemoveTask() +} + +suspend fun serverDownDialog(activity: FragmentActivity?) = withContext(Dispatchers.Main) { + activity?.let { + CustomBottomDialog.newInstance().apply { + title = it.getString(R.string.anilist_broken_title) + addView(TextView(activity).apply { + text = it.getString(R.string.anilist_broken) + }) + + setNegativeButton(it.getString(R.string.cancel)) { + dismiss() } - Runtime.getRuntime().exit(0) + + setPositiveButton(it.getString(R.string.close)) { + it.finishAffinity() + } + show(it.supportFragmentManager, "dialog") } - show() } } @@ -397,17 +440,13 @@ fun isOnline(context: Context): Boolean { fun startMainActivity(activity: Activity, bundle: Bundle? = null) { activity.finishAffinity() activity.startActivity( - Intent( - activity, - MainActivity::class.java - ).apply { - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + Intent(activity, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK if (bundle != null) putExtras(bundle) } ) } - class DatePickerFragment(activity: Activity, var date: FuzzyDate = FuzzyDate().getToday()) : DialogFragment(), DatePickerDialog.OnDateSetListener { @@ -605,11 +644,6 @@ fun MutableList.sortByTitle(string: String) { } } -fun String.findBetween(a: String, b: String): String? { - val string = substringAfter(a, "").substringBefore(b, "") - return string.ifEmpty { null } -} - fun ImageView.loadImage(url: String?, size: Int = 0) { if (!url.isNullOrEmpty()) { val localFile = File(url) @@ -621,8 +655,13 @@ fun ImageView.loadImage(url: String?, size: Int = 0) { } } +fun geUrlOrTrolled(url: String?) : String { + return if (PrefManager.getVal(PrefName.DisableMitM)) url ?: "" else + PrefManager.getVal(PrefName.ImageUrl).ifEmpty { url ?: "" } +} + fun ImageView.loadImage(file: FileUrl?, size: Int = 0) { - file?.url = PrefManager.getVal(PrefName.ImageUrl).ifEmpty { file?.url ?: "" } + file?.url = geUrlOrTrolled(file?.url) if (file?.url?.isNotEmpty() == true) { tryWith { if (file.url.startsWith("content://")) { @@ -993,34 +1032,56 @@ fun countDown(media: Media, view: ViewGroup) { fun sinceWhen(media: Media, view: ViewGroup) { if (media.status != "RELEASING" && media.status != "HIATUS") return CoroutineScope(Dispatchers.IO).launch { - MangaUpdates().search(media.mangaName(), media.startDate)?.let { - val latestChapter = MangaUpdates.getLatestChapter(view.context, it) - val timeSince = (System.currentTimeMillis() - - (it.metadata.series.lastUpdated!!.timestamp * 1000)) / 1000 + with (MangaUpdates()) { + findLatestRelease(media)?.let { + var timestamp: Long = it.metadata.series.lastUpdated!!.timestamp - withContext(Dispatchers.Main) { - val v = - ItemCountDownBinding.inflate(LayoutInflater.from(view.context), view, false) - view.addView(v.root, 0) - v.mediaCountdownText.text = - currActivity()?.getString(R.string.chapter_release_timeout, latestChapter) + val latestChapter = getSeries(it)?.let { series -> + timestamp = series.lastUpdated?.timestamp ?: timestamp + currActivity()?.getString(R.string.chapter_number, series.latestChapter) + } ?: { + val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT) + timestamp = dateFormat.parse(it.record.releaseDate)?.time ?: timestamp + getLatestChapter(view.context, it) + } + val predicted = predictRelease(media, timestamp * 1000) + val timeSince = (System.currentTimeMillis() - (timestamp * 1000)) / 1000 - object : CountUpTimer(86400000) { - override fun onTick(second: Int) { - val a = second + timeSince - v.mediaCountdown.text = currActivity()?.getString( - R.string.time_format, - a / 86400, - a % 86400 / 3600, - a % 86400 % 3600 / 60, - a % 86400 % 3600 % 60 + withContext(Dispatchers.Main) { + val v = ItemCountDownBinding.inflate( + LayoutInflater.from(view.context), view, false + ) + view.addView(v.root, 0) + v.mediaCountdownText.text = + currActivity()?.getString(R.string.chapter_release_timeout, latestChapter) + + predicted?.let { time -> + v.mediaPredication.text = currActivity()?.getString( + R.string.chapter_predication, + SimpleDateFormat.getDateTimeInstance().format(time) + .substringBeforeLast(' ').substringBeforeLast(' ') + // SimpleDateFormat parses MMMM to MO5 for May. This is a workaround ) + v.mediaPredication.isVisible = true } - override fun onFinish() { - // The legend will never die. - } - }.start() + object : CountUpTimer(86400000) { + override fun onTick(second: Int) { + val a = second + timeSince + v.mediaCountdown.text = currActivity()?.getString( + R.string.time_format, + a / 86400, + a % 86400 / 3600, + a % 86400 % 3600 / 60, + a % 86400 % 3600 % 60 + ) + } + + override fun onFinish() { + // The legend will never die. + } + }.start() + } } } } @@ -1103,68 +1164,6 @@ class EmptyAdapter(private val count: Int) : RecyclerView.Adapter { - gravity = (Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM) - width = WRAP_CONTENT - } - translationY = -(navBarHeight.dp + 32f) - translationZ = 32f - updatePadding(16f.px, right = 16f.px) - setOnClickListener { - snackBar.dismiss() - } - setOnLongClickListener { - copyToClipboard(clipboard ?: s, false) - toast(getString(R.string.copied_to_clipboard)) - true - } - } - snackBar.show() - } - return snackBar - } - Logger.log(s) - } - } catch (e: Exception) { - Logger.log(e) - Injekt.get().logException(e) - } - return null -} - -fun snackString(r: Int, activity: Activity? = null, clipboard: String? = null): Snackbar? { - return snackString(getAppString(r), activity, clipboard) -} - open class NoPaddingArrayAdapter(context: Context, layoutId: Int, items: List) : ArrayAdapter(context, layoutId, items) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { @@ -1329,12 +1328,12 @@ fun openSettings(context: Context, channelId: String?): Boolean { } suspend fun View.pop() { - currActivity()?.runOnUiThread { + withContext(Dispatchers.Main) { ObjectAnimator.ofFloat(this@pop, "scaleX", 1f, 1.25f).setDuration(120).start() ObjectAnimator.ofFloat(this@pop, "scaleY", 1f, 1.25f).setDuration(120).start() } delay(120) - currActivity()?.runOnUiThread { + withContext(Dispatchers.Main) { ObjectAnimator.ofFloat(this@pop, "scaleX", 1.25f, 1f).setDuration(100).start() ObjectAnimator.ofFloat(this@pop, "scaleY", 1.25f, 1f).setDuration(100).start() } @@ -1348,7 +1347,7 @@ fun blurImage(imageView: ImageView, banner: String?) { if (PrefManager.getVal(PrefName.BlurBanners)) { val context = imageView.context if (!(context as Activity).isDestroyed) { - val url = PrefManager.getVal(PrefName.ImageUrl).ifEmpty { banner } + val url = geUrlOrTrolled(banner) Glide.with(context as Context) .load( if (banner.startsWith("http")) GlideUrl(url) else if (banner.startsWith("content://")) Uri.parse( @@ -1356,7 +1355,11 @@ fun blurImage(imageView: ImageView, banner: String?) { ) else File(url) ) .diskCacheStrategy(DiskCacheStrategy.RESOURCE).override(400) - .apply(RequestOptions.bitmapTransform(BlurTransformation(radius, sampling))) + .apply(if (PrefManager.getVal(PrefName.ImageUrl).isEmpty()) { + RequestOptions.noTransformation() + } else { + RequestOptions.bitmapTransform(BlurTransformation(radius, sampling)) + }) .into(imageView) } } else {