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

Commit

Permalink
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




4617: Fix Glean SDK specific metrics API to be callable from Java r=Dexterp37 a=Dexterp37

This adds `@JvmOverloads` to the specific metrics API and the testing API.




4621:  Docs update (20191003-121147) [ci skip] r=jonalmeida a=MickeyMoz



Co-authored-by: James Hugman <[email protected]>
Co-authored-by: Jonathan Almeida <[email protected]>
Co-authored-by: Roger Yang <[email protected]>
Co-authored-by: Alessio Placitelli <[email protected]>
Co-authored-by: MickeyMoz <[email protected]>
  • Loading branch information
6 people committed Oct 3, 2019
5 parents 5f9e6da + b053252 + 0b1fbf5 + 89a06bc + 0744b46 commit 7e1343f
Show file tree
Hide file tree
Showing 185 changed files with 720 additions and 256 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 7e1343f

Please sign in to comment.