From f3c14a1e5591f9d244c73bc0e53a6e042f5b9a06 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Wed, 25 Oct 2017 17:03:07 -0400 Subject: Fix/pip video (#445) * Test frame wrapper * Update padding bounds * Clear toolbar on hide * Optimize * Update changelog * Disable toolbar rather than remove it * Improve controls and fix toolbar issue * Optimize --- .../pitchedapps/frost/activities/MainActivity.kt | 29 ++++++-- .../com/pitchedapps/frost/dbflow/CookiesDb.kt | 2 - .../frost/services/FrostNotifications.kt | 3 - .../kotlin/com/pitchedapps/frost/settings/Debug.kt | 4 -- .../com/pitchedapps/frost/utils/iab/IabBinder.kt | 2 +- .../com/pitchedapps/frost/views/FrostVideoView.kt | 55 ++++++++++----- .../pitchedapps/frost/views/FrostVideoViewer.kt | 78 +++++++++++++++------- .../kotlin/com/pitchedapps/frost/web/FrostJSI.kt | 1 - .../frost/web/FrostUrlOverlayValidator.kt | 1 - app/src/main/res/layout/activity_frame_wrapper.xml | 5 ++ app/src/main/res/layout/view_video.xml | 31 +++------ app/src/main/res/xml/frost_changelog.xml | 4 +- docs/Changelog.md | 3 +- 13 files changed, 135 insertions(+), 83 deletions(-) create mode 100644 app/src/main/res/layout/activity_frame_wrapper.xml diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt index a346e809..e37c38e9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt @@ -5,6 +5,7 @@ import android.app.AlarmManager import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.graphics.PointF import android.graphics.drawable.ColorDrawable import android.net.Uri import android.os.Bundle @@ -23,6 +24,7 @@ import android.view.Menu import android.view.MenuItem import android.webkit.ValueCallback import android.webkit.WebChromeClient +import android.widget.FrameLayout import ca.allanwang.kau.searchview.SearchItem import ca.allanwang.kau.searchview.SearchView import ca.allanwang.kau.searchview.bindSearchView @@ -47,6 +49,7 @@ import com.pitchedapps.frost.contracts.FileChooserContract import com.pitchedapps.frost.contracts.FileChooserDelegate import com.pitchedapps.frost.dbflow.loadFbCookie import com.pitchedapps.frost.dbflow.loadFbTabs +import com.pitchedapps.frost.enums.MainActivityLayout import com.pitchedapps.frost.enums.Theme import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.FbCookie.switchUser @@ -59,6 +62,7 @@ import com.pitchedapps.frost.utils.iab.FrostBilling import com.pitchedapps.frost.utils.iab.IS_FROST_PRO import com.pitchedapps.frost.utils.iab.IabMain import com.pitchedapps.frost.views.BadgedIcon +import com.pitchedapps.frost.views.FrostVideoContainerContract import com.pitchedapps.frost.views.FrostVideoViewer import com.pitchedapps.frost.views.FrostViewPager import io.reactivex.android.schedulers.AndroidSchedulers @@ -72,9 +76,11 @@ import java.util.concurrent.TimeUnit class MainActivity : BaseActivity(), ActivityWebContract, FileChooserContract by FileChooserDelegate(), + FrostVideoContainerContract, FrostBilling by IabMain() { lateinit var adapter: SectionsPagerAdapter + val frameWrapper: FrameLayout by bindView(R.id.frame_wrapper) val toolbar: Toolbar by bindView(R.id.toolbar) val viewPager: FrostViewPager by bindView(R.id.container) val fab: FloatingActionButton by bindView(R.id.fab) @@ -123,7 +129,8 @@ class MainActivity : BaseActivity(), "Frost id" to Prefs.frostId) } } - setContentView(Prefs.mainActivityLayout.layoutRes) + setContentView(R.layout.activity_frame_wrapper) + frameWrapper.inflate(Prefs.mainActivityLayout.layoutRes, true) setSupportActionBar(toolbar) adapter = SectionsPagerAdapter(supportFragmentManager, loadFbTabs()) viewPager.adapter = adapter @@ -165,11 +172,7 @@ class MainActivity : BaseActivity(), if (videoViewer != null) { videoViewer?.setVideo(url) } else { - val viewer = FrostVideoViewer.showVideo(coordinator.parentViewGroup, url) { - L.d("Video view released") - videoViewer = null - } - videoViewer = viewer + videoViewer = FrostVideoViewer.showVideo(url, this) } } @@ -478,4 +481,18 @@ class MainActivity : BaseActivity(), override fun getPageTitle(position: Int): CharSequence = getString(pages[position].titleId) } + override val lowerVideoPadding: PointF + get() = + if (Prefs.mainActivityLayout == MainActivityLayout.BOTTOM_BAR) + PointF(0f, toolbar.height.toFloat()) + else + PointF(0f, 0f) + + override val videoContainer: FrameLayout + get() = frameWrapper + + override fun onVideoFinished() { + L.d("Video view released") + videoViewer = null + } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt index cbddc77e..8c5806c8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt @@ -3,7 +3,6 @@ package com.pitchedapps.frost.dbflow import android.os.Parcel import android.os.Parcelable import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.pitchedapps.frost.facebook.FACEBOOK_COM import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.frostJsoup @@ -15,7 +14,6 @@ import com.raizlabs.android.dbflow.annotation.Table import com.raizlabs.android.dbflow.kotlinextensions.* import com.raizlabs.android.dbflow.structure.BaseModel import io.reactivex.schedulers.Schedulers -import org.jsoup.Jsoup import paperparcel.PaperParcel import java.net.UnknownHostException diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt index 9bef9170..749aec09 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt @@ -8,13 +8,10 @@ import android.app.job.JobInfo import android.app.job.JobScheduler import android.content.ComponentName import android.content.Context -import android.content.ContextWrapper import android.content.Intent import android.graphics.Bitmap -import android.graphics.Color import android.net.Uri import android.os.Build -import android.support.annotation.RequiresApi import android.support.v4.app.NotificationCompat import android.support.v4.app.NotificationManagerCompat import ca.allanwang.kau.utils.color diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt index 7001275d..fc008765 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt @@ -7,12 +7,8 @@ import ca.allanwang.kau.utils.string import com.afollestad.materialdialogs.MaterialDialog import com.pitchedapps.frost.R import com.pitchedapps.frost.activities.SettingsActivity -import com.pitchedapps.frost.facebook.FACEBOOK_COM -import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.FbItem -import com.pitchedapps.frost.facebook.USER_AGENT_BASIC import com.pitchedapps.frost.injectors.InjectorContract -import com.pitchedapps.frost.injectors.JsActions import com.pitchedapps.frost.injectors.JsAssets import com.pitchedapps.frost.utils.* import com.pitchedapps.frost.web.launchHeadlessHtmlExtractor diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt index 0e537f3a..15c0f9a8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt @@ -76,7 +76,7 @@ abstract class IabBinder : FrostBilling { putItemId(productId) putSuccess(true) if (currency != null) { - putCurrency(Currency.getInstance(Locale.getDefault())) + putCurrency(currency) putItemType(productId) putItemPrice(BigDecimal.valueOf(listing.priceValue)) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt index eaa4e698..9932abae 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt @@ -3,6 +3,7 @@ package com.pitchedapps.frost.views import android.annotation.SuppressLint import android.content.Context import android.graphics.PointF +import android.graphics.RectF import android.util.AttributeSet import android.view.GestureDetector import android.view.MotionEvent @@ -30,7 +31,8 @@ class FrostVideoView @JvmOverloads constructor( var backgroundView: View? = null var onFinishedListener: () -> Unit = {} - var viewerContract: FrostVideoViewerContract? = null + lateinit var viewerContract: FrostVideoViewerContract + lateinit var containerContract: FrostVideoContainerContract private val videoDimensions = PointF(0f, 0f) @@ -49,8 +51,7 @@ class FrostVideoView @JvmOverloads constructor( private val FAST_ANIMATION_DURATION = 100L } - private var upperMinimizedX = 0f - private var upperMinimizedY = 0f + private var videoBounds = RectF() var isExpanded: Boolean = true set(value) { @@ -61,22 +62,25 @@ class FrostVideoView @JvmOverloads constructor( if (field) { animate().scaleXY(1f).translationX(0f).translationY(0f).setDuration(ANIMATION_DURATION).withStartAction { backgroundView?.animate()?.alpha(1f)?.setDuration(ANIMATION_DURATION) - viewerContract?.onFade(1f, ANIMATION_DURATION) + viewerContract.onFade(1f, ANIMATION_DURATION) + }.withEndAction { + if (!isPlaying) showControls() } } else { hideControls() - val height = height - val width = width val scale = Math.min(height / 4f / videoDimensions.y, width / 2.3f / videoDimensions.x) val desiredHeight = scale * videoDimensions.y val desiredWidth = scale * videoDimensions.x - val translationX = (width - MINIMIZED_PADDING - desiredWidth) / 2 - val translationY = (height - MINIMIZED_PADDING - desiredHeight) / 2 - upperMinimizedX = width - desiredWidth - MINIMIZED_PADDING - upperMinimizedY = height - desiredHeight - MINIMIZED_PADDING + val padding = containerContract.lowerVideoPadding + val offsetX = width - MINIMIZED_PADDING - desiredWidth + val offsetY = height - MINIMIZED_PADDING - desiredHeight + val translationX = offsetX / 2 - padding.x + val translationY = offsetY / 2 - padding.y + videoBounds.set(offsetX, offsetY, width.toFloat(), height.toFloat()) + videoBounds.offset(padding.x, padding.y) animate().scaleXY(scale).translationX(translationX).translationY(translationY).setDuration(ANIMATION_DURATION).withStartAction { backgroundView?.animate()?.alpha(0f)?.setDuration(ANIMATION_DURATION) - viewerContract?.onFade(0f, ANIMATION_DURATION) + viewerContract.onFade(0f, ANIMATION_DURATION) } } } @@ -84,10 +88,10 @@ class FrostVideoView @JvmOverloads constructor( init { setOnPreparedListener { start() - showControls() + if (isExpanded) showControls() } setOnCompletionListener { - viewerContract?.onVideoComplete() + viewerContract.onVideoComplete() } setOnTouchListener(FrameTouchListener(context)) v.setOnTouchListener(VideoTouchListener(context)) @@ -104,6 +108,23 @@ class FrostVideoView @JvmOverloads constructor( videoControls?.finishLoading() } + override fun pause() { + audioFocusHelper.abandonFocus() + videoViewImpl.pause() + keepScreenOn = false + if (isExpanded) + videoControls?.updatePlaybackState(false) + } + + override fun restart(): Boolean { + videoUri ?: return false + if (videoViewImpl.restart() && isExpanded) { + videoControls?.showLoading(true) + return true + } + return false + } + private fun hideControls() { if (videoControls?.isVisible == true) videoControls?.hide() @@ -118,14 +139,14 @@ class FrostVideoView @JvmOverloads constructor( fun shouldParentAcceptTouch(ev: MotionEvent): Boolean { if (isExpanded) return true - return ev.x >= upperMinimizedX && ev.y >= upperMinimizedY + return !videoBounds.contains(ev.x, ev.y) } fun destroy() { stopPlayback() if (alpha > 0f) animate().alpha(0f).setDuration(FAST_ANIMATION_DURATION).withEndAction { onFinishedListener() }.withStartAction { - viewerContract?.onFade(0f, FAST_ANIMATION_DURATION) + viewerContract.onFade(0f, FAST_ANIMATION_DURATION) }.start() else onFinishedListener() @@ -154,7 +175,7 @@ class FrostVideoView @JvmOverloads constructor( } override fun onSingleTapConfirmed(event: MotionEvent): Boolean { - if (viewerContract?.onSingleTapConfirmed(event) != true) + if (!viewerContract.onSingleTapConfirmed(event)) toggleControls() return true } @@ -217,7 +238,7 @@ class FrostVideoView @JvmOverloads constructor( } override fun onSingleTapConfirmed(event: MotionEvent): Boolean { - if (viewerContract?.onSingleTapConfirmed(event) == true) return true + if (viewerContract.onSingleTapConfirmed(event)) return true if (!isExpanded) { isExpanded = true return true diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt index 0f7d49e8..7ba49d3c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt @@ -2,13 +2,14 @@ package com.pitchedapps.frost.views import android.content.Context import android.graphics.Color +import android.graphics.PointF import android.net.Uri -import android.support.constraint.ConstraintLayout import android.support.v7.widget.Toolbar import android.util.AttributeSet import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.ImageView import ca.allanwang.kau.utils.* import com.mikepenz.google_material_typeface_library.GoogleMaterial @@ -23,24 +24,7 @@ import com.pitchedapps.frost.utils.frostDownload */ class FrostVideoViewer @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : ConstraintLayout(context, attrs, defStyleAttr), FrostVideoViewerContract { - - override fun onFade(alpha: Float, duration: Long) { - toolbar.animate().alpha(alpha).setDuration(duration) - } - - override fun onSingleTapConfirmed(event: MotionEvent): Boolean { - if (restarter.isVisible) { - restarter.performClick() - return true - } - return false - } - - override fun onVideoComplete() { - video.jumpToStart() - restarter.fadeIn() - } +) : FrameLayout(context, attrs, defStyleAttr), FrostVideoViewerContract { val container: ViewGroup by bindView(R.id.video_container) val toolbar: Toolbar by bindView(R.id.video_toolbar) @@ -48,20 +32,21 @@ class FrostVideoViewer @JvmOverloads constructor( val video: FrostVideoView by bindView(R.id.video) val restarter: ImageView by bindView(R.id.video_restart) - companion object { /** * Simplified binding to add video to layout, and remove it when finished * This is under the assumption that the container allows for overlays, * such as a FrameLayout */ - inline fun showVideo(container: ViewGroup, url: String, crossinline onFinish: () -> Unit): FrostVideoViewer { + fun showVideo(url: String, contract: FrostVideoContainerContract): FrostVideoViewer { + val container = contract.videoContainer val videoViewer = FrostVideoViewer(container.context) container.addView(videoViewer) videoViewer.bringToFront() L.d("Create video view", url) videoViewer.setVideo(url) - videoViewer.video.onFinishedListener = { container.removeView(videoViewer); onFinish() } + videoViewer.video.containerContract = contract + videoViewer.video.onFinishedListener = { container.removeView(videoViewer); contract.onVideoFinished() } return videoViewer } } @@ -91,6 +76,7 @@ class FrostVideoViewer @JvmOverloads constructor( video.restart() restarter.fadeOut { restarter.gone() } } +// toolbar.setOnTouchListener { _, event -> video.shouldParentAcceptTouch(event) } } fun setVideo(url: String) { @@ -103,14 +89,40 @@ class FrostVideoViewer @JvmOverloads constructor( * returns true if consumed, false otherwise */ fun onBackPressed(): Boolean { - if (video.isExpanded) { + parent ?: return false + if (video.isExpanded) video.isExpanded = false + else + video.destroy() + return true + } + + fun pause() = video.pause() + + /* + * ------------------------------------------------------------- + * FrostVideoViewerContract + * ------------------------------------------------------------- + */ + + override fun onFade(alpha: Float, duration: Long) { + toolbar.visible().animate().alpha(alpha).setDuration(duration).withEndAction { + if (alpha == 0f) toolbar.gone() + } + } + + override fun onSingleTapConfirmed(event: MotionEvent): Boolean { + if (restarter.isVisible) { + restarter.performClick() return true } return false } - fun pause() = video.pause() + override fun onVideoComplete() { + video.jumpToStart() + restarter.fadeIn() + } } @@ -118,4 +130,22 @@ interface FrostVideoViewerContract { fun onSingleTapConfirmed(event: MotionEvent): Boolean fun onFade(alpha: Float, duration: Long) fun onVideoComplete() +} + +interface FrostVideoContainerContract { + /** + * Returns extra padding to be added + * from the right and from the bottom respectively + */ + val lowerVideoPadding: PointF + + /** + * Get the container which will hold the video viewer + */ + val videoContainer: FrameLayout + + /** + * Called once the video has stopped & should be removed + */ + fun onVideoFinished() } \ 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 07703dde..4700c894 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt @@ -6,7 +6,6 @@ import android.webkit.JavascriptInterface import com.pitchedapps.frost.activities.MainActivity import com.pitchedapps.frost.dbflow.CookieModel import com.pitchedapps.frost.facebook.FbCookie -import com.pitchedapps.frost.facebook.formattedFbUrl import com.pitchedapps.frost.utils.* import io.reactivex.subjects.Subject 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 bf53c7eb..ac5cde29 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt @@ -1,6 +1,5 @@ package com.pitchedapps.frost.web -import com.pitchedapps.frost.activities.MainActivity import com.pitchedapps.frost.activities.WebOverlayActivity import com.pitchedapps.frost.activities.WebOverlayActivityBase import com.pitchedapps.frost.activities.WebOverlayBasicActivity diff --git a/app/src/main/res/layout/activity_frame_wrapper.xml b/app/src/main/res/layout/activity_frame_wrapper.xml new file mode 100644 index 00000000..585789ef --- /dev/null +++ b/app/src/main/res/layout/activity_frame_wrapper.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_video.xml b/app/src/main/res/layout/view_video.xml index e8782459..b8226ff2 100644 --- a/app/src/main/res/layout/view_video.xml +++ b/app/src/main/res/layout/view_video.xml @@ -1,5 +1,5 @@ - + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clickable="false" /> + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" /> @@ -50,4 +39,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml index 5cc8a2c8..98441a58 100644 --- a/app/src/main/res/xml/frost_changelog.xml +++ b/app/src/main/res/xml/frost_changelog.xml @@ -6,7 +6,7 @@ --> - + @@ -14,7 +14,7 @@ - + diff --git a/docs/Changelog.md b/docs/Changelog.md index e4e67f28..4ac020b0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,6 +1,6 @@ # Changelog -## v1.6.0 +## v1.6.1 * Add Spanish translations * Add French translations * Add German translations @@ -8,6 +8,7 @@ * Add pip video support * Add video downloader * Fix bugs with parsing url queries +* Fix search update from Facebook ## v1.5.9 * Add notification support for Android O -- cgit v1.2.3