aboutsummaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/AndroidManifest.xml2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt10
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt84
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt5
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Showcase.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/DragFrame.kt58
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt18
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt7
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt20
-rw-r--r--app/src/main/play/en-US/whatsnew3
-rw-r--r--app/src/main/res/layout/activity_image.xml57
-rw-r--r--app/src/main/res/layout/activity_image_textless.xml19
-rw-r--r--app/src/main/res/values/styles.xml10
-rw-r--r--app/src/main/res/xml/frost_changelog.xml5
-rw-r--r--app/src/web/ts/click_a.ts10
-rw-r--r--app/src/web/ts/horizontal_scrolling.ts61
-rw-r--r--app/src/web/typings/frost.d.ts26
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;