Skip to content

Commit

Permalink
Add Duck Player prime modal
Browse files Browse the repository at this point in the history
  • Loading branch information
CrisBarreiro committed Aug 7, 2024
1 parent 742ea02 commit e815f2e
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 8 deletions.
61 changes: 53 additions & 8 deletions app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import android.annotation.SuppressLint
import android.app.Activity.RESULT_OK
import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.*
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
Expand All @@ -31,7 +35,12 @@ import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.*
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.print.PrintAttributes
import android.print.PrintManager
import android.provider.MediaStore
Expand All @@ -40,8 +49,14 @@ import android.text.Spannable
import android.text.SpannableString
import android.text.Spanned
import android.text.style.StyleSpan
import android.view.*
import android.view.View.*
import android.view.ContextMenu
import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import android.view.View.GONE
import android.view.View.OnFocusChangeListener
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.view.inputmethod.EditorInfo
import android.webkit.PermissionRequest
Expand All @@ -53,7 +68,9 @@ import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebView.FindListener
import android.webkit.WebView.HitTestResult
import android.webkit.WebView.HitTestResult.*
import android.webkit.WebView.HitTestResult.IMAGE_TYPE
import android.webkit.WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
import android.webkit.WebView.HitTestResult.UNKNOWN_TYPE
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.TextView
Expand All @@ -68,13 +85,21 @@ import androidx.core.net.toUri
import androidx.core.text.HtmlCompat
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
import androidx.core.text.toSpannable
import androidx.core.view.*
import androidx.core.view.doOnLayout
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.postDelayed
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.commitNow
import androidx.fragment.app.setFragmentResultListener
import androidx.fragment.app.transaction
import androidx.lifecycle.*
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.webkit.JavaScriptReplyProxy
import androidx.webkit.WebMessageCompat
Expand Down Expand Up @@ -157,6 +182,11 @@ import com.duckduckgo.app.browser.webview.WebContentDebugging
import com.duckduckgo.app.browser.webview.WebViewBlobDownloadFeature
import com.duckduckgo.app.browser.webview.safewebview.SafeWebViewFeature
import com.duckduckgo.app.cta.ui.*
import com.duckduckgo.app.cta.ui.Cta
import com.duckduckgo.app.cta.ui.CtaViewModel
import com.duckduckgo.app.cta.ui.DaxBubbleCta
import com.duckduckgo.app.cta.ui.HomePanelCta
import com.duckduckgo.app.cta.ui.OnboardingDaxDialogCta
import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteEntity
import com.duckduckgo.app.fire.fireproofwebsite.data.website
Expand Down Expand Up @@ -250,6 +280,7 @@ import com.duckduckgo.downloads.api.DownloadConfirmationDialogListener
import com.duckduckgo.downloads.api.DownloadsFileActions
import com.duckduckgo.downloads.api.FileDownloader
import com.duckduckgo.downloads.api.FileDownloader.PendingFileDownload
import com.duckduckgo.duckplayer.api.DuckPlayer
import com.duckduckgo.duckplayer.api.DuckPlayerSettingsNoParams
import com.duckduckgo.js.messaging.api.JsCallbackData
import com.duckduckgo.js.messaging.api.JsMessageCallback
Expand Down Expand Up @@ -286,10 +317,16 @@ import javax.inject.Inject
import javax.inject.Named
import javax.inject.Provider
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import timber.log.Timber

Expand Down Expand Up @@ -499,6 +536,9 @@ class BrowserTabFragment :
@Inject
lateinit var safeWebViewFeature: SafeWebViewFeature

@Inject
lateinit var duckPlayer: DuckPlayer

/**
* We use this to monitor whether the user was seeing the in-context Email Protection signup prompt
* This is needed because the activity stack will be cleared if an external link is opened in our browser
Expand Down Expand Up @@ -1545,6 +1585,11 @@ class BrowserTabFragment :
is Command.LaunchScreen -> launchScreen(it.screen, it.payload)
is Command.HideOnboardingDaxDialog -> hideOnboardingDaxDialog(it.onboardingCta)
is Command.OpenDuckPlayerSettings -> globalActivityStarter.start(binding.root.context, DuckPlayerSettingsNoParams)
is Command.OpenDuckPlayerInfo -> {
context?.resources?.configuration?.let {
duckPlayer.showDuckPlayerPrimeModal(it, childFragmentManager)
}
}
else -> {
// NO OP
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.duckduckgo.app.browser.duckplayer

import com.duckduckgo.app.browser.commands.Command
import com.duckduckgo.app.browser.commands.Command.OpenDuckPlayerInfo
import com.duckduckgo.app.browser.commands.Command.OpenDuckPlayerSettings
import com.duckduckgo.app.browser.commands.Command.SendResponseToDuckPlayer
import com.duckduckgo.app.browser.commands.Command.SendResponseToJs
Expand Down Expand Up @@ -168,6 +169,9 @@ class DuckPlayerJSHelper @Inject constructor(
"openSettings" -> {
return OpenDuckPlayerSettings
}
"openInfo" -> {
return OpenDuckPlayerInfo
}
else -> {
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class ContentScopeScriptsJsMessaging @Inject constructor(
"sendDuckPlayerPixel",
"setUserValues",
"openDuckPlayer",
"openInfo",
"initialSetup",
"reportPageException",
"reportInitException",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@

package com.duckduckgo.duckplayer.api

import android.content.res.Configuration
import android.net.Uri
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import androidx.fragment.app.FragmentManager
import com.duckduckgo.duckplayer.api.PrivatePlayerMode.AlwaysAsk
import com.duckduckgo.duckplayer.api.PrivatePlayerMode.Disabled
import com.duckduckgo.duckplayer.api.PrivatePlayerMode.Enabled
Expand Down Expand Up @@ -143,6 +145,14 @@ interface DuckPlayer {
webView: WebView,
): WebResourceResponse?

/**
* Shows the Duck Player Prime modal.
*
* @param configuration The configuration of the device.
* @param fragmentManager The fragment manager.
*/
fun showDuckPlayerPrimeModal(configuration: Configuration, fragmentManager: FragmentManager)

/**
* Data class representing user preferences for Duck Player.
*
Expand Down
1 change: 1 addition & 0 deletions duckplayer/duckplayer-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ dependencies {
implementation Google.android.material
implementation Google.dagger

implementation "com.airbnb.android:lottie:_"
implementation "com.squareup.logcat:logcat:_"

testImplementation Testing.junit4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@

package com.duckduckgo.duckplayer.impl

import android.content.res.Configuration
import android.net.Uri
import android.webkit.MimeTypeMap
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import androidx.core.net.toUri
import androidx.fragment.app.FragmentManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.common.utils.UrlScheme.Companion.duck
Expand All @@ -32,6 +34,8 @@ import com.duckduckgo.duckplayer.api.DuckPlayer.UserPreferences
import com.duckduckgo.duckplayer.api.PrivatePlayerMode.AlwaysAsk
import com.duckduckgo.duckplayer.api.PrivatePlayerMode.Disabled
import com.duckduckgo.duckplayer.api.PrivatePlayerMode.Enabled
import com.duckduckgo.duckplayer.impl.ui.DuckPlayerPrimeBottomSheet
import com.duckduckgo.duckplayer.impl.ui.DuckPlayerPrimeDialogFragment
import com.squareup.anvil.annotations.ContributesBinding
import dagger.SingleInstanceIn
import java.io.InputStream
Expand Down Expand Up @@ -270,4 +274,12 @@ class RealDuckPlayer @Inject constructor(
}
return WebResourceResponse(null, null, null)
}

override fun showDuckPlayerPrimeModal(configuration: Configuration, fragmentManager: FragmentManager) {
if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
DuckPlayerPrimeDialogFragment.newInstance().show(fragmentManager, null)
} else {
DuckPlayerPrimeBottomSheet.newInstance().show(fragmentManager, null)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.duckplayer.impl.ui

import android.app.Dialog
import android.content.res.Configuration
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import com.duckduckgo.duckplayer.impl.R
import com.duckduckgo.duckplayer.impl.databinding.ModalDuckPlayerBinding
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment

class DuckPlayerPrimeBottomSheet : BottomSheetDialogFragment() {

private lateinit var binding: ModalDuckPlayerBinding

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = ModalDuckPlayerBinding.inflate(inflater, container, false)
LottieCompositionFactory.fromRawRes(context, R.raw.duckplayer)
binding.duckPlayerAnimation.setAnimation(R.raw.duckplayer)
binding.duckPlayerAnimation.playAnimation()
binding.duckPlayerAnimation.repeatCount = LottieDrawable.INFINITE
binding.dismissButton.setOnClickListener {
dismiss()
}
binding.closeButton.setOnClickListener {
dismiss()
}
return binding.root
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
return dialog
}

override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
dismiss()
}

companion object {
fun newInstance(): DuckPlayerPrimeBottomSheet =
DuckPlayerPrimeBottomSheet()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.duckplayer.impl.ui

import android.content.res.Configuration
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.view.WindowInsets
import android.view.WindowManager
import androidx.fragment.app.DialogFragment
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import com.duckduckgo.duckplayer.impl.R
import com.duckduckgo.duckplayer.impl.databinding.ModalDuckPlayerBinding

class DuckPlayerPrimeDialogFragment : DialogFragment() {

private lateinit var binding: ModalDuckPlayerBinding

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = ModalDuckPlayerBinding.inflate(inflater, container, false)
LottieCompositionFactory.fromRawRes(context, R.raw.duckplayer)
binding.duckPlayerAnimation.setAnimation(R.raw.duckplayer)
binding.duckPlayerAnimation.playAnimation()
binding.duckPlayerAnimation.repeatCount = LottieDrawable.INFINITE
binding.dismissButton.setOnClickListener {
dismiss()
}
binding.closeButton.setOnClickListener {
dismiss()
}
return binding.root
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, com.duckduckgo.mobile.android.R.style.Widget_DuckDuckGo_DialogFullScreen)
}
override fun onStart() {
super.onStart()
dialog?.window?.let { window ->
window.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
if (VERSION.SDK_INT >= VERSION_CODES.R) {
window.insetsController?.hide(WindowInsets.Type.statusBars())
} else {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
}
}
}

override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
dismiss()
}
companion object {
fun newInstance(): DuckPlayerPrimeDialogFragment =
DuckPlayerPrimeDialogFragment()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
android:pathData="M64,80a7,7 0,0 0,3.55 -13.03c-0.87,-0.51 -1.73,-1.05 -2.35,-1.85L48.15,43.19c-1.08,-1.39 -3.26,-0.1 -2.56,1.51l11.01,25.68c0.35,0.82 0.41,1.73 0.41,2.62"
android:fillColor="#444"/>
</vector>

Loading

0 comments on commit e815f2e

Please sign in to comment.