commit 289afce4a5cd27b294fdc3577ce249f4a043e621
parent 8e87e7c84d100c55ef05bc475df12b0de01c6233
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sun,  6 Aug 2023 22:07:53 +0200

package refactor

Diffstat:
Mapp/build.gradle.kts | 1+
Mapp/src/main/AndroidManifest.xml | 2+-
Aapp/src/main/kotlin/me/rhunk/snapenhance/ui/MapActivity.kt | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt | 2+-
Aapp/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/PickLocation.kt | 5+++++
Mcore/build.gradle.kts | 1-
Mcore/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt | 3+--
Mcore/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt | 2+-
Acore/src/main/kotlin/me/rhunk/snapenhance/download/data/MediaFilter.kt | 18++++++++++++++++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt | 2+-
Dcore/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt | 105-------------------------------------------------------------------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt | 243-------------------------------------------------------------------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt | 215-------------------------------------------------------------------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt | 18------------------
Dcore/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt | 98-------------------------------------------------------------------------------
15 files changed, 127 insertions(+), 686 deletions(-)

diff --git a/app/build.gradle.kts b/app/build.gradle.kts @@ -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") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml @@ -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" diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/MapActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/MapActivity.kt @@ -0,0 +1,98 @@ +package me.rhunk.snapenhance.ui + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.AlertDialog +import android.content.Context +import android.os.Bundle +import android.view.MotionEvent +import android.widget.Button +import android.widget.EditText +import me.rhunk.snapenhance.core.R +import org.osmdroid.config.Configuration +import org.osmdroid.tileprovider.tilesource.TileSourceFactory +import org.osmdroid.util.GeoPoint +import org.osmdroid.views.MapView +import org.osmdroid.views.Projection +import org.osmdroid.views.overlay.Marker +import org.osmdroid.views.overlay.Overlay + + +class MapActivity : Activity() { + + private lateinit var mapView: MapView + + @SuppressLint("MissingInflatedId", "ResourceType") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val contextBundle = intent.extras?.getBundle("location") ?: return + val locationLatitude = contextBundle.getDouble("latitude") + val locationLongitude = contextBundle.getDouble("longitude") + + Configuration.getInstance().load(applicationContext, getSharedPreferences("osmdroid", Context.MODE_PRIVATE)) + + setContentView(R.layout.map) + + mapView = findViewById(R.id.mapView) + mapView.setMultiTouchControls(true); + mapView.setTileSource(TileSourceFactory.MAPNIK) + + val startPoint = GeoPoint(locationLatitude, locationLongitude) + mapView.controller.setZoom(10.0) + mapView.controller.setCenter(startPoint) + + val marker = Marker(mapView) + marker.isDraggable = true + marker.position = startPoint + marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) + + mapView.overlays.add(object: Overlay() { + override fun onSingleTapConfirmed(e: MotionEvent?, mapView: MapView?): Boolean { + val proj: Projection = mapView!!.projection + val loc = proj.fromPixels(e!!.x.toInt(), e.y.toInt()) as GeoPoint + marker.position = loc + mapView.invalidate() + return true + } + }) + + mapView.overlays.add(marker) + + val applyButton = findViewById<Button>(R.id.apply_location_button) + applyButton.setOnClickListener { + val bundle = Bundle() + bundle.putFloat("latitude", marker.position.latitude.toFloat()) + bundle.putFloat("longitude", marker.position.longitude.toFloat()) + setResult(RESULT_OK, intent.putExtra("location", bundle)) + finish() + } + + val setPreciseLocationButton = findViewById<Button>(R.id.set_precise_location_button) + + setPreciseLocationButton.setOnClickListener { + val locationDialog = layoutInflater.inflate(R.layout.precise_location_dialog, null) + val dialogLatitude = locationDialog.findViewById<EditText>(R.id.dialog_latitude).also { it.setText(marker.position.latitude.toString()) } + val dialogLongitude = locationDialog.findViewById<EditText>(R.id.dialog_longitude).also { it.setText(marker.position.longitude.toString()) } + + AlertDialog.Builder(this) + .setView(locationDialog) + .setTitle("Set a precise location") + .setPositiveButton("Set") { _, _ -> + val latitude = dialogLatitude.text.toString().toDoubleOrNull() + val longitude = dialogLongitude.text.toString().toDoubleOrNull() + if (latitude != null && longitude != null) { + val preciseLocation = GeoPoint(latitude, longitude) + mapView.controller.setCenter(preciseLocation) + marker.position = preciseLocation + mapView.invalidate() + } + }.setNegativeButton("Cancel") { _, _ -> }.show() + } + } + + override fun onDestroy() { + super.onDestroy() + mapView.onDetach() + } +} diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt @@ -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() { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/PickLocation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/PickLocation.kt @@ -0,0 +1,4 @@ +package me.rhunk.snapenhance.ui.manager.sections.features + +class PickLocation { +}+ \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts @@ -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) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt @@ -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()) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt @@ -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 diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/data/MediaFilter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/MediaFilter.kt @@ -0,0 +1,17 @@ +package me.rhunk.snapenhance.download.data + +enum class MediaFilter( + val key: String, + val shouldIgnoreFilter: Boolean = false +) { + NONE("none", true), + PENDING("pending", true), + CHAT_MEDIA("chat_media"), + STORY("story"), + SPOTLIGHT("spotlight"); + + fun matches(source: String?): Boolean { + if (source == null) return false + return source.contains(key, ignoreCase = true) + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt @@ -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 diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt @@ -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) - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt @@ -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) - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt @@ -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() - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt @@ -1,17 +0,0 @@ -package me.rhunk.snapenhance.ui.download - -enum class MediaFilter( - val key: String, - val shouldIgnoreFilter: Boolean = false -) { - NONE("none", true), - PENDING("pending", true), - CHAT_MEDIA("chat_media"), - STORY("story"), - SPOTLIGHT("spotlight"); - - fun matches(source: String?): Boolean { - if (source == null) return false - return source.contains(key, ignoreCase = true) - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt @@ -1,98 +0,0 @@ -package me.rhunk.snapenhance.ui.map - -import android.annotation.SuppressLint -import android.app.Activity -import android.app.AlertDialog -import android.content.Context -import android.os.Bundle -import android.view.MotionEvent -import android.widget.Button -import android.widget.EditText -import me.rhunk.snapenhance.core.R -import org.osmdroid.config.Configuration -import org.osmdroid.tileprovider.tilesource.TileSourceFactory -import org.osmdroid.util.GeoPoint -import org.osmdroid.views.MapView -import org.osmdroid.views.Projection -import org.osmdroid.views.overlay.Marker -import org.osmdroid.views.overlay.Overlay - - -class MapActivity : Activity() { - - private lateinit var mapView: MapView - - @SuppressLint("MissingInflatedId", "ResourceType") - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val contextBundle = intent.extras?.getBundle("location") ?: return - val locationLatitude = contextBundle.getDouble("latitude") - val locationLongitude = contextBundle.getDouble("longitude") - - Configuration.getInstance().load(applicationContext, getSharedPreferences("osmdroid", Context.MODE_PRIVATE)) - - setContentView(R.layout.map) - - mapView = findViewById(R.id.mapView) - mapView.setMultiTouchControls(true); - mapView.setTileSource(TileSourceFactory.MAPNIK) - - val startPoint = GeoPoint(locationLatitude, locationLongitude) - mapView.controller.setZoom(10.0) - mapView.controller.setCenter(startPoint) - - val marker = Marker(mapView) - marker.isDraggable = true - marker.position = startPoint - marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) - - mapView.overlays.add(object: Overlay() { - override fun onSingleTapConfirmed(e: MotionEvent?, mapView: MapView?): Boolean { - val proj: Projection = mapView!!.projection - val loc = proj.fromPixels(e!!.x.toInt(), e.y.toInt()) as GeoPoint - marker.position = loc - mapView.invalidate() - return true - } - }) - - mapView.overlays.add(marker) - - val applyButton = findViewById<Button>(R.id.apply_location_button) - applyButton.setOnClickListener { - val bundle = Bundle() - bundle.putFloat("latitude", marker.position.latitude.toFloat()) - bundle.putFloat("longitude", marker.position.longitude.toFloat()) - setResult(RESULT_OK, intent.putExtra("location", bundle)) - finish() - } - - val setPreciseLocationButton = findViewById<Button>(R.id.set_precise_location_button) - - setPreciseLocationButton.setOnClickListener { - val locationDialog = layoutInflater.inflate(R.layout.precise_location_dialog, null) - val dialogLatitude = locationDialog.findViewById<EditText>(R.id.dialog_latitude).also { it.setText(marker.position.latitude.toString()) } - val dialogLongitude = locationDialog.findViewById<EditText>(R.id.dialog_longitude).also { it.setText(marker.position.longitude.toString()) } - - AlertDialog.Builder(this) - .setView(locationDialog) - .setTitle("Set a precise location") - .setPositiveButton("Set") { _, _ -> - val latitude = dialogLatitude.text.toString().toDoubleOrNull() - val longitude = dialogLongitude.text.toString().toDoubleOrNull() - if (latitude != null && longitude != null) { - val preciseLocation = GeoPoint(latitude, longitude) - mapView.controller.setCenter(preciseLocation) - marker.position = preciseLocation - mapView.invalidate() - } - }.setNegativeButton("Cancel") { _, _ -> }.show() - } - } - - override fun onDestroy() { - super.onDestroy() - mapView.onDetach() - } -}