mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
package refactor
This commit is contained in:
@ -101,6 +101,7 @@ dependencies {
|
||||
implementation(libs.gson)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.video)
|
||||
implementation(libs.osmdroid.android)
|
||||
|
||||
debugImplementation("androidx.compose.ui:ui-tooling:1.4.3")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview:1.4.3")
|
||||
|
@ -52,7 +52,7 @@
|
||||
android:theme="@style/AppTheme"
|
||||
android:excludeFromRecents="true" />
|
||||
<activity
|
||||
android:name=".ui.map.MapActivity"
|
||||
android:name=".ui.MapActivity"
|
||||
android:exported="true"
|
||||
android:excludeFromRecents="true" />
|
||||
<activity android:name=".bridge.ForceStartActivity"
|
||||
|
@ -1,4 +1,4 @@
|
||||
package me.rhunk.snapenhance.ui.map
|
||||
package me.rhunk.snapenhance.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
@ -58,7 +58,7 @@ import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.R
|
||||
import me.rhunk.snapenhance.data.FileType
|
||||
import me.rhunk.snapenhance.download.data.DownloadObject
|
||||
import me.rhunk.snapenhance.ui.download.MediaFilter
|
||||
import me.rhunk.snapenhance.download.data.MediaFilter
|
||||
import me.rhunk.snapenhance.ui.manager.Section
|
||||
|
||||
class DownloadsSection : Section() {
|
||||
|
@ -0,0 +1,4 @@
|
||||
package me.rhunk.snapenhance.ui.manager.sections.features
|
||||
|
||||
class PickLocation {
|
||||
}
|
@ -38,7 +38,6 @@ dependencies {
|
||||
implementation(libs.recyclerview)
|
||||
implementation(libs.gson)
|
||||
implementation(libs.ffmpeg.kit)
|
||||
implementation(libs.osmdroid.android)
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.androidx.documentfile)
|
||||
|
||||
|
@ -4,13 +4,12 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import me.rhunk.snapenhance.action.AbstractAction
|
||||
import me.rhunk.snapenhance.core.BuildConfig
|
||||
import me.rhunk.snapenhance.ui.map.MapActivity
|
||||
|
||||
class OpenMap: AbstractAction("action.open_map") {
|
||||
override fun run() {
|
||||
context.runOnUiThread {
|
||||
val mapActivityIntent = Intent()
|
||||
mapActivityIntent.setClassName(BuildConfig.APPLICATION_ID, MapActivity::class.java.name)
|
||||
mapActivityIntent.setClassName(BuildConfig.APPLICATION_ID, "me.rhunk.snapenhance.ui.MapActivity")
|
||||
mapActivityIntent.putExtra("location", Bundle().apply {
|
||||
putDouble("latitude", context.config.spoof.location.latitude.get().toDouble())
|
||||
putDouble("longitude", context.config.spoof.location.longitude.get().toDouble())
|
||||
|
@ -6,7 +6,7 @@ import android.database.sqlite.SQLiteDatabase
|
||||
import me.rhunk.snapenhance.download.data.DownloadMetadata
|
||||
import me.rhunk.snapenhance.download.data.DownloadObject
|
||||
import me.rhunk.snapenhance.download.data.DownloadStage
|
||||
import me.rhunk.snapenhance.ui.download.MediaFilter
|
||||
import me.rhunk.snapenhance.download.data.MediaFilter
|
||||
import me.rhunk.snapenhance.util.SQLiteDatabaseHelper
|
||||
import me.rhunk.snapenhance.util.getIntOrNull
|
||||
import me.rhunk.snapenhance.util.getStringOrNull
|
||||
|
@ -1,4 +1,4 @@
|
||||
package me.rhunk.snapenhance.ui.download
|
||||
package me.rhunk.snapenhance.download.data
|
||||
|
||||
enum class MediaFilter(
|
||||
val key: String,
|
@ -32,7 +32,7 @@ import me.rhunk.snapenhance.hook.HookAdapter
|
||||
import me.rhunk.snapenhance.hook.HookStage
|
||||
import me.rhunk.snapenhance.hook.Hooker
|
||||
import me.rhunk.snapenhance.ui.ViewAppearanceHelper
|
||||
import me.rhunk.snapenhance.ui.download.MediaFilter
|
||||
import me.rhunk.snapenhance.download.data.MediaFilter
|
||||
import me.rhunk.snapenhance.util.download.RemoteMediaResolver
|
||||
import me.rhunk.snapenhance.util.getObjectField
|
||||
import me.rhunk.snapenhance.util.protobuf.ProtoReader
|
||||
|
@ -1,104 +0,0 @@
|
||||
package me.rhunk.snapenhance.ui.download
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ListView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import me.rhunk.snapenhance.SharedContext
|
||||
import me.rhunk.snapenhance.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.core.R
|
||||
import java.io.File
|
||||
|
||||
class ActionListAdapter(
|
||||
private val activity: DownloadManagerActivity,
|
||||
private val layoutId: Int,
|
||||
private val actions: Array<Pair<String, () -> Unit>>
|
||||
) : ArrayAdapter<Pair<String, () -> Unit>>(activity, layoutId, actions) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = convertView ?: activity.layoutInflater.inflate(layoutId, parent, false)
|
||||
val action = actions[position]
|
||||
view.isClickable = true
|
||||
|
||||
view.findViewById<TextView>(R.id.feature_text).text = action.first
|
||||
view.setOnClickListener {
|
||||
action.second()
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
class DebugSettingsLayoutInflater(
|
||||
private val activity: DownloadManagerActivity
|
||||
) {
|
||||
private fun confirmAction(title: String, message: String, action: () -> Unit) {
|
||||
activity.runOnUiThread {
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(SharedContext.translation["button.positive"]) { _, _ ->
|
||||
action()
|
||||
}
|
||||
.setNegativeButton(SharedContext.translation["button.negative"]) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSuccessToast() {
|
||||
Toast.makeText(activity, "Success", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun inflate(parent: ViewGroup) {
|
||||
val debugSettingsLayout = activity.layoutInflater.inflate(R.layout.debug_settings_page, parent, false)
|
||||
|
||||
val debugSettingsTranslation = activity.translation.getCategory("debug_settings_page")
|
||||
|
||||
debugSettingsLayout.findViewById<ImageButton>(R.id.back_button).setOnClickListener {
|
||||
parent.removeView(debugSettingsLayout)
|
||||
}
|
||||
|
||||
debugSettingsLayout.findViewById<TextView>(R.id.title).text = activity.translation["debug_settings"]
|
||||
|
||||
debugSettingsLayout.findViewById<ListView>(R.id.setting_page_list).apply {
|
||||
adapter = ActionListAdapter(activity, R.layout.debug_setting_item, mutableListOf<Pair<String, () -> Unit>>().apply {
|
||||
add(debugSettingsTranslation["clear_cache_title"] to {
|
||||
context.cacheDir.listFiles()?.forEach {
|
||||
it.deleteRecursively()
|
||||
}
|
||||
showSuccessToast()
|
||||
})
|
||||
|
||||
BridgeFileType.values().forEach { fileType ->
|
||||
val actionName = debugSettingsTranslation.format("clear_file_title", "file_name" to fileType.displayName)
|
||||
add(actionName to {
|
||||
confirmAction(actionName, debugSettingsTranslation.format("clear_file_confirmation", "file_name" to fileType.displayName)) {
|
||||
fileType.resolve(context).deleteRecursively()
|
||||
showSuccessToast()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
add(debugSettingsTranslation["reset_all_title"] to {
|
||||
confirmAction(debugSettingsTranslation["reset_all_title"], debugSettingsTranslation["reset_all_confirmation"]) {
|
||||
arrayOf(context.cacheDir, context.filesDir, File(context.dataDir, "databases"), File(context.dataDir, "shared_prefs")).forEach {
|
||||
it.listFiles()?.forEach { file ->
|
||||
file.deleteRecursively()
|
||||
}
|
||||
}
|
||||
showSuccessToast()
|
||||
}
|
||||
})
|
||||
}.toTypedArray())
|
||||
}
|
||||
|
||||
activity.registerBackCallback {
|
||||
parent.removeView(debugSettingsLayout)
|
||||
}
|
||||
|
||||
parent.addView(debugSettingsLayout)
|
||||
}
|
||||
}
|
@ -1,242 +0,0 @@
|
||||
package me.rhunk.snapenhance.ui.download
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import me.rhunk.snapenhance.Logger
|
||||
import me.rhunk.snapenhance.SharedContext
|
||||
import me.rhunk.snapenhance.core.R
|
||||
import me.rhunk.snapenhance.data.FileType
|
||||
import me.rhunk.snapenhance.download.data.DownloadObject
|
||||
import me.rhunk.snapenhance.download.data.DownloadStage
|
||||
import me.rhunk.snapenhance.util.snap.PreviewUtils
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.net.URL
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
class DownloadListAdapter(
|
||||
private val activity: DownloadManagerActivity,
|
||||
private val downloadList: MutableList<DownloadObject>
|
||||
): Adapter<DownloadListAdapter.ViewHolder>() {
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
private val previewJobs = mutableMapOf<Int, Job>()
|
||||
|
||||
inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
||||
val bitmojiIcon: ImageView = view.findViewById(R.id.bitmoji_icon)
|
||||
val title: TextView = view.findViewById(R.id.item_title)
|
||||
val subtitle: TextView = view.findViewById(R.id.item_subtitle)
|
||||
val status: TextView = view.findViewById(R.id.item_status)
|
||||
val actionButton: Button = view.findViewById(R.id.item_action_button)
|
||||
val radius by lazy {
|
||||
view.context.resources.getDimensionPixelSize(R.dimen.download_manager_item_preview_radius)
|
||||
}
|
||||
val viewWidth by lazy {
|
||||
view.resources.displayMetrics.widthPixels
|
||||
}
|
||||
val viewHeight by lazy {
|
||||
view.layoutParams.height
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.download_manager_item, parent, false))
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return downloadList.size
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
private suspend fun handlePreview(download: DownloadObject, holder: ViewHolder) {
|
||||
download.outputFile?.let {
|
||||
val uri = Uri.parse(it)
|
||||
runCatching {
|
||||
if (uri.scheme == "content") {
|
||||
val fileType = activity.contentResolver.openInputStream(uri)!!.use { stream ->
|
||||
FileType.fromInputStream(stream)
|
||||
}
|
||||
fileType to activity.contentResolver.openInputStream(uri)
|
||||
} else {
|
||||
FileType.fromFile(File(it)) to FileInputStream(it)
|
||||
}
|
||||
}.getOrNull()
|
||||
}?.also { (fileType, assetStream) ->
|
||||
val previewBitmap = assetStream?.use { stream ->
|
||||
//don't preview files larger than 30MB
|
||||
if (stream.available() > 30 * 1024 * 1024) return@also
|
||||
|
||||
val tempFile = File.createTempFile("preview", ".${fileType.fileExtension}")
|
||||
tempFile.outputStream().use { output ->
|
||||
stream.copyTo(output)
|
||||
}
|
||||
runCatching {
|
||||
PreviewUtils.createPreviewFromFile(tempFile)?.let { preview ->
|
||||
val offsetY = (preview.height / 2 - holder.viewHeight / 2).coerceAtLeast(0)
|
||||
|
||||
Bitmap.createScaledBitmap(
|
||||
Bitmap.createBitmap(
|
||||
preview, 0, offsetY,
|
||||
preview.width.coerceAtMost(holder.viewWidth),
|
||||
preview.height.coerceAtMost(holder.viewHeight)
|
||||
),
|
||||
holder.viewWidth,
|
||||
holder.viewHeight,
|
||||
false
|
||||
)
|
||||
}
|
||||
}.onFailure {
|
||||
Logger.error("failed to create preview $fileType", it)
|
||||
}.also {
|
||||
tempFile.delete()
|
||||
}.getOrNull()
|
||||
} ?: return@also
|
||||
|
||||
if (coroutineContext.job.isCancelled) return@also
|
||||
Handler(holder.view.context.mainLooper).post {
|
||||
holder.view.background = RoundedBitmapDrawableFactory.create(
|
||||
holder.view.context.resources,
|
||||
previewBitmap
|
||||
).also {
|
||||
it.cornerRadius = holder.radius.toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateViewHolder(download: DownloadObject, holder: ViewHolder) {
|
||||
holder.status.text = download.downloadStage.toString()
|
||||
holder.view.background = holder.view.context.getDrawable(R.drawable.download_manager_item_background)
|
||||
|
||||
coroutineScope.launch {
|
||||
withTimeout(2000) {
|
||||
handlePreview(download, holder)
|
||||
}
|
||||
}
|
||||
|
||||
val isSaved = download.downloadStage == DownloadStage.SAVED
|
||||
//if the download is in progress, the user can cancel it
|
||||
val canInteract = if (download.job != null) !download.downloadStage.isFinalStage || isSaved
|
||||
else isSaved
|
||||
|
||||
holder.status.visibility = if (isSaved) View.GONE else View.VISIBLE
|
||||
|
||||
with(holder.actionButton) {
|
||||
isEnabled = canInteract
|
||||
alpha = if (canInteract) 1f else 0.5f
|
||||
background = context.getDrawable(if (isSaved) R.drawable.action_button_success else R.drawable.action_button_cancel)
|
||||
setTextColor(context.getColor(if (isSaved) R.color.successColor else R.color.actionBarColor))
|
||||
text = if (isSaved)
|
||||
SharedContext.translation["button.open"]
|
||||
else
|
||||
SharedContext.translation["button.cancel"]
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val pendingDownload = downloadList[position]
|
||||
|
||||
pendingDownload.changeListener = { _, _ ->
|
||||
Handler(holder.view.context.mainLooper).post {
|
||||
updateViewHolder(pendingDownload, holder)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
// holder.bitmojiIcon.setImageResource(R.drawable.bitmoji_blank)
|
||||
|
||||
pendingDownload.metadata.iconUrl?.let { url ->
|
||||
thread(start = true) {
|
||||
runCatching {
|
||||
val iconBitmap = URL(url).openStream().use {
|
||||
BitmapFactory.decodeStream(it)
|
||||
}
|
||||
Handler(holder.view.context.mainLooper).post {
|
||||
holder.bitmojiIcon.setImageBitmap(iconBitmap)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
holder.title.visibility = View.GONE
|
||||
holder.subtitle.visibility = View.GONE
|
||||
|
||||
pendingDownload.metadata.mediaDisplayType?.let {
|
||||
holder.title.text = it
|
||||
holder.title.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
pendingDownload.metadata.mediaDisplaySource?.let {
|
||||
holder.subtitle.text = it
|
||||
holder.subtitle.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
holder.actionButton.setOnClickListener {
|
||||
if (pendingDownload.downloadStage != DownloadStage.SAVED) {
|
||||
pendingDownload.cancel()
|
||||
pendingDownload.downloadStage = DownloadStage.CANCELLED
|
||||
updateViewHolder(pendingDownload, holder)
|
||||
notifyItemChanged(position);
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
pendingDownload.outputFile?.let {
|
||||
fun showFileNotFound() {
|
||||
Toast.makeText(holder.view.context, SharedContext.translation["download_manager_activity.file_not_found_toast"], Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
val uri = Uri.parse(it)
|
||||
val fileType = runCatching {
|
||||
if (uri.scheme == "content") {
|
||||
activity.contentResolver.openInputStream(uri)?.use { input ->
|
||||
FileType.fromInputStream(input)
|
||||
} ?: run {
|
||||
showFileNotFound()
|
||||
return@setOnClickListener
|
||||
}
|
||||
} else {
|
||||
val file = File(it)
|
||||
if (!file.exists()) {
|
||||
showFileNotFound()
|
||||
return@setOnClickListener
|
||||
}
|
||||
FileType.fromFile(file)
|
||||
}
|
||||
}.onFailure { exception ->
|
||||
Logger.error("Failed to open file", exception)
|
||||
}.getOrDefault(FileType.UNKNOWN)
|
||||
if (fileType == FileType.UNKNOWN) {
|
||||
showFileNotFound()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
intent.setDataAndType(uri, fileType.mimeType)
|
||||
holder.view.context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
updateViewHolder(pendingDownload, holder)
|
||||
}
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
package me.rhunk.snapenhance.ui.download
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import me.rhunk.snapenhance.SharedContext
|
||||
import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper
|
||||
import me.rhunk.snapenhance.core.BuildConfig
|
||||
import me.rhunk.snapenhance.core.R
|
||||
import me.rhunk.snapenhance.download.data.DownloadObject
|
||||
|
||||
class DownloadManagerActivity : Activity() {
|
||||
lateinit var translation: LocaleWrapper
|
||||
|
||||
private val backCallbacks = mutableListOf<() -> Unit>()
|
||||
private val fetchedDownloadTasks = mutableListOf<DownloadObject>()
|
||||
private var listFilter = MediaFilter.NONE
|
||||
|
||||
private val preferences by lazy {
|
||||
getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
private fun updateNoDownloadText() {
|
||||
findViewById<TextView>(R.id.no_download_title).let {
|
||||
it.text = translation["no_downloads"]
|
||||
it.visibility = if (fetchedDownloadTasks.isEmpty()) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun updateListContent() {
|
||||
fetchedDownloadTasks.clear()
|
||||
fetchedDownloadTasks.addAll(SharedContext.downloadTaskManager.queryFirstTasks(filter = listFilter).values)
|
||||
|
||||
with(findViewById<RecyclerView>(R.id.download_list)) {
|
||||
adapter?.notifyDataSetChanged()
|
||||
scrollToPosition(0)
|
||||
}
|
||||
updateNoDownloadText()
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onBackPressed() {
|
||||
backCallbacks.lastOrNull()?.let {
|
||||
it()
|
||||
backCallbacks.removeLast()
|
||||
} ?: super.onBackPressed()
|
||||
}
|
||||
|
||||
fun registerBackCallback(callback: () -> Unit) {
|
||||
backCallbacks.add(callback)
|
||||
}
|
||||
|
||||
@SuppressLint("BatteryLife", "NotifyDataSetChanged", "SetTextI18n")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
SharedContext.ensureInitialized(this)
|
||||
translation = SharedContext.translation.getCategory("download_manager_activity")
|
||||
|
||||
setContentView(R.layout.download_manager_activity)
|
||||
|
||||
findViewById<TextView>(R.id.title).text = resources.getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME
|
||||
|
||||
findViewById<ImageButton>(R.id.debug_settings_button).setOnClickListener {
|
||||
DebugSettingsLayoutInflater(this).inflate(findViewById(android.R.id.content))
|
||||
}
|
||||
|
||||
with(findViewById<RecyclerView>(R.id.download_list)) {
|
||||
layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this@DownloadManagerActivity)
|
||||
|
||||
adapter = DownloadListAdapter(this@DownloadManagerActivity, fetchedDownloadTasks).apply {
|
||||
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
updateNoDownloadText()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
|
||||
override fun getMovementFlags(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int {
|
||||
val download = fetchedDownloadTasks[viewHolder.absoluteAdapterPosition]
|
||||
return if (download.isJobActive()) {
|
||||
0
|
||||
} else {
|
||||
super.getMovementFlags(recyclerView, viewHolder)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
fetchedDownloadTasks.removeAt(viewHolder.absoluteAdapterPosition).let {
|
||||
SharedContext.downloadTaskManager.removeTask(it)
|
||||
}
|
||||
adapter?.notifyItemRemoved(viewHolder.absoluteAdapterPosition)
|
||||
}
|
||||
}).attachToRecyclerView(this)
|
||||
|
||||
var isLoading = false
|
||||
|
||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
val layoutManager = recyclerView.layoutManager as androidx.recyclerview.widget.LinearLayoutManager
|
||||
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||
|
||||
if (lastVisibleItemPosition == RecyclerView.NO_POSITION) {
|
||||
return
|
||||
}
|
||||
|
||||
if (lastVisibleItemPosition == fetchedDownloadTasks.size - 1 && !isLoading) {
|
||||
isLoading = true
|
||||
|
||||
SharedContext.downloadTaskManager.queryTasks(fetchedDownloadTasks.last().downloadId, filter = listFilter).forEach {
|
||||
fetchedDownloadTasks.add(it.value)
|
||||
adapter?.notifyItemInserted(fetchedDownloadTasks.size - 1)
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
arrayOf(
|
||||
Pair(R.id.all_category, MediaFilter.NONE),
|
||||
Pair(R.id.pending_category, MediaFilter.PENDING),
|
||||
Pair(R.id.snap_category, MediaFilter.CHAT_MEDIA),
|
||||
Pair(R.id.story_category, MediaFilter.STORY),
|
||||
Pair(R.id.spotlight_category, MediaFilter.SPOTLIGHT)
|
||||
).let { categoryPairs ->
|
||||
categoryPairs.forEach { pair ->
|
||||
this@DownloadManagerActivity.findViewById<TextView>(pair.first).apply {
|
||||
text = translation["category.${resources.getResourceEntryName(pair.first)}"]
|
||||
}.setOnClickListener { view ->
|
||||
listFilter = pair.second
|
||||
updateListContent()
|
||||
categoryPairs.map { this@DownloadManagerActivity.findViewById<TextView>(it.first) }.forEach {
|
||||
it.setTextColor(getColor(R.color.primaryText))
|
||||
}
|
||||
(view as TextView).setTextColor(getColor(R.color.focusedCategoryColor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this@DownloadManagerActivity.findViewById<Button>(R.id.remove_all_button).also {
|
||||
it.text = translation["remove_all"]
|
||||
}.setOnClickListener {
|
||||
with(AlertDialog.Builder(this@DownloadManagerActivity)) {
|
||||
setTitle(translation["remove_all_title"])
|
||||
setMessage(translation["remove_all_text"])
|
||||
setPositiveButton(SharedContext.translation["button.positive"]) { _, _ ->
|
||||
SharedContext.downloadTaskManager.removeAllTasks()
|
||||
fetchedDownloadTasks.removeIf {
|
||||
if (it.isJobActive()) it.cancel()
|
||||
true
|
||||
}
|
||||
adapter?.notifyDataSetChanged()
|
||||
updateNoDownloadText()
|
||||
}
|
||||
setNegativeButton(SharedContext.translation["button.negative"]) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateListContent()
|
||||
|
||||
if (!preferences.getBoolean("ask_battery_optimisations", true) ||
|
||||
!(getSystemService(Context.POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(packageName)) return
|
||||
|
||||
with(Intent()) {
|
||||
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||
data = Uri.parse("package:$packageName")
|
||||
startActivityForResult(this, 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == 1) {
|
||||
preferences.edit().putBoolean("ask_battery_optimisations", false).apply()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateListContent()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user