diff options
Diffstat (limited to 'app/src/main/kotlin')
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt | 10 | ||||
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivityOrig.kt | 438 |
2 files changed, 5 insertions, 443 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt index c381591e..da991236 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt @@ -50,7 +50,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.pitchedapps.frost.R -import com.pitchedapps.frost.databinding.ActivityImage2Binding +import com.pitchedapps.frost.databinding.ActivityImageBinding import com.pitchedapps.frost.facebook.FB_IMAGE_ID_MATCHER import com.pitchedapps.frost.facebook.get import com.pitchedapps.frost.facebook.requests.call @@ -135,7 +135,7 @@ class ImageActivity : KauBaseActivity() { "${abs(FB_IMAGE_ID_MATCHER.find(imageUrl)[1]?.hashCode() ?: 0)}_${abs(imageUrl.hashCode())}" } - lateinit var binding: ActivityImage2Binding + lateinit var binding: ActivityImageBinding private var bottomBehavior: BottomSheetBehavior<View>? = null private val baseBackgroundColor: Int @@ -173,7 +173,7 @@ class ImageActivity : KauBaseActivity() { L.v { "Launching image with url $result" } result } - binding = ActivityImage2Binding.inflate(layoutInflater) + binding = ActivityImageBinding.inflate(layoutInflater) setContentView(binding.root) binding.init() launch(CoroutineExceptionHandler { _, throwable -> loadError(throwable) }) { @@ -183,7 +183,7 @@ class ImageActivity : KauBaseActivity() { } } - private fun ActivityImage2Binding.showImage(url: String) { + private fun ActivityImageBinding.showImage(url: String) { imagePhoto.showImage(Uri.parse(url)) imagePhoto.setImageShownCallback(object : ImageShownCallback { override fun onThumbnailShown() {} @@ -195,7 +195,7 @@ class ImageActivity : KauBaseActivity() { }) } - private fun ActivityImage2Binding.init() { + private fun ActivityImageBinding.init() { imageContainer.setBackgroundColor(baseBackgroundColor) toolbar.setBackgroundColor(baseBackgroundColor) this@ImageActivity.imageText.also { text -> diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivityOrig.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivityOrig.kt deleted file mode 100644 index 3f6a90fe..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivityOrig.kt +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2018 Allan Wang - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.pitchedapps.frost.activities - -import android.content.Context -import android.content.Intent -import android.content.res.ColorStateList -import android.graphics.Color -import android.os.Bundle -import android.view.View -import android.widget.ImageView -import androidx.customview.widget.ViewDragHelper -import ca.allanwang.kau.internal.KauBaseActivity -import ca.allanwang.kau.logging.KauLoggerExtension -import ca.allanwang.kau.utils.adjustAlpha -import ca.allanwang.kau.utils.colorToForeground -import ca.allanwang.kau.utils.copyFromInputStream -import ca.allanwang.kau.utils.fadeIn -import ca.allanwang.kau.utils.fadeOut -import ca.allanwang.kau.utils.gone -import ca.allanwang.kau.utils.invisible -import ca.allanwang.kau.utils.isHidden -import ca.allanwang.kau.utils.isVisible -import ca.allanwang.kau.utils.materialDialog -import ca.allanwang.kau.utils.scaleXY -import ca.allanwang.kau.utils.setIcon -import ca.allanwang.kau.utils.tint -import ca.allanwang.kau.utils.toast -import ca.allanwang.kau.utils.withAlpha -import ca.allanwang.kau.utils.withMinAlpha -import com.davemorrissey.labs.subscaleview.ImageSource -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.floatingactionbutton.FloatingActionButton -import com.mikepenz.iconics.typeface.IIcon -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial -import com.pitchedapps.frost.R -import com.pitchedapps.frost.databinding.ActivityImageBinding -import com.pitchedapps.frost.facebook.FB_IMAGE_ID_MATCHER -import com.pitchedapps.frost.facebook.get -import com.pitchedapps.frost.facebook.requests.call -import com.pitchedapps.frost.facebook.requests.getFullSizedImageUrl -import com.pitchedapps.frost.facebook.requests.requestBuilder -import com.pitchedapps.frost.injectors.ThemeProvider -import com.pitchedapps.frost.prefs.Prefs -import com.pitchedapps.frost.services.LocalService -import com.pitchedapps.frost.utils.ARG_COOKIE -import com.pitchedapps.frost.utils.ARG_IMAGE_URL -import com.pitchedapps.frost.utils.ARG_TEXT -import com.pitchedapps.frost.utils.ActivityThemer -import com.pitchedapps.frost.utils.frostDownload -import com.pitchedapps.frost.utils.frostSnackbar -import com.pitchedapps.frost.utils.frostUriFromFile -import com.pitchedapps.frost.utils.isIndirectImageUrl -import com.pitchedapps.frost.utils.logFrostEvent -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.io.File -import java.io.FileNotFoundException -import java.io.IOException -import javax.inject.Inject -import kotlin.math.abs -import kotlin.math.max - -/** - * Created by Allan Wang on 2017-07-15. - */ -@AndroidEntryPoint -class ImageActivityOrig : KauBaseActivity() { - - @Inject - lateinit var activityThemer: ActivityThemer - - @Inject - lateinit var prefs: Prefs - - @Inject - lateinit var themeProvider: ThemeProvider - - @Volatile - internal var errorRef: Throwable? = null - - /** - * Reference to the temporary file path - */ - internal var tempFile: File? = null - - private lateinit var dragHelper: ViewDragHelper - - companion object { - /** - * Cache folder to store images - * Linked to the uri provider - */ - private const val IMAGE_FOLDER = "images" - private const val TIME_FORMAT = "yyyyMMdd_HHmmss" - private const val IMG_TAG = "Frost" - const val PURGE_TIME: Long = 10 * 60 * 1000 // 10 min block - private val L = KauLoggerExtension("Image", com.pitchedapps.frost.utils.L) - - fun cacheDir(context: Context): File = - File(context.cacheDir, IMAGE_FOLDER) - } - - private val cookie: String? by lazy { intent.getStringExtra(ARG_COOKIE) } - - val imageUrl: String by lazy { intent.getStringExtra(ARG_IMAGE_URL)?.trim('"') ?: "" } - - private lateinit var trueImageUrl: Deferred<String> - - private val imageText: String? by lazy { intent.getStringExtra(ARG_TEXT) } - - // a unique image identifier based on the id (if it exists), and its hash - private val imageHash: String by lazy { - "${abs(FB_IMAGE_ID_MATCHER.find(imageUrl)[1]?.hashCode() ?: 0)}_${abs(imageUrl.hashCode())}" - } - - lateinit var binding: ActivityImageBinding - private var bottomBehavior: BottomSheetBehavior<View>? = null - - private val baseBackgroundColor: Int - get() = if (prefs.blackMediaBg) Color.BLACK - else themeProvider.bgColor.withMinAlpha(235) - - private fun loadError(e: Throwable) { - if (e.message?.contains("<!DOCTYPE html>") == true) { - applicationContext.toast(R.string.image_not_found) - finish() - return - } - errorRef = e - e.logFrostEvent("Image load error") - with(binding) { - if (imageProgress.isVisible) - imageProgress.fadeOut() - } - tempFile?.delete() - binding.error.fadeIn() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - if (imageUrl.isEmpty()) { - return finish() - } - L.i { "Displaying image" } - trueImageUrl = async(Dispatchers.IO) { - val result = if (!imageUrl.isIndirectImageUrl) imageUrl - else cookie?.getFullSizedImageUrl(imageUrl) ?: imageUrl - if (result != imageUrl) - L.v { "Launching with true url $result" } - result - } - binding = ActivityImageBinding.inflate(layoutInflater) - setContentView(binding.root) - binding.init() - launch(CoroutineExceptionHandler { _, throwable -> loadError(throwable) }) { - val tempFile = downloadTempImage() - this@ImageActivityOrig.tempFile = tempFile - binding.imageProgress.fadeOut() -// binding.imagePhoto.setImageURI(frostUriFromFile(tempFile)) - binding.imagePhoto.setImage(ImageSource.uri(frostUriFromFile(tempFile))) - binding.imagePhoto.animate().alpha(1f).scaleXY(1f).start() - } - } - - private fun ActivityImageBinding.init() { - imageContainer.setBackgroundColor(baseBackgroundColor) - toolbar.setBackgroundColor(baseBackgroundColor) - this@ImageActivityOrig.imageText.also { text -> - if (text.isNullOrBlank()) { - imageText.gone() - } else { - imageText.setTextColor(if (prefs.blackMediaBg) Color.WHITE else themeProvider.textColor) - imageText.setBackgroundColor( - baseBackgroundColor.colorToForeground(0.2f).withAlpha(255) - ) - imageText.text = text - bottomBehavior = BottomSheetBehavior.from<View>(imageText).apply { - addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { - override fun onSlide(bottomSheet: View, slideOffset: Float) { - imageText.alpha = slideOffset / 2 + 0.5f - } - - override fun onStateChanged(bottomSheet: View, newState: Int) { - // No op - } - }) - } - imageText.bringToFront() - } - } - val foregroundTint = if (prefs.blackMediaBg) Color.WHITE else themeProvider.accentColor - - fun ImageView.setState(state: FabStatesOrig) { - setIcon(state.iicon, color = foregroundTint, sizeDp = 24) - setOnClickListener { state.onClick(this@ImageActivityOrig) } - } - - imageProgress.tint(foregroundTint) - error.apply { - invisible() - setState(FabStatesOrig.ERROR) - } - download.apply { - setState(FabStatesOrig.DOWNLOAD) - } - share.apply { - setState(FabStatesOrig.SHARE) - } -// imagePhoto.setOnImageEventListener(object : -// SubsamplingScaleImageView.DefaultOnImageEventListener() { -// override fun onImageLoadError(e: Exception) { -// loadError(e) -// } -// }) - activityThemer.setFrostColors { - themeWindow = false - } - dragHelper = ViewDragHelper.create(imageDrag, ViewDragCallback()).apply { - setEdgeTrackingEnabled(ViewDragHelper.EDGE_TOP or ViewDragHelper.EDGE_BOTTOM) - } - imageDrag.dragHelper = dragHelper - imageDrag.viewToIgnore = imageText - } - - private inner class ViewDragCallback : ViewDragHelper.Callback() { - private var scrollPercent: Float = 0f - private var scrollThreshold = 0.5f - private var scrollToTop = false - - override fun tryCaptureView(view: View, i: Int): Boolean { - L.d { "Try capture ${view.id} $i ${binding.imagePhoto.id} ${binding.imageText.id}" } - return view === binding.imagePhoto - } - - override fun getViewHorizontalDragRange(child: View): Int = 0 - - override fun getViewVerticalDragRange(child: View): Int = child.height - - override fun onViewPositionChanged( - changedView: View, - left: Int, - top: Int, - dx: Int, - dy: Int - ) { - super.onViewPositionChanged(changedView, left, top, dx, dy) - with(binding) { - // make sure that we are using the proper axis - scrollPercent = abs(top.toFloat() / imageContainer.height) - scrollToTop = top < 0 - val multiplier = max(1f - scrollPercent, 0f) - - toolbar.alpha = multiplier - bottomBehavior?.also { - imageText.alpha = - multiplier * (if (it.state == BottomSheetBehavior.STATE_COLLAPSED) 0.5f else 1f) - } - imageContainer.setBackgroundColor(baseBackgroundColor.adjustAlpha(multiplier)) - - if (scrollPercent >= 1) { - if (!isFinishing) { - finish() - overridePendingTransition(0, 0) - } - } - } - } - - override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) { - val overScrolled = scrollPercent > scrollThreshold - val maxOffset = releasedChild.height + 10 - val finalTop = when { - scrollToTop && (overScrolled || yvel < -dragHelper.minVelocity) -> -maxOffset - !scrollToTop && (overScrolled || yvel > dragHelper.minVelocity) -> maxOffset - else -> 0 - } - dragHelper.settleCapturedViewAt(0, finalTop) - binding.imageDrag.invalidate() - } - - override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int = 0 - - override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int = top - } - - private fun getImageExtension(type: String?): String? { - if (type?.startsWith("image/") != true) { - return null - } - return when (type.substring(6)) { - "jpeg" -> "jpg" - "png" -> "png" - "gif" -> "gif" - else -> null - } - } - - @Throws(IOException::class) - private suspend fun downloadTempImage(): File = withContext(Dispatchers.IO) { - - // We assume all images are jpg - // Activity launcher may be able to provide specifics, but this beats sending a request - // just to get the content header - val file = File(cacheDir(this@ImageActivityOrig), "$imageHash.jpg") - - if (!file.isFile) { - file.parentFile?.mkdirs() - file.createNewFile() - } else { - file.setLastModified(System.currentTimeMillis()) - } - - // Forbid overwrites - if (file.isFile && file.length() > 0) { - L.i { "Forbid image overwrite" } - return@withContext file - } - - val response = cookie.requestBuilder() - .url(trueImageUrl.await()) - .get() - .call() - .execute() - - if (!response.isSuccessful) { - throw IOException("Unsuccessful response for image: ${response.peekBody(128).string()}") - } - - val body = response.body ?: throw IOException("Failed to retrieve image body") - file.copyFromInputStream(body.byteStream()) - file - } - - internal suspend fun saveImage() { - frostDownload(cookie = cookie, url = trueImageUrl.await()) - } - - override fun onDestroy() { - LocalService.schedule(this, LocalService.Flag.PURGE_IMAGE) - super.onDestroy() - } -} - -internal enum class FabStatesOrig( - val iicon: IIcon, - val iconColorProvider: (ThemeProvider) -> Int = { it.iconColor }, - val backgroundTint: Int = Int.MAX_VALUE -) { - ERROR(GoogleMaterial.Icon.gmd_error, { Color.WHITE }, Color.RED) { - override fun onClick(activity: ImageActivityOrig) { - val err = - activity.errorRef?.takeIf { it !is FileNotFoundException && it.message != "Image failed to decode using JPEG decoder" } - ?: return - activity.materialDialog { - title(R.string.kau_error) - message(text = err.message ?: err.javaClass.name) - } - } - }, - NOTHING(GoogleMaterial.Icon.gmd_adjust) { - override fun onClick(activity: ImageActivityOrig) {} - }, - DOWNLOAD(GoogleMaterial.Icon.gmd_file_download) { - override fun onClick(activity: ImageActivityOrig) { - activity.launch { - activity.binding.download.fadeOut() - activity.saveImage() - } - } - }, - SHARE(GoogleMaterial.Icon.gmd_share) { - override fun onClick(activity: ImageActivityOrig) { - val file = activity.tempFile ?: return - try { - val photoURI = activity.frostUriFromFile(file) - val intent = Intent(Intent.ACTION_SEND).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK - putExtra(Intent.EXTRA_STREAM, photoURI) - type = "image/png" - } - activity.startActivity(intent) - } catch (e: Exception) { - activity.errorRef = e - e.logFrostEvent("Image share failed") - activity.frostSnackbar(R.string.image_share_failed, activity.themeProvider) - } - } - }; - - /** - * Change the fab look - * If it's in view, give it some animations - * - * TODO investigate what is wrong with fadeScaleTransition - * - * https://github.com/AllanWang/KAU/issues/184 - * - */ - fun update(fab: FloatingActionButton, themeProvider: ThemeProvider) { - val tint = - if (backgroundTint != Int.MAX_VALUE) backgroundTint else themeProvider.accentColor - val iconColor = iconColorProvider(themeProvider) - if (fab.isHidden) { - fab.setIcon(iicon, color = iconColor) - fab.backgroundTintList = ColorStateList.valueOf(tint) - fab.show() - } else { - fab.hide(object : FloatingActionButton.OnVisibilityChangedListener() { - override fun onHidden(fab: FloatingActionButton) { - fab.setIcon(iicon, color = iconColor) - fab.show() - } - }) - } - } - - abstract fun onClick(activity: ImageActivityOrig) -} |