diff options
Diffstat (limited to 'app/src')
23 files changed, 327 insertions, 86 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bd8776d1..e4fc0415 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -147,7 +147,7 @@ android:theme="@style/Kau.About" /> <activity android:name=".activities.ImageActivity" - android:theme="@style/FrostTheme.Transparent" /> + android:theme="@style/FrostTheme.Overlay" /> <activity android:name=".activities.DebugActivity" /> <service diff --git a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt index cf8acdd3..61b4a194 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt @@ -86,7 +86,12 @@ class StartActivity : KauBaseActivity() { FbCookie.switchBackUser() val cookies = ArrayList(cookieDao.selectAll()) L.i { "Cookies loaded at time ${System.currentTimeMillis()}" } - L._d { "Cookies: ${cookies.joinToString("\t", transform = CookieEntity::toSensitiveString)}" } + L._d { + "Cookies: ${cookies.joinToString( + "\t", + transform = CookieEntity::toSensitiveString + )}" + } loadAssets() authDefer.await() when { @@ -112,7 +117,8 @@ class StartActivity : KauBaseActivity() { */ private suspend fun migrate() = withContext(Dispatchers.IO) { if (cookieDao.selectAll().isNotEmpty()) return@withContext - val cookies = (select from CookieModel::class).queryList().map { CookieEntity(it.id, it.name, it.cookie) } + val cookies = (select from CookieModel::class).queryList() + .map { CookieEntity(it.id, it.name, it.cookie) } if (cookies.isNotEmpty()) { cookieDao.save(cookies) L._d { "Migrated cookies ${cookieDao.selectAll()}" } 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 814ce778..c7d31032 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt @@ -23,11 +23,13 @@ import android.graphics.Color import android.os.Bundle import android.os.Environment import android.view.View +import androidx.customview.widget.ViewDragHelper import ca.allanwang.kau.internal.KauBaseActivity import ca.allanwang.kau.logging.KauLoggerExtension import ca.allanwang.kau.mediapicker.scanMedia import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE import ca.allanwang.kau.permissions.kauRequestPermissions +import ca.allanwang.kau.utils.adjustAlpha import ca.allanwang.kau.utils.colorToForeground import ca.allanwang.kau.utils.copyFromInputStream import ca.allanwang.kau.utils.fadeOut @@ -75,6 +77,8 @@ import java.io.IOException import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import kotlin.math.abs +import kotlin.math.max /** * Created by Allan Wang on 2017-07-15. @@ -103,6 +107,8 @@ class ImageActivity : KauBaseActivity() { value.update(image_fab) } + private lateinit var dragHelper: ViewDragHelper + companion object { /** * Cache folder to store images @@ -135,6 +141,9 @@ class ImageActivity : KauBaseActivity() { )}_${Math.abs(imageUrl.hashCode())}" } + private val baseBackgroundColor = if (Prefs.blackMediaBg) Color.BLACK + else Prefs.bgColor.withMinAlpha(235) + private fun loadError(e: Throwable) { errorRef = e e.logFrostEvent("Image load error") @@ -158,12 +167,10 @@ class ImageActivity : KauBaseActivity() { result } - val layout = if (!imageText.isNullOrBlank()) R.layout.activity_image else R.layout.activity_image_textless + val layout = + if (!imageText.isNullOrBlank()) R.layout.activity_image else R.layout.activity_image_textless setContentView(layout) - image_container.setBackgroundColor( - if (Prefs.blackMediaBg) Color.BLACK - else Prefs.bgColor.withMinAlpha(222) - ) + image_container.setBackgroundColor(baseBackgroundColor) image_text?.setTextColor(if (Prefs.blackMediaBg) Color.WHITE else Prefs.textColor) image_text?.setBackgroundColor( (if (Prefs.blackMediaBg) Color.BLACK else Prefs.bgColor) @@ -171,7 +178,8 @@ class ImageActivity : KauBaseActivity() { ) image_text?.text = imageText image_progress.tint(if (Prefs.blackMediaBg) Color.WHITE else Prefs.accentColor) - image_panel?.addPanelSlideListener(object : SlidingUpPanelLayout.SimplePanelSlideListener() { + image_panel?.addPanelSlideListener(object : + SlidingUpPanelLayout.SimplePanelSlideListener() { override fun onPanelSlide(panel: View, slideOffset: Float) { if (slideOffset == 0f && !image_fab.isShown) image_fab.show() else if (slideOffset != 0f && image_fab.isShown) image_fab.hide() @@ -179,7 +187,8 @@ class ImageActivity : KauBaseActivity() { } }) image_fab.setOnClickListener { fabAction.onClick(this) } - image_photo.setOnImageEventListener(object : SubsamplingScaleImageView.DefaultOnImageEventListener() { + image_photo.setOnImageEventListener(object : + SubsamplingScaleImageView.DefaultOnImageEventListener() { override fun onImageLoadError(e: Exception) { loadError(e) } @@ -194,14 +203,73 @@ class ImageActivity : KauBaseActivity() { image_photo.setImage(ImageSource.uri(frostUriFromFile(tempFile))) fabAction = FabStates.DOWNLOAD image_photo.animate().alpha(1f).scaleXY(1f).start() + dragHelper = ViewDragHelper.create(image_drag, ViewDragCallback()).apply { + setEdgeTrackingEnabled(ViewDragHelper.EDGE_TOP or ViewDragHelper.EDGE_BOTTOM) + } + image_drag.dragHelper = dragHelper + } + } + + 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 { + return true + } + + 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) + //make sure that we are using the proper axis + scrollPercent = abs(top.toFloat() / image_container.height) + scrollToTop = top < 0 + val multiplier = max(1f - scrollPercent, 0f) + image_fab.alpha = multiplier + image_panel?.alpha = multiplier + image_container.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) + image_drag.invalidate() + } + + override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int = 0 + + override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int = top } @Throws(IOException::class) private fun createPublicMediaFile(): File { val timeStamp = SimpleDateFormat(TIME_FORMAT, Locale.getDefault()).format(Date()) val imageFileName = "${IMG_TAG}_${timeStamp}_" - val storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + val storageDir = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) val frostDir = File(storageDir, IMG_TAG) if (!frostDir.exists()) frostDir.mkdirs() return File.createTempFile(imageFileName, IMG_EXTENSION, frostDir) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt index 5b2d01d4..b3ef9bd4 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt @@ -28,6 +28,7 @@ import android.webkit.WebChromeClient import android.widget.FrameLayout import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout +import ca.allanwang.kau.swipe.SwipeBackContract import ca.allanwang.kau.swipe.kauSwipeOnCreate import ca.allanwang.kau.swipe.kauSwipeOnDestroy import ca.allanwang.kau.utils.ContextHelper @@ -168,6 +169,8 @@ open class WebOverlayActivityBase(private val forceDesktopAgent: Boolean) : Base private inline val urlTest: String? get() = intent.getStringExtra(ARG_URL) ?: intent.dataString + lateinit var swipeBack: SwipeBackContract + /** * Nonnull variant; verify by checking [urlTest] */ @@ -235,7 +238,7 @@ open class WebOverlayActivityBase(private val forceDesktopAgent: Boolean) : Base FrostRunnable.propagate(this, intent) L.v { "Done propagation" } - kauSwipeOnCreate { + swipeBack = kauSwipeOnCreate { if (!Prefs.overlayFullScreenSwipe) edgeSize = 20.dpToPx transitionSystemBars = false } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt index 18fd0673..a8cd13ae 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt @@ -40,8 +40,6 @@ enum class CssHider(vararg val items: String) : InjectorContract { NON_RECENT("article:not([data-store*=actor_name])"), STORIES( "#MStoriesTray", - // Main article wrapper; this may end up excluding more than just stories - "article:not([data-store-id])", // Sub element with just the tray; title is not a part of this "[data-testid=story_tray]" ) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt index ad42418e..d46422b8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt @@ -34,7 +34,7 @@ import java.util.Locale */ enum class JsAssets : InjectorContract { MENU, CLICK_A, CONTEXT_A, MEDIA, HEADER_BADGES, TEXTAREA_LISTENER, NOTIF_MSG, - DOCUMENT_WATCHER + DOCUMENT_WATCHER, HORIZONTAL_SCROLLING ; @VisibleForTesting diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt index 7656a081..51eb856b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -19,7 +19,6 @@ package com.pitchedapps.frost.utils import android.graphics.Color import ca.allanwang.kau.kotlin.lazyResettable import ca.allanwang.kau.kpref.KPref -import ca.allanwang.kau.kpref.kpref import ca.allanwang.kau.utils.colorToForeground import ca.allanwang.kau.utils.isColorVisibleOn import ca.allanwang.kau.utils.withAlpha diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Showcase.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Showcase.kt index 27016018..df48bfbc 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Showcase.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Showcase.kt @@ -17,8 +17,6 @@ package com.pitchedapps.frost.utils import ca.allanwang.kau.kpref.KPref -import ca.allanwang.kau.kpref.kpref -import ca.allanwang.kau.kpref.kprefSingle /** * Created by Allan Wang on 2017-07-03. diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt index 8ad0d432..0574aeae 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt @@ -327,6 +327,10 @@ val dependentSegments = arrayOf( * Editing images */ "/confirmation/?", + /** + * Remove entry from "people you may know" + */ + "/pymk/xout/", /* * Facebook messages have the following cases for the tid query * mid* or id* for newer threads, which can be launched in new windows diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/DragFrame.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/DragFrame.kt new file mode 100644 index 00000000..c2f36536 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/DragFrame.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2019 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.views + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import android.widget.FrameLayout +import androidx.core.view.ViewCompat +import androidx.customview.widget.ViewDragHelper + +class DragFrame @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + var dragHelper: ViewDragHelper? = null + + override fun onInterceptTouchEvent(event: MotionEvent): Boolean { + return try { + dragHelper?.shouldInterceptTouchEvent(event) ?: false + } catch (e: Exception) { + false + } + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + try { + dragHelper?.processTouchEvent(event) ?: return false + } catch (e: Exception) { + return false + } + return true + } + + override fun computeScroll() { + super.computeScroll() + if (dragHelper?.continueSettling(true) == true) { + ViewCompat.postInvalidateOnAnimation(this) + } + } +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt index 2154ce2d..5c7c58f1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2019 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.views import android.content.Context @@ -84,4 +100,4 @@ class SwipeRefreshLayout @JvmOverloads constructor(context: Context, attrs: Attr * Alias for adding on refresh listener */ interface OnRefreshListener : SwipeRefreshLayout.OnRefreshListener -}
\ No newline at end of file +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt index 0d980ba0..376257d4 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt @@ -19,6 +19,7 @@ package com.pitchedapps.frost.web import android.content.Context import android.webkit.JavascriptInterface import com.pitchedapps.frost.activities.MainActivity +import com.pitchedapps.frost.activities.WebOverlayActivityBase import com.pitchedapps.frost.contracts.MainActivityContract import com.pitchedapps.frost.contracts.VideoViewHolder import com.pitchedapps.frost.db.CookieEntity @@ -140,4 +141,10 @@ class FrostJSI(val web: FrostWebView) { html ?: return header?.offer(html) } + + @JavascriptInterface + fun allowHorizontalScrolling(enable: Boolean) { + activity?.viewPager?.enableSwipe = enable + (context as? WebOverlayActivityBase)?.swipeBack?.disallowIntercept = !enable + } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt index b06208a6..4aa43b49 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt @@ -78,16 +78,16 @@ fun FrostWebView.requestWebOverlay(url: String): Boolean { val shouldUseDesktop = url.formattedFbUrl.shouldUseDesktopAgent //already overlay; manage user agent if (userAgentString != USER_AGENT_DESKTOP && shouldUseDesktop) { - L.i { "Switch to desktop agent overlay" } + L._i { "Switch to desktop agent overlay" } context.launchWebOverlayDesktop(url) return true } if (userAgentString == USER_AGENT_DESKTOP && !shouldUseDesktop) { - L.i { "Switch from desktop agent" } + L._i { "Switch from desktop agent" } context.launchWebOverlay(url) return true } - L.i { "return false switch" } + L._i { "return false switch" } return false } L.v { "Request web overlay passed" } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt index 003ed7f9..85914f33 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt @@ -102,6 +102,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { Prefs.aggressiveRecents ), JsAssets.DOCUMENT_WATCHER, + JsAssets.HORIZONTAL_SCROLLING, JsAssets.CLICK_A, CssHider.ADS.maybe(!Prefs.showFacebookAds), JsAssets.CONTEXT_A, diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt index 6e7fc0b6..da0ebf0d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt @@ -72,8 +72,6 @@ open class NestedWebView @JvmOverloads constructor( // NestedPreScroll if (dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) { deltaY -= scrollConsumed[1] - event.offsetLocation(0f, -scrollOffset[1].toFloat()) - nestedOffsetY += scrollOffset[1] } lastY = eventY - scrollOffset[1] returnValue = super.onTouchEvent(event) @@ -122,10 +120,20 @@ open class NestedWebView @JvmOverloads constructor( dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray? - ) = childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow) - - final override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?) = - childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow) + ) = childHelper.dispatchNestedScroll( + dxConsumed, + dyConsumed, + dxUnconsumed, + dyUnconsumed, + offsetInWindow + ) + + final override fun dispatchNestedPreScroll( + dx: Int, + dy: Int, + consumed: IntArray?, + offsetInWindow: IntArray? + ) = childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow) final override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean) = childHelper.dispatchNestedFling(velocityX, velocityY, consumed) diff --git a/app/src/main/play/en-US/whatsnew b/app/src/main/play/en-US/whatsnew index cd43de9e..9ae7372d 100644 --- a/app/src/main/play/en-US/whatsnew +++ b/app/src/main/play/en-US/whatsnew @@ -1,4 +1,5 @@ v2.3.1 * Hide all story panels if enabled -* Prevent swipe to refresh if not at the very top
\ No newline at end of file +* Prevent swipe to refresh if not at the very top +* Add vertical swipe to dismiss when viewing images
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_image.xml b/app/src/main/res/layout/activity_image.xml index 4e6d4ce1..85837fc3 100644 --- a/app/src/main/res/layout/activity_image.xml +++ b/app/src/main/res/layout/activity_image.xml @@ -12,31 +12,6 @@ android:layout_height="wrap_content" android:layout_gravity="center" /> - <com.sothree.slidinguppanel.SlidingUpPanelLayout - android:id="@+id/image_panel" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="bottom" - app:umanoPanelHeight="44dp" - app:umanoShadowHeight="0dp"> - - <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView - android:id="@+id/image_photo" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:alpha="0" - android:scaleX="0.9" - android:scaleY="0.9" /> - - <TextView - android:id="@+id/image_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:alpha="0.5" - android:padding="@dimen/kau_padding_normal" /> - - </com.sothree.slidinguppanel.SlidingUpPanelLayout> - <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/image_fab" android:layout_width="wrap_content" @@ -45,4 +20,36 @@ android:layout_margin="@dimen/kau_fab_margin" android:visibility="invisible" /> + <com.pitchedapps.frost.views.DragFrame + android:id="@+id/image_drag" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.sothree.slidinguppanel.SlidingUpPanelLayout + android:id="@+id/image_panel" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="bottom" + app:umanoPanelHeight="44dp" + app:umanoShadowHeight="0dp"> + + <TextView + android:id="@+id/image_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:alpha="0.5" + android:padding="@dimen/kau_padding_normal" /> + + <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView + android:id="@+id/image_photo" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:alpha="0" + android:scaleX="0.9" + android:scaleY="0.9" /> + + </com.sothree.slidinguppanel.SlidingUpPanelLayout> + + </com.pitchedapps.frost.views.DragFrame> + </androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_image_textless.xml b/app/src/main/res/layout/activity_image_textless.xml index e047b23d..41fd43a2 100644 --- a/app/src/main/res/layout/activity_image_textless.xml +++ b/app/src/main/res/layout/activity_image_textless.xml @@ -11,13 +11,20 @@ android:layout_height="wrap_content" android:layout_gravity="center" /> - <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView - android:id="@+id/image_photo" + <com.pitchedapps.frost.views.DragFrame + android:id="@+id/image_drag" android:layout_width="match_parent" - android:layout_height="match_parent" - android:alpha="0" - android:scaleX="0.9" - android:scaleY="0.9" /> + android:layout_height="match_parent"> + + <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView + android:id="@+id/image_photo" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:alpha="0" + android:scaleX="0.9" + android:scaleY="0.9" /> + + </com.pitchedapps.frost.views.DragFrame> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/image_fab" diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 7816533b..33f36174 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -38,6 +38,9 @@ </style> <style name="FrostTheme.Overlay"> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:backgroundDimEnabled">false</item> + <item name="android:colorBackgroundCacheHint">@null</item> <item name="android:windowIsTranslucent">true</item> </style> @@ -49,12 +52,7 @@ <item name="android:windowAnimationStyle">@style/KauFadeInFadeOut</item> </style> - <style name="FrostTheme.Video" parent="FrostTheme.Overlay.Fade"> - <item name="android:windowBackground">@android:color/transparent</item> - <item name="android:backgroundDimEnabled">false</item> - <item name="android:colorBackgroundCacheHint">@null</item> - <item name="android:windowIsTranslucent">true</item> - </style> + <style name="FrostTheme.Video" parent="FrostTheme.Overlay.Fade" /> <style name="FrostTheme.Settings" parent="FrostTheme"> <item name="android:windowAnimationStyle">@style/KauSlideInFadeOut</item> diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml index c059c532..32fc108b 100644 --- a/app/src/main/res/xml/frost_changelog.xml +++ b/app/src/main/res/xml/frost_changelog.xml @@ -9,7 +9,7 @@ <version title="v2.3.1" /> <item text="Hide all story panels if enabled" /> <item text="Prevent swipe to refresh if not at the very top" /> - <item text="" /> + <item text="Add vertical swipe to dismiss when viewing images" /> <item text="" /> <item text="" /> @@ -21,9 +21,6 @@ <item text="Add fingerprint unlock screen" /> <item text="Fix messenger redirect" /> <item text="Lots of internal updates" /> - <item text="" /> - <item text="" /> - <item text="" /> <version title="v2.2.4" /> <item text="Show top bar to allow sharing posts" /> diff --git a/app/src/web/ts/click_a.ts b/app/src/web/ts/click_a.ts index 8d1d545b..1fd63683 100644 --- a/app/src/web/ts/click_a.ts +++ b/app/src/web/ts/click_a.ts @@ -91,14 +91,16 @@ prevented = true; }; + const _frostAllowClick = () => { + prevented = false; + clearTimeout(clickTimeout) + }; + document.addEventListener('click', _frostAClick, true); let clickTimeout: number | undefined = undefined; document.addEventListener('touchstart', () => { clickTimeout = setTimeout(_frostPreventClick, 400); }, true); - document.addEventListener('touchend', () => { - prevented = false; - clearTimeout(clickTimeout) - }, true); + document.addEventListener('touchend', _frostAllowClick, true); }).call(undefined); diff --git a/app/src/web/ts/horizontal_scrolling.ts b/app/src/web/ts/horizontal_scrolling.ts new file mode 100644 index 00000000..b104725e --- /dev/null +++ b/app/src/web/ts/horizontal_scrolling.ts @@ -0,0 +1,61 @@ +(function () { + + /** + * Go up at most [depth] times, to retrieve a parent matching the provided predicate + * If one is found, it is returned immediately. + * Otherwise, null is returned. + */ + function _parentEl(el: HTMLElement, depth: number, predicate: (el: HTMLElement) => boolean): HTMLElement | null { + for (let i = 0; i < depth + 1; i++) { + if (predicate(el)) { + return el + } + const parent = el.parentElement; + if (!parent) { + return null + } + el = parent + } + return null + } + + /** + * Check if element can scroll horizontally. + * We primarily rely on the overflow-x field. + * For performance reasons, we will check scrollWidth first to see if scrolling is a possibility + */ + function _canScrollHorizontally(el: HTMLElement): boolean { + /* + * Sometimes the offsetWidth is off by < 10px. We use the multiplier + * since the trays are typically more than 2 times greater + */ + if (el.scrollWidth > el.offsetWidth * 1.2) { + return true + } + const styles = window.getComputedStyle(el); + /* + * Works well in testing, but on mobile it just shows 'visible' + */ + return styles.overflowX === 'scroll'; + } + + const _frostCheckHorizontalScrolling = (e: Event) => { + const target = e.target || e.currentTarget || e.srcElement; + if (!(target instanceof HTMLElement)) { + return + } + const scrollable = _parentEl(target, 5, _canScrollHorizontally) !== null; + if (scrollable) { + console.log('Pause horizontal scrolling'); + Frost.allowHorizontalScrolling(false); + } + }; + + const _frostResetHorizontalScrolling = (e: Event) => { + Frost.allowHorizontalScrolling(true) + }; + + document.addEventListener('touchstart', _frostCheckHorizontalScrolling, true); + document.addEventListener('touchend', _frostResetHorizontalScrolling, true); +}).call(undefined); + diff --git a/app/src/web/typings/frost.d.ts b/app/src/web/typings/frost.d.ts index 8f60c9dd..ae7c97ab 100644 --- a/app/src/web/typings/frost.d.ts +++ b/app/src/web/typings/frost.d.ts @@ -1,27 +1,29 @@ declare interface FrostJSI { - loadUrl(url: string | null): boolean + loadUrl(url: string | null): boolean - loadVideo(url: string | null, isGif: boolean): boolean + loadVideo(url: string | null, isGif: boolean): boolean - reloadBaseUrl(animate: boolean) + reloadBaseUrl(animate: boolean) - contextMenu(url: string | null, text: string | null) + contextMenu(url: string | null, text: string | null) - longClick(start: boolean) + longClick(start: boolean) - disableSwipeRefresh(disable: boolean) + disableSwipeRefresh(disable: boolean) - loadLogin() + loadLogin() - loadImage(imageUrl: string, text: string | null) + loadImage(imageUrl: string, text: string | null) - emit(flag: number) + emit(flag: number) - isReady() + isReady() - handleHtml(html: string | null) + handleHtml(html: string | null) - handleHeader(html: string | null) + handleHeader(html: string | null) + + allowHorizontalScrolling(enable: boolean) } declare var Frost: FrostJSI; |