Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Merge #3918 #4572
Browse files Browse the repository at this point in the history
3918: Closes #3791 - Derive app icon for likely app-link app r=jonalmeida a=jhugman

This PR exposes the icon and name of the app used to open a given link. 

This is to allow Fenix to display the app icon in the Quick Action Bar `Open in App` action.

### Pull Request checklist
<!-- Before submitting the PR, please address each item -->
- [X] **Quality**: This PR builds and passes detekt/ktlint checks (A pre-push hook is recommended)
- [X] **Tests**: This PR includes thorough tests or an explanation of why it does not
- [ ] **Changelog**: This PR includes [a changelog entry](https://github.com/mozilla-mobile/android-components/blob/master/docs/changelog.md) or does not need one
- ~[ ] **Accessibility**: The code in this PR follows [accessibility best practices](https://github.com/mozilla-mobile/shared-docs/blob/master/android/accessibility_guide.md) or does not include any user facing features~

### After merge
- [ ] **Milestone**: Make sure issues closed by this pull request are added to the [milestone](https://github.com/mozilla-mobile/android-components/milestones) of the version currently in development.


4572: Closes #2203, #3511: Inline Socorro upload functionality and add supp… r=pocmo a=rocketsroger

…ort for "uncaught exception" crash reports




Co-authored-by: James Hugman <[email protected]>
Co-authored-by: Jonathan Almeida <[email protected]>
Co-authored-by: Roger Yang <[email protected]>
  • Loading branch information
4 people committed Oct 3, 2019
3 parents 342fda1 + b053252 + 3dc4c06 commit cd9ba81
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,34 @@
package mozilla.components.feature.app.links

import android.content.Intent
import android.content.pm.ResolveInfo

/**
* Data class for the external Intent or fallback URL a given URL encodes for.
*/
data class AppLinkRedirect(
val appIntent: Intent?,
val webUrl: String?,
val isFallback: Boolean
val isFallback: Boolean,
val info: ResolveInfo? = null
) {
/**
* If there is a third-party app intent.
*/
fun hasExternalApp() = appIntent != null

/**
* If there is a fallback URL (should the intent fails).
*/
fun hasFallback() = webUrl != null && isFallback

/**
* If the app link is a redirect (to an app or URL).
*/
fun isRedirect() = hasExternalApp() || hasFallback()

/**
* Is the app link one that can be installed from a store.
*/
fun isInstallable() = appIntent?.data?.scheme == "market"
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,32 +77,36 @@ class AppLinksUseCases(
) {
operator fun invoke(url: String): AppLinkRedirect {
val intents = createBrowsableIntents(url)
val appIntent = if (includeHttpAppLinks) {
intents.firstOrNull { getNonBrowserActivities(it).isNotEmpty() }
val (appIntent, resolveInfos) = if (includeHttpAppLinks) {
intents.asSequence()
.map { it to getNonBrowserActivities(it) }
.firstOrNull { it.second.isNotEmpty() }
?.let {
// The user may have decided to keep opening this type of link in this browser.
if (ignoreDefaultBrowser &&
findDefaultActivity(it)?.activityInfo?.packageName == context.packageName) {
findDefaultActivity(it.first)?.activityInfo?.packageName == context.packageName) {
// in which case, this isn't an app intent anymore.
null
} else {
it
}
}
} else {
intents.filter { it.data?.isHttpOrHttps != true }
.firstOrNull {
getNonBrowserActivities(it).isNotEmpty()
}
}
intents.asSequence()
.filter { it.data?.isHttpOrHttps != true }
.map { it to getNonBrowserActivities(it) }
.firstOrNull { it.second.isNotEmpty() }
} ?: null to null

val webUrls = intents.mapNotNull {
if (it.data?.isHttpOrHttps == true) it.dataString else null
}

val webUrl = webUrls.firstOrNull { it != url } ?: webUrls.firstOrNull()

return AppLinkRedirect(appIntent, webUrl, webUrl != url)
val appInfo = resolveInfos?.firstOrNull()

return AppLinkRedirect(appIntent, webUrl, webUrl != url, appInfo)
}

private fun getNonBrowserActivities(intent: Intent): List<ResolveInfo> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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 mozilla.components.feature.app.links

import android.content.Intent
import android.net.Uri
import mozilla.components.support.test.mock
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mockito.Mockito.`when`

class AppLinkRedirectTest {

@Test
fun hasExternalApp() {
var appLink = AppLinkRedirect(appIntent = mock(), webUrl = null, isFallback = true)
assertTrue(appLink.hasExternalApp())

appLink = AppLinkRedirect(appIntent = null, webUrl = null, isFallback = true)
assertFalse(appLink.hasExternalApp())
}

@Test
fun hasFallback() {
var appLink = AppLinkRedirect(appIntent = mock(), webUrl = null, isFallback = true)
assertFalse(appLink.hasFallback())

appLink = AppLinkRedirect(appIntent = mock(), webUrl = "https://example.com", isFallback = false)
assertFalse(appLink.hasFallback())

appLink = AppLinkRedirect(appIntent = mock(), webUrl = "https://example.com", isFallback = true)
assertTrue(appLink.hasFallback())
}

@Test
fun isRedirect() {
var appLink = AppLinkRedirect(appIntent = null, webUrl = null, isFallback = true)
assertFalse(appLink.isRedirect())

appLink = AppLinkRedirect(appIntent = mock(), webUrl = null, isFallback = true)
assertTrue(appLink.isRedirect())

appLink = AppLinkRedirect(appIntent = null, webUrl = "https://example.com", isFallback = true)
assertTrue(appLink.isRedirect())

appLink = AppLinkRedirect(appIntent = mock(), webUrl = "https://example.com", isFallback = true)
assertTrue(appLink.isRedirect())
}

@Test
fun isInstallable() {
val intent: Intent = mock()
val uri: Uri = mock()
`when`(intent.data).thenReturn(uri)
`when`(uri.scheme).thenReturn("market")

var appLink = AppLinkRedirect(appIntent = null, webUrl = "https://example.com", isFallback = true)
assertFalse(appLink.isInstallable())

appLink = AppLinkRedirect(appIntent = intent, webUrl = "https://example.com", isFallback = true)
assertTrue(appLink.isInstallable())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,20 @@ class AppLinksUseCasesTest {
val pm = testContext.packageManager
val packageManager = shadowOf(pm)

urlToPackages.forEach { (urlString, packageName) ->
urlToPackages.forEach { (urlString, pkgName) ->
val intent = Intent.parseUri(urlString, 0).addCategory(Intent.CATEGORY_BROWSABLE)

val activityInfo = ActivityInfo()
activityInfo.packageName = packageName

val resolveInfo = ResolveInfo()
resolveInfo.activityInfo = activityInfo

packageManager.addResolveInfoForIntentNoDefaults(intent, resolveInfo)
val info = ActivityInfo().apply {
packageName = pkgName
icon = android.R.drawable.btn_default
}

val resolveInfo = ResolveInfo().apply {
labelRes = android.R.string.ok
activityInfo = info
}
packageManager.addResolveInfoForIntent(intent, resolveInfo)
packageManager.addDrawableResolution(pkgName, android.R.drawable.btn_default, mock())
}

val context = mock<Context>()
Expand Down Expand Up @@ -111,10 +115,24 @@ class AppLinksUseCasesTest {
val redirect = subject.interceptedAppLinkRedirect(uri)
assertTrue(redirect.hasExternalApp())
assertNotNull(redirect.appIntent)
assertNotNull(redirect.info)
assertEquals("com.example.app", redirect.info!!.activityInfo.packageName)

assertEquals("zxing://scan/", redirect.appIntent!!.dataString)
}

@Test
fun `A market scheme uri is an install link`() {
val uri = "intent://details/#Intent;scheme=market;package=com.google.play;end"
val context = createContext(uri to appPackage, appUrl to browserPackage)
val subject = AppLinksUseCases(context, setOf(browserPackage))

val redirect = subject.appLinkRedirect.invoke(uri)

assertTrue(redirect.hasExternalApp())
assertTrue(redirect.isInstallable())
}

@Test
fun `A intent scheme uri without an installed app`() {
val context = createContext(appUrl to browserPackage)
Expand All @@ -126,6 +144,7 @@ class AppLinksUseCasesTest {
assertFalse(redirect.hasExternalApp())
assertFalse(redirect.hasFallback())
assertNull(redirect.webUrl)
assertFalse(redirect.isInstallable())
}

@Test
Expand All @@ -139,6 +158,8 @@ class AppLinksUseCasesTest {
assertFalse(redirect.hasExternalApp())
assertTrue(redirect.hasFallback())

assertNull(redirect.info)

assertEquals("http://zxing.org", redirect.webUrl)
}

Expand Down
1 change: 1 addition & 0 deletions components/lib/crash/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ dependencies {
testImplementation Dependencies.testing_robolectric
testImplementation Dependencies.testing_mockito
testImplementation Dependencies.testing_coroutines
testImplementation Dependencies.testing_mockwebserver
}

ext.gleanGenerateMarkdownDocs = true
Expand Down
Loading

0 comments on commit cd9ba81

Please sign in to comment.