Skip to content

Commit

Permalink
Permissions: Unify Location (#5208)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1174433894299346/1208719691317744/f

### Description
This PR moves the Location permission logic to the
SitePermissionsManager
Note: This 

### Steps to test this PR

_Location permission granted_
- [x] Fresh install and visit permission.site
- [x] Ask for Location permissions
- [x] Verify new Location dialog appears
- [x] Allow permissions
- [x] Verify system dialog is shown
- [x] Allow permissions
- [x] Verify location is granted (location button is green)

_Location permission not granted_
- [x] Fresh install and visit permission.site
- [x] Ask for Location permissions
- [x] Verify new Location dialog appears
- [x] Deny permissions
- [x] Verify location is not granted (location button is red)

_Location permission granted (not system granted)_
- [x] Fresh install and visit permission.site
- [x] Ask for Location permissions
- [x] Verify new Location dialog appears
- [x] Allow permissions
- [x] Verify system dialog is shown
- [x] Deny permissions
- [x] Verify location is not granted (location button is red)
- [x] Verify Snackbar appears
- [x] Ask for Location permissions
- [x] Verify system dialog is shown
- [x] Deny permissions
- [x] Verify Settings dialog appears
- [x] Tap on Open Settings
- [x] Verify Device Settings screen opens

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1208719691317744
  • Loading branch information
malmstein authored Nov 13, 2024
1 parent c5a59ad commit 7a238c8
Show file tree
Hide file tree
Showing 42 changed files with 1,006 additions and 1,041 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.duckduckgo.appbuildconfig.api.AppBuildConfig
import com.duckduckgo.common.utils.DefaultDispatcherProvider
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.site.permissions.api.SitePermissionsManager
import com.duckduckgo.site.permissions.api.SitePermissionsManager.LocationPermissionRequest
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -144,16 +145,26 @@ class BrowserChromeClient @Inject constructor(
}

override fun onPermissionRequest(request: PermissionRequest) {
Timber.d("Permissions: permission requested ${request.resources.asList()}")
webViewClientListener?.getCurrentTabId()?.let { tabId ->
appCoroutineScope.launch(coroutineDispatcher.io()) {
val permissionsAllowedToAsk = sitePermissionsManager.getSitePermissions(tabId, request)
if (permissionsAllowedToAsk.userHandled.isNotEmpty()) {
Timber.d("Permissions: permission requested not user handled")
webViewClientListener?.onSitePermissionRequested(request, permissionsAllowedToAsk)
}
}
}
}

override fun onGeolocationPermissionsShowPrompt(
origin: String,
callback: GeolocationPermissions.Callback,
) {
Timber.d("Permissions: location permission requested $origin")
onPermissionRequest(LocationPermissionRequest(origin, callback))
}

override fun onCloseWindow(window: WebView?) {
webViewClientListener?.closeCurrentTab()
}
Expand Down Expand Up @@ -208,13 +219,6 @@ class BrowserChromeClient @Inject constructor(
return true
}

override fun onGeolocationPermissionsShowPrompt(
origin: String,
callback: GeolocationPermissions.Callback,
) {
webViewClientListener?.onSiteLocationPermissionRequested(origin, callback)
}

override fun getDefaultVideoPoster(): Bitmap {
return Bitmap.createBitmap(intArrayOf(Color.TRANSPARENT), 1, 1, ARGB_8888)
}
Expand Down
133 changes: 0 additions & 133 deletions app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.AnyThread
import androidx.annotation.StringRes
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.text.HtmlCompat
Expand Down Expand Up @@ -100,7 +99,6 @@ import com.duckduckgo.app.accessibility.data.AccessibilitySettingsDataStore
import com.duckduckgo.app.autocomplete.api.AutoComplete.AutoCompleteSuggestion
import com.duckduckgo.app.brokensite.BrokenSiteActivity
import com.duckduckgo.app.browser.BrowserTabViewModel.FileChooserRequestedParams
import com.duckduckgo.app.browser.BrowserTabViewModel.LocationPermission
import com.duckduckgo.app.browser.R.string
import com.duckduckgo.app.browser.SSLErrorType.NONE
import com.duckduckgo.app.browser.WebViewErrorResponse.LOADING
Expand All @@ -118,8 +116,6 @@ import com.duckduckgo.app.browser.cookies.ThirdPartyCookieManager
import com.duckduckgo.app.browser.customtabs.CustomTabActivity
import com.duckduckgo.app.browser.customtabs.CustomTabPixelNames
import com.duckduckgo.app.browser.customtabs.CustomTabViewModel.Companion.CUSTOM_TAB_NAME_PREFIX
import com.duckduckgo.app.browser.databinding.ContentSiteLocationPermissionDialogBinding
import com.duckduckgo.app.browser.databinding.ContentSystemLocationPermissionDialogBinding
import com.duckduckgo.app.browser.databinding.FragmentBrowserTabBinding
import com.duckduckgo.app.browser.databinding.HttpAuthenticationBinding
import com.duckduckgo.app.browser.downloader.BlobConverterInjector
Expand Down Expand Up @@ -186,7 +182,6 @@ import com.duckduckgo.app.global.view.isImmersiveModeEnabled
import com.duckduckgo.app.global.view.launchDefaultAppActivity
import com.duckduckgo.app.global.view.renderIfChanged
import com.duckduckgo.app.global.view.toggleFullScreen
import com.duckduckgo.app.location.data.LocationPermissionType
import com.duckduckgo.app.pixels.AppPixelName
import com.duckduckgo.app.privatesearch.PrivateSearchScreenNoParams
import com.duckduckgo.app.settings.db.SettingsDataStore
Expand Down Expand Up @@ -297,7 +292,6 @@ import com.duckduckgo.voice.api.VoiceSearchLauncher
import com.duckduckgo.voice.api.VoiceSearchLauncher.Source.BROWSER
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import java.io.File
Expand Down Expand Up @@ -1661,9 +1655,6 @@ class BrowserTabFragment :
is Command.ShowErrorWithAction -> showErrorSnackbar(it)
is Command.HideWebContent -> webView?.hide()
is Command.ShowWebContent -> webView?.show()
is Command.CheckSystemLocationPermission -> checkSystemLocationPermission(it.domain, it.deniedForever)
is Command.RequestSystemLocationPermission -> requestLocationPermissions()
is Command.AskDomainPermission -> askSiteLocationPermission(it.locationPermission)
is Command.RefreshUserAgent -> refreshUserAgent(it.url, it.isDesktop)
is Command.AskToFireproofWebsite -> askToFireproofWebsite(requireContext(), it.fireproofWebsite)
is Command.AskToAutomateFireproofWebsite -> askToAutomateFireproofWebsite(requireContext(), it.fireproofWebsite)
Expand Down Expand Up @@ -1909,116 +1900,6 @@ class BrowserTabFragment :
}
}

private fun locationPermissionsHaveNotBeenGranted(): Boolean {
return ContextCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.ACCESS_COARSE_LOCATION,
) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(requireActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
}

private fun checkSystemLocationPermission(
domain: String,
deniedForever: Boolean,
) {
if (locationPermissionsHaveNotBeenGranted()) {
if (deniedForever) {
viewModel.onSystemLocationPermissionDeniedForever()
} else {
showSystemLocationPermissionDialog(domain)
}
} else {
viewModel.onSystemLocationPermissionGranted()
}
}

private fun showSystemLocationPermissionDialog(domain: String) {
val binding = ContentSystemLocationPermissionDialogBinding.inflate(layoutInflater)

val originUrl = domain.websiteFromGeoLocationsApiOrigin()
val subtitle = getString(R.string.preciseLocationSystemDialogSubtitle, originUrl, originUrl)
binding.systemPermissionDialogSubtitle.text = subtitle

val dialog = CustomAlertDialogBuilder(requireActivity())
.setView(binding)
.build()

binding.allowLocationPermission.setOnClickListener {
viewModel.onSystemLocationPermissionAllowed()
dialog.dismiss()
}

binding.denyLocationPermission.setOnClickListener {
viewModel.onSystemLocationPermissionNotAllowed()
dialog.dismiss()
}

binding.neverAllowLocationPermission.setOnClickListener {
viewModel.onSystemLocationPermissionNeverAllowed()
dialog.dismiss()
}

dialog.show()
}

private fun requestLocationPermissions() {
requestPermissions(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
),
PERMISSION_REQUEST_GEO_LOCATION,
)
}

private fun askSiteLocationPermission(locationPermission: LocationPermission) {
if (!isActiveCustomTab() && !isActiveTab) {
Timber.v("Will not launch a dialog for an inactive tab")
return
}

val binding = ContentSiteLocationPermissionDialogBinding.inflate(layoutInflater)

val domain = locationPermission.origin
val title = domain.websiteFromGeoLocationsApiOrigin()
binding.sitePermissionDialogTitle.text = getString(R.string.preciseLocationSiteDialogTitle, title)
binding.sitePermissionDialogSubtitle.text = if (title == DDG_DOMAIN) {
getString(R.string.preciseLocationDDGDialogSubtitle)
} else {
getString(R.string.preciseLocationSiteDialogSubtitle)
}

val dialog = MaterialAlertDialogBuilder(requireActivity())
.setView(binding.root)
.setOnCancelListener {
// Called when user clicks outside the dialog - deny to be safe
locationPermission.callback.invoke(locationPermission.origin, false, false)
}
.create()

binding.siteAllowAlwaysLocationPermission.setOnClickListener {
viewModel.onSiteLocationPermissionSelected(domain, LocationPermissionType.ALLOW_ALWAYS)
dialog.dismiss()
}

binding.siteAllowOnceLocationPermission.setOnClickListener {
viewModel.onSiteLocationPermissionSelected(domain, LocationPermissionType.ALLOW_ONCE)
dialog.dismiss()
}

binding.siteDenyOnceLocationPermission.setOnClickListener {
viewModel.onSiteLocationPermissionSelected(domain, LocationPermissionType.DENY_ONCE)
dialog.dismiss()
}

binding.siteDenyAlwaysLocationPermission.setOnClickListener {
viewModel.onSiteLocationPermissionSelected(domain, LocationPermissionType.DENY_ALWAYS)
dialog.dismiss()
}

dialog.show()
}

private fun launchBrokenSiteFeedback(data: BrokenSiteData) {
val context = context ?: return

Expand Down Expand Up @@ -3522,18 +3403,6 @@ class BrowserTabFragment :
omnibar.toolbar.makeSnackbarWithNoBottomInset(R.string.permissionRequiredToDownload, Snackbar.LENGTH_LONG).show()
}
}

PERMISSION_REQUEST_GEO_LOCATION -> {
if ((grantResults.isNotEmpty()) && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
viewModel.onSystemLocationPermissionGranted()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) {
viewModel.onSystemLocationPermissionDeniedOneTime()
} else {
viewModel.onSystemLocationPermissionDeniedTwice()
}
}
}
}
}

Expand Down Expand Up @@ -3632,7 +3501,6 @@ class BrowserTabFragment :
private const val TAB_ID_ARG = "TAB_ID_ARG"
private const val URL_EXTRA_ARG = "URL_EXTRA_ARG"
private const val SKIP_HOME_ARG = "SKIP_HOME_ARG"
private const val DDG_DOMAIN = "duckduckgo.com"
private const val LAUNCH_FROM_EXTERNAL_EXTRA = "LAUNCH_FROM_EXTERNAL_EXTRA"

const val ADD_SAVED_SITE_FRAGMENT_TAG = "ADD_SAVED_SITE"
Expand All @@ -3642,7 +3510,6 @@ class BrowserTabFragment :

private const val REQUEST_CODE_CHOOSE_FILE = 100
private const val PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 200
private const val PERMISSION_REQUEST_GEO_LOCATION = 300

private const val URL_BUNDLE_KEY = "url"

Expand Down
Loading

0 comments on commit 7a238c8

Please sign in to comment.