1
0
Fork 0
fenix/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFra...

254 lines
9.8 KiB
Kotlin
Raw Normal View History

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings.quicksettings
import android.app.Dialog
import android.content.Context
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity.BOTTOM
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.widget.NestedScrollView
import com.google.android.material.bottomsheet.BottomSheetDialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import mozilla.components.feature.sitepermissions.SitePermissions
2019-04-26 20:45:24 +02:00
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserFragment
import org.mozilla.fenix.exceptions.ExceptionDomains
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getAutoDisposeObservable
import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.settings.PhoneFeature
import java.net.MalformedURLException
import java.net.URL
import kotlin.coroutines.CoroutineContext
private const val KEY_URL = "KEY_URL"
private const val KEY_IS_SECURED = "KEY_IS_SECURED"
private const val KEY_SITE_PERMISSIONS = "KEY_SITE_PERMISSIONS"
private const val KEY_IS_TP_ON = "KEY_IS_TP_ON"
private const val KEY_DIALOG_GRAVITY = "KEY_DIALOG_GRAVITY"
private const val REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS = 4
@SuppressWarnings("TooManyFunctions")
class QuickSettingsSheetDialogFragment : AppCompatDialogFragment(), CoroutineScope {
private val safeArguments get() = requireNotNull(arguments)
private val url: String by lazy { safeArguments.getString(KEY_URL) }
private val isSecured: Boolean by lazy { safeArguments.getBoolean(KEY_IS_SECURED) }
private val isTrackingProtectionOn: Boolean by lazy { safeArguments.getBoolean(KEY_IS_TP_ON) }
private val promptGravity: Int by lazy { safeArguments.getInt(KEY_DIALOG_GRAVITY) }
private lateinit var quickSettingsComponent: QuickSettingsComponent
private lateinit var job: Job
var sitePermissions: SitePermissions?
get() = safeArguments.getParcelable(KEY_SITE_PERMISSIONS)
set(value) {
safeArguments.putParcelable(KEY_SITE_PERMISSIONS, value)
}
override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job()
}
2019-04-26 20:45:24 +02:00
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val rootView = inflateRootView(container)
quickSettingsComponent = QuickSettingsComponent(
rootView as NestedScrollView, this, ActionBusFactory.get(this),
QuickSettingsState(
QuickSettingsState.Mode.Normal(
url,
isSecured,
isTrackingProtectionOn,
sitePermissions
)
)
)
return rootView
}
private fun inflateRootView(container: ViewGroup? = null): View {
2019-04-26 20:45:24 +02:00
val contextThemeWrapper = ContextThemeWrapper(
activity,
(activity as HomeActivity).themeManager.currentThemeResource
)
return LayoutInflater.from(contextThemeWrapper).inflate(
2019-04-26 20:45:24 +02:00
R.layout.fragment_quick_settings_dialog_sheet,
container,
false
)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val customDialog = if (promptGravity == BOTTOM) {
return BottomSheetDialog(requireContext(), this.theme)
} else {
Dialog(requireContext())
}
return customDialog.applyCustomizationsForTopDialog(inflateRootView())
}
private fun Dialog.applyCustomizationsForTopDialog(rootView: View): Dialog {
addContentView(
rootView,
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
)
window?.apply {
setGravity(promptGravity)
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
// This must be called after addContentView, or it won't fully fill to the edge.
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
return this
}
companion object {
const val FRAGMENT_TAG = "QUICK_SETTINGS_FRAGMENT_TAG"
fun newInstance(
url: String,
isSecured: Boolean,
isTrackingProtectionOn: Boolean,
sitePermissions: SitePermissions?,
gravity: Int = BOTTOM
): QuickSettingsSheetDialogFragment {
val fragment = QuickSettingsSheetDialogFragment()
val arguments = fragment.arguments ?: Bundle()
with(arguments) {
putString(KEY_URL, url)
putBoolean(KEY_IS_SECURED, isSecured)
putBoolean(KEY_IS_TP_ON, isTrackingProtectionOn)
putParcelable(KEY_SITE_PERMISSIONS, sitePermissions)
putInt(KEY_DIALOG_GRAVITY, gravity)
}
fragment.arguments = arguments
return fragment
}
}
2019-04-26 20:45:24 +02:00
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (arePermissionsGranted(requestCode, grantResults)) {
val feature = requireNotNull(PhoneFeature.findFeatureBy(permissions))
getManagedEmitter<QuickSettingsChange>()
.onNext(QuickSettingsChange.PermissionGranted(feature, sitePermissions))
}
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
private fun arePermissionsGranted(requestCode: Int, grantResults: IntArray) =
requestCode == REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS && grantResults.all { it == PERMISSION_GRANTED }
private fun toggleTrackingProtection(context: Context, url: String) {
val host = try {
URL(url).host
} catch (e: MalformedURLException) {
url
}
launch {
if (!ExceptionDomains.load(context).contains(host)) {
ExceptionDomains.add(context, host)
} else {
ExceptionDomains.remove(context, listOf(host))
}
}
}
override fun onResume() {
super.onResume()
getAutoDisposeObservable<QuickSettingsAction>()
.subscribe {
when (it) {
is QuickSettingsAction.SelectBlockedByAndroid -> {
requestPermissions(it.permissions, REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS)
}
is QuickSettingsAction.SelectReportProblem -> {
launch(Dispatchers.Main) {
val reportUrl =
String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, it.url)
requireComponents.useCases.sessionUseCases.loadUrl.invoke(reportUrl)
}
dismiss()
}
is QuickSettingsAction.ToggleTrackingProtection -> {
val trackingEnabled = it.trackingProtection
context?.let { context: Context -> toggleTrackingProtection(context, url) }
launch(Dispatchers.Main) {
getManagedEmitter<QuickSettingsChange>().onNext(
QuickSettingsChange.Change(
url,
isSecured,
trackingEnabled,
sitePermissions
)
)
requireContext().components.useCases.sessionUseCases.reload.invoke()
}
}
is QuickSettingsAction.TogglePermission -> {
launch {
sitePermissions = quickSettingsComponent.toggleSitePermission(
context = requireContext(),
featurePhone = it.featurePhone,
url = url,
sitePermissions = sitePermissions
)
launch(Dispatchers.Main) {
getManagedEmitter<QuickSettingsChange>()
2019-04-26 20:45:24 +02:00
.onNext(
QuickSettingsChange.Stored(
it.featurePhone,
sitePermissions
)
)
requireContext().components.useCases.sessionUseCases.reload.invoke()
}
}
}
}
}
if (isVisible) {
getManagedEmitter<QuickSettingsChange>()
.onNext(QuickSettingsChange.PromptRestarted(sitePermissions))
}
}
}