* For #4977 - Support opening Fennec pinned website shortcuts in Fenix Fennec's pinned website shortcuts are set to open the BrowserApp activity. So we need a new activity alias to actually catch such Intents. Otherwise they would open "org.mozilla.firefox/.App" without any way to inform that this is the result of the user clicking on a pinned shortcut. For actually checking if the newly received Intent is of a Fennec pinned shortcut we introduce a new FennecBookmarkShortcutsIntentProcessor which will prepare the Intent to open the shortcut's URL in a new tab. * For #4977 - Don't keep IntentReceiverActivity on the back stack For successive Fennec pinned shortcuts to create a new IntentReceiverActivity and be processed as normal we need to not keep this as our task root. * For #4977 - Test the FennecBookmarkShortcutsIntentProcessormaster
parent
fdb1f505cc
commit
8454f208af
|
@ -48,6 +48,14 @@
|
||||||
android:resource="@xml/shortcuts" />
|
android:resource="@xml/shortcuts" />
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Fennec declared entry for homescreen pinned shortcuts.
|
||||||
|
-->
|
||||||
|
<activity-alias
|
||||||
|
android:name="org.mozilla.gecko.BrowserApp"
|
||||||
|
android:targetActivity=".IntentReceiverActivity">
|
||||||
|
</activity-alias>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".HomeActivity"
|
android:name=".HomeActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|layoutDirection|smallestScreenSize|screenLayout"
|
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|layoutDirection|smallestScreenSize|screenLayout"
|
||||||
|
|
|
@ -64,8 +64,9 @@ class IntentReceiverActivity : Activity() {
|
||||||
|
|
||||||
intent.setClassName(applicationContext, intentProcessorType.activityClassName)
|
intent.setClassName(applicationContext, intentProcessorType.activityClassName)
|
||||||
intent.putExtra(HomeActivity.OPEN_TO_BROWSER, intentProcessorType.shouldOpenToBrowser(intent))
|
intent.putExtra(HomeActivity.OPEN_TO_BROWSER, intentProcessorType.shouldOpenToBrowser(intent))
|
||||||
startActivity(intent)
|
|
||||||
|
|
||||||
|
// finish() before starting another activity. Don't keep this on the activities back stack.
|
||||||
finish()
|
finish()
|
||||||
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import mozilla.components.feature.pwa.intent.TrustedWebActivityIntentProcessor
|
||||||
import mozilla.components.feature.search.SearchUseCases
|
import mozilla.components.feature.search.SearchUseCases
|
||||||
import mozilla.components.feature.session.SessionUseCases
|
import mozilla.components.feature.session.SessionUseCases
|
||||||
import org.mozilla.fenix.BuildConfig
|
import org.mozilla.fenix.BuildConfig
|
||||||
|
import org.mozilla.fenix.home.intent.FennecBookmarkShortcutsIntentProcessor
|
||||||
import org.mozilla.fenix.test.Mockable
|
import org.mozilla.fenix.test.Mockable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,7 +63,8 @@ class IntentProcessors(
|
||||||
apiKey = BuildConfig.DIGITAL_ASSET_LINKS_TOKEN,
|
apiKey = BuildConfig.DIGITAL_ASSET_LINKS_TOKEN,
|
||||||
store = customTabsStore
|
store = customTabsStore
|
||||||
),
|
),
|
||||||
WebAppIntentProcessor(sessionManager, sessionUseCases.loadUrl, ManifestStorage(context))
|
WebAppIntentProcessor(sessionManager, sessionUseCases.loadUrl, ManifestStorage(context)),
|
||||||
|
FennecBookmarkShortcutsIntentProcessor(sessionManager, sessionUseCases.loadUrl)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* 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.home.intent
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.Intent.ACTION_VIEW
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import mozilla.components.browser.session.Session
|
||||||
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.concept.engine.EngineSession
|
||||||
|
import mozilla.components.feature.intent.ext.putSessionId
|
||||||
|
import mozilla.components.feature.intent.processing.IntentProcessor
|
||||||
|
import mozilla.components.feature.session.SessionUseCases
|
||||||
|
import mozilla.components.support.utils.toSafeIntent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy processor for pinned websites shortcuts created by Fennec.
|
||||||
|
* https://developer.android.com/guide/topics/ui/shortcuts/creating-shortcuts#pinned
|
||||||
|
*/
|
||||||
|
class FennecBookmarkShortcutsIntentProcessor(
|
||||||
|
private val sessionManager: SessionManager,
|
||||||
|
private val loadUrlUseCase: SessionUseCases.DefaultLoadUrlUseCase
|
||||||
|
) : IntentProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this Intent is of a Fennec pinned shortcut.
|
||||||
|
*/
|
||||||
|
override fun matches(intent: Intent): Boolean {
|
||||||
|
return intent.toSafeIntent().action == ACTION_FENNEC_HOMESCREEN_SHORTCUT
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is an Intent for a Fennec pinned website shortcut
|
||||||
|
* prepare it for opening website's URL in a new tab.
|
||||||
|
*/
|
||||||
|
override suspend fun process(intent: Intent): Boolean {
|
||||||
|
val safeIntent = intent.toSafeIntent()
|
||||||
|
val url = safeIntent.dataString
|
||||||
|
|
||||||
|
return if (!url.isNullOrEmpty() && matches(intent)) {
|
||||||
|
val session = Session(url, private = false, source = Session.Source.HOME_SCREEN)
|
||||||
|
|
||||||
|
sessionManager.add(session)
|
||||||
|
loadUrlUseCase(url, session, EngineSession.LoadUrlFlags.external())
|
||||||
|
intent.action = ACTION_VIEW
|
||||||
|
intent.putSessionId(session.id)
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Fennec set action for the pinned website shortcut Intent.
|
||||||
|
*/
|
||||||
|
const val ACTION_FENNEC_HOMESCREEN_SHORTCUT = "org.mozilla.gecko.BOOKMARK"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/* 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.home.intent
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import assertk.assertAll
|
||||||
|
import assertk.assertThat
|
||||||
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFalse
|
||||||
|
import assertk.assertions.isTrue
|
||||||
|
import io.mockk.Called
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.verify
|
||||||
|
import io.mockk.verifyAll
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import mozilla.components.browser.session.Session
|
||||||
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.concept.engine.EngineSession
|
||||||
|
import mozilla.components.feature.intent.ext.getSessionId
|
||||||
|
import mozilla.components.feature.session.SessionUseCases
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mozilla.fenix.TestApplication
|
||||||
|
import org.mozilla.fenix.home.intent.FennecBookmarkShortcutsIntentProcessor.Companion.ACTION_FENNEC_HOMESCREEN_SHORTCUT
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(application = TestApplication::class)
|
||||||
|
class FennecBookmarkShortcutsIntentProcessorTest {
|
||||||
|
private val sessionManager = mockk<SessionManager>(relaxed = true)
|
||||||
|
private val loadUrlUseCase = mockk<SessionUseCases.DefaultLoadUrlUseCase>(relaxed = true)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `only match Fennec pinned shortcut Intents`() {
|
||||||
|
val processor = FennecBookmarkShortcutsIntentProcessor(sessionManager, loadUrlUseCase)
|
||||||
|
|
||||||
|
assertAll {
|
||||||
|
assertThat(processor.matches(Intent(ACTION_FENNEC_HOMESCREEN_SHORTCUT))).isTrue()
|
||||||
|
assertThat(processor.matches(Intent("ShouldNotMatch"))).isFalse()
|
||||||
|
assertThat(processor.matches(Intent())).isFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `do not process blank Intents`() = runBlocking {
|
||||||
|
val processor = FennecBookmarkShortcutsIntentProcessor(sessionManager, loadUrlUseCase)
|
||||||
|
val fennecShortcutsIntent = Intent(ACTION_FENNEC_HOMESCREEN_SHORTCUT)
|
||||||
|
fennecShortcutsIntent.data = Uri.parse("http://mozilla.org")
|
||||||
|
|
||||||
|
val wasEmptyIntentProcessed = processor.matches(Intent())
|
||||||
|
|
||||||
|
assertThat(wasEmptyIntentProcessed).isFalse()
|
||||||
|
verify {
|
||||||
|
sessionManager wasNot Called
|
||||||
|
loadUrlUseCase wasNot Called
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `processing a Fennec shortcut Intent results in loading it's URL in a new Session`() = runBlocking {
|
||||||
|
mockkStatic(UUID::class)
|
||||||
|
// The Session constructor uses UUID.randomUUID().toString() as the default value for it's id field
|
||||||
|
every { UUID.randomUUID().toString() } returns "test"
|
||||||
|
val processor = FennecBookmarkShortcutsIntentProcessor(sessionManager, loadUrlUseCase)
|
||||||
|
val fennecShortcutsIntent = Intent(ACTION_FENNEC_HOMESCREEN_SHORTCUT)
|
||||||
|
val testUrl = "http://mozilla.org"
|
||||||
|
fennecShortcutsIntent.data = Uri.parse(testUrl)
|
||||||
|
val expectedSession = Session(testUrl, private = false, source = Session.Source.HOME_SCREEN)
|
||||||
|
|
||||||
|
val wasIntentProcessed = processor.process(fennecShortcutsIntent)
|
||||||
|
|
||||||
|
assertAll {
|
||||||
|
assertThat(wasIntentProcessed).isTrue()
|
||||||
|
assertThat(fennecShortcutsIntent.action).isEqualTo(Intent.ACTION_VIEW)
|
||||||
|
assertThat(fennecShortcutsIntent.getSessionId()).isEqualTo(expectedSession.id)
|
||||||
|
}
|
||||||
|
verifyAll {
|
||||||
|
sessionManager.add(expectedSession)
|
||||||
|
loadUrlUseCase(testUrl, expectedSession, EngineSession.LoadUrlFlags.external())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue