diff --git a/components/browser/session/src/main/java/mozilla/components/browser/session/Session.kt b/components/browser/session/src/main/java/mozilla/components/browser/session/Session.kt index 12af1da7167..2bedfab37c5 100644 --- a/components/browser/session/src/main/java/mozilla/components/browser/session/Session.kt +++ b/components/browser/session/src/main/java/mozilla/components/browser/session/Session.kt @@ -105,6 +105,11 @@ class Session( _, _, _ -> notifyObservers ({ onCustomTabConfigChanged() }) } + /** + * Returns whether or not this session is used for a Custom Tab. + */ + fun isCustomTabSession() = customTabConfig != null + /** * Helper method to notify observers. */ diff --git a/components/browser/session/src/main/java/mozilla/components/browser/session/SessionManager.kt b/components/browser/session/src/main/java/mozilla/components/browser/session/SessionManager.kt index c7cfdee8669..d5ec7376e1f 100644 --- a/components/browser/session/src/main/java/mozilla/components/browser/session/SessionManager.kt +++ b/components/browser/session/src/main/java/mozilla/components/browser/session/SessionManager.kt @@ -38,9 +38,15 @@ class SessionManager( } /** - * Gets all sessions. + * Returns a list of active sessions and filters out sessions used for CustomTabs. */ val sessions: List + get() = synchronized(values) { values.filter { !it.isCustomTabSession() } } + + /** + * Returns a list of all active sessions. + */ + val all: List get() = synchronized(values) { values.toList() } /** @@ -81,7 +87,6 @@ class SessionManager( this.engineSession = engineSession this.engineObserver = EngineObserver(session).also { observer -> engineSession.register(observer) - engineSession.loadUrl(session.url) } } } diff --git a/components/browser/session/src/main/java/mozilla/components/browser/session/storage/DefaultSessionStorage.kt b/components/browser/session/src/main/java/mozilla/components/browser/session/storage/DefaultSessionStorage.kt index 16fef4e54d6..39f4c2113c8 100644 --- a/components/browser/session/src/main/java/mozilla/components/browser/session/storage/DefaultSessionStorage.kt +++ b/components/browser/session/src/main/java/mozilla/components/browser/session/storage/DefaultSessionStorage.kt @@ -84,7 +84,6 @@ class DefaultSessionStorage( val jsonSession = jsonRoot.getJSONObject(it) val session = deserializeSession(it, jsonSession.getJSONObject(SESSION_KEY)) val engineSession = deserializeEngineSession(engine, jsonSession.getJSONObject(ENGINE_SESSION_KEY)) - sessionManager.add(session, engineSession = engineSession) } @@ -110,12 +109,12 @@ class DefaultSessionStorage( json.put(VERSION_KEY, VERSION) json.put(SELECTED_SESSION_KEY, sessionManager.selectedSession.id) - sessionManager.sessions.forEach({ session -> + sessionManager.sessions.forEach { session -> val sessionJson = JSONObject() sessionJson.put(SESSION_KEY, serializeSession(session)) sessionJson.put(ENGINE_SESSION_KEY, serializeEngineSession(sessionManager.getEngineSession(session))) json.put(session.id, sessionJson) - }) + } file = getFile() outputStream = file.startWrite() diff --git a/components/browser/session/src/main/java/mozilla/components/browser/session/tab/CustomTabsService.kt b/components/browser/session/src/main/java/mozilla/components/browser/session/tab/CustomTabsService.kt new file mode 100644 index 00000000000..b8bab6db07a --- /dev/null +++ b/components/browser/session/src/main/java/mozilla/components/browser/session/tab/CustomTabsService.kt @@ -0,0 +1,28 @@ +/* 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.browser.session.tab + +import android.app.Service +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.os.IBinder +import android.support.customtabs.ICustomTabsCallback +import android.support.customtabs.ICustomTabsService + +class CustomTabsService : Service() { + override fun onBind(intent: Intent): IBinder? { + return object : ICustomTabsService.Stub() { + override fun warmup(flags: Long): Boolean = true + override fun newSession(cb: ICustomTabsCallback) = true + override fun mayLaunchUrl(cb: ICustomTabsCallback, url: Uri, extras: Bundle, bundles: List) = true + override fun extraCommand(s: String, bundle: Bundle): Bundle? = null + override fun updateVisuals(cb: ICustomTabsCallback, bundle: Bundle) = false + override fun requestPostMessageChannel(cb: ICustomTabsCallback, uri: Uri): Boolean = false + override fun postMessage(cb: ICustomTabsCallback, s: String, bundle: Bundle): Int = 0 + override fun validateRelationship(cb: ICustomTabsCallback, i: Int, uri: Uri, bundle: Bundle) = false + } + } +} diff --git a/components/browser/session/src/test/java/mozilla/components/browser/session/storage/DefaultSessionStorageTest.kt b/components/browser/session/src/test/java/mozilla/components/browser/session/storage/DefaultSessionStorageTest.kt index 09c59389ad8..02f44e8ad1d 100644 --- a/components/browser/session/src/test/java/mozilla/components/browser/session/storage/DefaultSessionStorageTest.kt +++ b/components/browser/session/src/test/java/mozilla/components/browser/session/storage/DefaultSessionStorageTest.kt @@ -7,6 +7,7 @@ package mozilla.components.browser.session.storage import android.util.AtomicFile import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager +import mozilla.components.browser.session.tab.CustomTabConfig import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.EngineSession import org.json.JSONException @@ -72,6 +73,35 @@ class DefaultSessionStorageTest { verify(restoredEngineSession)!!.restoreState(engineSessionState.filter { it.key != "k3" }) } + @Test + fun testPersistIgnoresCustomTabSessions() { + val session = Session("http://mozilla.org") + session.customTabConfig = mock(CustomTabConfig::class.java) + val engineSessionState = mutableMapOf("k0" to "v0", "k1" to 1, "k2" to true, "k3" to emptyList()) + + val engineSession = mock(EngineSession::class.java) + `when`(engineSession.saveState()).thenReturn(engineSessionState) + + val engine = mock(Engine::class.java) + `when`(engine.createSession()).thenReturn(mock(EngineSession::class.java)) + + val sessionManager = SessionManager(engine) + sessionManager.add(session, true, engineSession) + + // Persist current sessions + val storage = DefaultSessionStorage(RuntimeEnvironment.application) + val persisted = storage.persist(sessionManager) + assertTrue(persisted) + + // Restore session using a fresh SessionManager + val restoredSessionManager = SessionManager(engine) + val restored = storage.restore(engine, restoredSessionManager) + assertTrue(restored) + + // Nothing to restore as CustomTab sessions should not be persisted + assertEquals(0, restoredSessionManager.sessions.size) + } + @Test fun testRestoreReturnsFalseOnIOException() { val engine = mock(Engine::class.java) diff --git a/components/browser/session/src/test/java/mozilla/components/browser/session/tab/CustomTabsServiceTest.kt b/components/browser/session/src/test/java/mozilla/components/browser/session/tab/CustomTabsServiceTest.kt new file mode 100644 index 00000000000..ac8f4fc9c27 --- /dev/null +++ b/components/browser/session/src/test/java/mozilla/components/browser/session/tab/CustomTabsServiceTest.kt @@ -0,0 +1,48 @@ +/* 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.browser.session.tab + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.support.customtabs.ICustomTabsCallback +import android.support.customtabs.ICustomTabsService +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class CustomTabsServiceTest { + + @Test + fun testCustomTabService() { + val customTabsService = CustomTabsService() + val customTabsServiceStub = customTabsService.onBind(mock(Intent::class.java)) + assertNotNull(customTabsServiceStub) + + val stub = customTabsServiceStub as ICustomTabsService.Stub + assertTrue(stub.warmup(123)) + assertTrue(stub.newSession(mock(ICustomTabsCallback::class.java))) + assertNull(stub.extraCommand("", mock(Bundle::class.java))) + assertFalse(stub.updateVisuals(mock(ICustomTabsCallback::class.java), mock(Bundle::class.java))) + assertFalse(stub.requestPostMessageChannel(mock(ICustomTabsCallback::class.java), mock(Uri::class.java))) + assertEquals(0, stub.postMessage(mock(ICustomTabsCallback::class.java), "", mock(Bundle::class.java))) + assertFalse(stub.validateRelationship( + mock(ICustomTabsCallback::class.java), + 0, + mock(Uri::class.java), + mock(Bundle::class.java))) + assertTrue(stub.mayLaunchUrl( + mock(ICustomTabsCallback::class.java), + mock(Uri::class.java), + mock(Bundle::class.java), emptyList())) + } +} \ No newline at end of file diff --git a/components/concept/toolbar/src/main/java/mozilla/components/concept/toolbar/Toolbar.kt b/components/concept/toolbar/src/main/java/mozilla/components/concept/toolbar/Toolbar.kt index bebadc7f9bd..5112e4621b3 100644 --- a/components/concept/toolbar/src/main/java/mozilla/components/concept/toolbar/Toolbar.kt +++ b/components/concept/toolbar/src/main/java/mozilla/components/concept/toolbar/Toolbar.kt @@ -70,6 +70,11 @@ interface Toolbar { */ fun addNavigationAction(action: Action) + /** + * Casts this toolbar to an Android View object. + */ + fun asView(): View = this as View + /** * Generic interface for actions to be added to the toolbar. */ diff --git a/components/feature/session/build.gradle b/components/feature/session/build.gradle index 96a5174cc84..56a78c9c7b2 100644 --- a/components/feature/session/build.gradle +++ b/components/feature/session/build.gradle @@ -35,6 +35,7 @@ dependencies { testImplementation "junit:junit:${rootProject.ext.dependencies['junit']}" testImplementation "org.robolectric:robolectric:${rootProject.ext.dependencies['robolectric']}" testImplementation "org.mockito:mockito-core:${rootProject.ext.dependencies['mockito']}" + testImplementation "com.android.support:customtabs:${rootProject.ext.dependencies['supportLibraries']}" } archivesBaseName = "feature-session" diff --git a/components/feature/session/src/main/java/mozilla/components/feature/session/EngineViewPresenter.kt b/components/feature/session/src/main/java/mozilla/components/feature/session/EngineViewPresenter.kt index 7607d939456..860d79d4cf5 100644 --- a/components/feature/session/src/main/java/mozilla/components/feature/session/EngineViewPresenter.kt +++ b/components/feature/session/src/main/java/mozilla/components/feature/session/EngineViewPresenter.kt @@ -13,14 +13,18 @@ import mozilla.components.concept.engine.EngineView */ class EngineViewPresenter( private val sessionManager: SessionManager, - private val engineView: EngineView + private val engineView: EngineView, + private val sessionId: String? = null ) : SessionManager.Observer { + private var activeSession: Session? = null + /** * Start presenter and display data in view. */ fun start() { - renderSession(sessionManager.selectedSession) + val session = sessionId?.let { sessionManager.findSessionById(sessionId) } ?: sessionManager.selectedSession + renderSession(session) sessionManager.register(this) } @@ -37,9 +41,13 @@ class EngineViewPresenter( */ override fun onSessionSelected(session: Session) { renderSession(session) + activeSession = session } private fun renderSession(session: Session) { - engineView.render(sessionManager.getOrCreateEngineSession(session)) + sessionManager.getOrCreateEngineSession(session).apply { + engineView.render(this) + loadUrl(session.url) + } } } diff --git a/components/feature/session/src/main/java/mozilla/components/feature/session/SessionFeature.kt b/components/feature/session/src/main/java/mozilla/components/feature/session/SessionFeature.kt index 34480bfb30c..5071ba94746 100644 --- a/components/feature/session/src/main/java/mozilla/components/feature/session/SessionFeature.kt +++ b/components/feature/session/src/main/java/mozilla/components/feature/session/SessionFeature.kt @@ -15,16 +15,16 @@ class SessionFeature( private val sessionManager: SessionManager, private val sessionUseCases: SessionUseCases, engineView: EngineView, - private val sessionStorage: SessionStorage? = null + private val sessionStorage: SessionStorage? = null, + sessionId: String? = null ) { - internal val presenter = EngineViewPresenter(sessionManager, engineView) + internal val presenter = EngineViewPresenter(sessionManager, engineView, sessionId) /** * Start feature: App is in the foreground. */ fun start() { presenter.start() - sessionStorage?.start(sessionManager) } diff --git a/components/feature/session/src/main/java/mozilla/components/feature/session/SessionIntentProcessor.kt b/components/feature/session/src/main/java/mozilla/components/feature/session/SessionIntentProcessor.kt index 3770e833afe..56d723aa1a9 100644 --- a/components/feature/session/src/main/java/mozilla/components/feature/session/SessionIntentProcessor.kt +++ b/components/feature/session/src/main/java/mozilla/components/feature/session/SessionIntentProcessor.kt @@ -6,6 +6,10 @@ package mozilla.components.feature.session import android.content.Intent import android.text.TextUtils +import mozilla.components.browser.session.Session +import mozilla.components.browser.session.SessionManager +import mozilla.components.browser.session.tab.CustomTabConfig +import mozilla.components.support.utils.SafeIntent typealias IntentHandler = (Intent) -> Boolean @@ -14,17 +18,31 @@ typealias IntentHandler = (Intent) -> Boolean */ class SessionIntentProcessor( private val sessionUseCases: SessionUseCases, + private val sessionManager: SessionManager, useDefaultHandlers: Boolean = true ) { private val defaultActionViewHandler = { intent: Intent -> - val url = intent.dataString - if (TextUtils.isEmpty(url)) { - false - } else { - // TODO switch to loadUrlInNewTab - // https://github.com/mozilla-mobile/android-components/issues/136 - sessionUseCases.loadUrl.invoke(url) - true + val safeIntent = SafeIntent(intent) + val url = safeIntent.dataString ?: "" + + when { + TextUtils.isEmpty(url) -> false + + CustomTabConfig.isCustomTabIntent(safeIntent) -> { + val session = Session(url).apply { + this.customTabConfig = CustomTabConfig.createFromIntent(safeIntent) + } + sessionManager.add(session) + sessionUseCases.loadUrl.invoke(url, session) + intent.putExtra(ACTIVE_SESSION_ID, session.id) + true + } + + else -> { + // TODO support loadUrlInNewTab: https://github.com/mozilla-mobile/android-components/issues/136 + sessionUseCases.loadUrl.invoke(url) + true + } } } @@ -63,4 +81,8 @@ class SessionIntentProcessor( fun unregisterHandler(action: String) { handlers.remove(action) } + + companion object { + public const val ACTIVE_SESSION_ID = "activeSessionId" + } } diff --git a/components/feature/session/src/main/java/mozilla/components/feature/session/SessionUseCases.kt b/components/feature/session/src/main/java/mozilla/components/feature/session/SessionUseCases.kt index eef5c76f9ef..4831fc8718a 100644 --- a/components/feature/session/src/main/java/mozilla/components/feature/session/SessionUseCases.kt +++ b/components/feature/session/src/main/java/mozilla/components/feature/session/SessionUseCases.kt @@ -18,12 +18,14 @@ class SessionUseCases( private val sessionManager: SessionManager ) { /** - * Loads the provided URL using the currently selected session. + * Loads the provided URL using the provided session (or the currently + * selected session if none is provided). * * @param url url to load. + * @param session the session for which the URL should be loaded. */ - fun invoke(url: String) { - sessionManager.getOrCreateEngineSession().loadUrl(url) + fun invoke(url: String, session: Session = sessionManager.selectedSession) { + sessionManager.getOrCreateEngineSession(session).loadUrl(url) } } diff --git a/components/feature/session/src/test/java/mozilla/components/feature/session/EngineViewPresenterTest.kt b/components/feature/session/src/test/java/mozilla/components/feature/session/EngineViewPresenterTest.kt index 464549906e6..128c716eda5 100644 --- a/components/feature/session/src/test/java/mozilla/components/feature/session/EngineViewPresenterTest.kt +++ b/components/feature/session/src/test/java/mozilla/components/feature/session/EngineViewPresenterTest.kt @@ -4,18 +4,26 @@ package mozilla.components.feature.session +import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager +import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.EngineView import org.junit.Test +import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.mockito.Mockito.verify class EngineViewPresenterTest { private val sessionManager = mock(SessionManager::class.java) + private val session = mock(Session::class.java) + private val engineSession = mock(EngineSession::class.java) private val engineView = mock(EngineView::class.java) @Test fun testStartRegistersObserver() { + `when`(sessionManager.selectedSession).thenReturn(session) + `when`(sessionManager.getOrCreateEngineSession(session)).thenReturn(engineSession) + val engineViewPresenter = EngineViewPresenter(sessionManager, engineView) engineViewPresenter.start() verify(sessionManager).register(engineViewPresenter) diff --git a/components/feature/session/src/test/java/mozilla/components/feature/session/SessionFeatureTest.kt b/components/feature/session/src/test/java/mozilla/components/feature/session/SessionFeatureTest.kt index f00e946f914..224610b3762 100644 --- a/components/feature/session/src/test/java/mozilla/components/feature/session/SessionFeatureTest.kt +++ b/components/feature/session/src/test/java/mozilla/components/feature/session/SessionFeatureTest.kt @@ -6,6 +6,7 @@ package mozilla.components.feature.session import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager +import mozilla.components.browser.session.storage.SessionStorage import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.EngineView import org.junit.Test @@ -17,13 +18,15 @@ import org.mockito.Mockito.verify class SessionFeatureTest { private val sessionManager = mock(SessionManager::class.java) private val engineView = mock(EngineView::class.java) + private val sessionStorage = mock(SessionStorage::class.java) private val sessionUseCases = SessionUseCases(sessionManager) @Test - fun testStartEngineViewPresenter() { - val feature = SessionFeature(sessionManager, sessionUseCases, engineView) + fun testStart() { + val feature = SessionFeature(sessionManager, sessionUseCases, engineView, sessionStorage) feature.start() verify(sessionManager).register(feature.presenter) + verify(sessionStorage).start(sessionManager) } @Test @@ -45,9 +48,10 @@ class SessionFeatureTest { } @Test - fun testStopEngineViewPresenter() { - val feature = SessionFeature(sessionManager, sessionUseCases, engineView) + fun testStop() { + val feature = SessionFeature(sessionManager, sessionUseCases, engineView, sessionStorage) feature.stop() verify(sessionManager).unregister(feature.presenter) + verify(sessionStorage).stop() } } \ No newline at end of file diff --git a/components/feature/session/src/test/java/mozilla/components/feature/session/SessionIntentProcessorTest.kt b/components/feature/session/src/test/java/mozilla/components/feature/session/SessionIntentProcessorTest.kt index b297d40db20..b84c94d0987 100644 --- a/components/feature/session/src/test/java/mozilla/components/feature/session/SessionIntentProcessorTest.kt +++ b/components/feature/session/src/test/java/mozilla/components/feature/session/SessionIntentProcessorTest.kt @@ -5,16 +5,25 @@ package mozilla.components.feature.session import android.content.Intent +import android.support.customtabs.CustomTabsIntent +import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager +import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.EngineSession +import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito.`when` +import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock import org.mockito.Mockito.never +import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions import org.robolectric.RobolectricTestRunner @@ -22,17 +31,19 @@ import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class SessionIntentProcessorTest { private val sessionManager = mock(SessionManager::class.java) + private val session = mock(Session::class.java) private val engineSession = mock(EngineSession::class.java) private val useCases = SessionUseCases(sessionManager) @Before fun setup() { + `when`(sessionManager.selectedSession).thenReturn(session) `when`(sessionManager.getOrCreateEngineSession()).thenReturn(engineSession) } @Test fun testProcessWithDefaultHandlers() { - val handler = SessionIntentProcessor(useCases) + val handler = SessionIntentProcessor(useCases, sessionManager) val intent = mock(Intent::class.java) `when`(intent.action).thenReturn(Intent.ACTION_VIEW) @@ -47,7 +58,7 @@ class SessionIntentProcessorTest { @Test fun testProcessWithoutDefaultHandlers() { - val handler = SessionIntentProcessor(useCases, useDefaultHandlers = false) + val handler = SessionIntentProcessor(useCases, sessionManager, useDefaultHandlers = false) val intent = mock(Intent::class.java) `when`(intent.action).thenReturn(Intent.ACTION_VIEW) `when`(intent.dataString).thenReturn("http://mozilla.org") @@ -58,7 +69,7 @@ class SessionIntentProcessorTest { @Test fun testProcessWithCustomHandlers() { - val handler = SessionIntentProcessor(useCases, useDefaultHandlers = false) + val handler = SessionIntentProcessor(useCases, sessionManager, useDefaultHandlers = false) val intent = mock(Intent::class.java) `when`(intent.action).thenReturn(Intent.ACTION_SEND) @@ -77,4 +88,34 @@ class SessionIntentProcessorTest { handler.process(intent) assertFalse(handlerInvoked) } + + @Test + fun testProcessCustomTabIntentWithDefaultHandlers() { + val engine = mock(Engine::class.java) + val sessionManager = spy(SessionManager(engine)) + doReturn(engineSession).`when`(sessionManager).getOrCreateEngineSession(anySession()) + val useCases = SessionUseCases(sessionManager) + + val handler = SessionIntentProcessor(useCases, sessionManager) + + val intent = mock(Intent::class.java) + `when`(intent.action).thenReturn(Intent.ACTION_VIEW) + `when`(intent.hasExtra(CustomTabsIntent.EXTRA_SESSION)).thenReturn(true) + `when`(intent.dataString).thenReturn("http://mozilla.org") + + handler.process(intent) + verify(sessionManager).add(anySession(), eq(false), eq(null)) + verify(engineSession).loadUrl("http://mozilla.org") + + val customTabSession = sessionManager.all[0] + assertNotNull(customTabSession) + assertEquals("http://mozilla.org", customTabSession.url) + assertNotNull(customTabSession.customTabConfig) + } + + @Suppress("UNCHECKED_CAST") + private fun anySession(): T { + any() + return null as T + } } \ No newline at end of file diff --git a/components/feature/session/src/test/java/mozilla/components/feature/session/SessionUseCasesTest.kt b/components/feature/session/src/test/java/mozilla/components/feature/session/SessionUseCasesTest.kt index 3bc726f5ea5..08c68551762 100644 --- a/components/feature/session/src/test/java/mozilla/components/feature/session/SessionUseCasesTest.kt +++ b/components/feature/session/src/test/java/mozilla/components/feature/session/SessionUseCasesTest.kt @@ -32,6 +32,9 @@ class SessionUseCasesTest { fun testLoadUrl() { useCases.loadUrl.invoke("http://mozilla.org") verify(selectedEngineSession).loadUrl("http://mozilla.org") + + useCases.loadUrl.invoke("http://getpocket.com", selectedSession) + verify(selectedEngineSession).loadUrl("http://getpocket.com") } @Test diff --git a/components/feature/toolbar/src/main/java/mozilla/components/feature/toolbar/ToolbarFeature.kt b/components/feature/toolbar/src/main/java/mozilla/components/feature/toolbar/ToolbarFeature.kt index 15073943419..db0be9071df 100644 --- a/components/feature/toolbar/src/main/java/mozilla/components/feature/toolbar/ToolbarFeature.kt +++ b/components/feature/toolbar/src/main/java/mozilla/components/feature/toolbar/ToolbarFeature.kt @@ -21,9 +21,10 @@ class ToolbarFeature( val toolbar: Toolbar, sessionManager: SessionManager, loadUrlUseCase: SessionUseCases.LoadUrlUseCase, - searchUseCase: SearchUseCase? = null + searchUseCase: SearchUseCase? = null, + sessionId: String? = null ) { - private val presenter = ToolbarPresenter(toolbar, sessionManager) + private val presenter = ToolbarPresenter(toolbar, sessionManager, sessionId) private val interactor = ToolbarInteractor(toolbar, loadUrlUseCase, searchUseCase) /** diff --git a/components/feature/toolbar/src/main/java/mozilla/components/feature/toolbar/ToolbarPresenter.kt b/components/feature/toolbar/src/main/java/mozilla/components/feature/toolbar/ToolbarPresenter.kt index 34a78983176..79ec56091f1 100644 --- a/components/feature/toolbar/src/main/java/mozilla/components/feature/toolbar/ToolbarPresenter.kt +++ b/components/feature/toolbar/src/main/java/mozilla/components/feature/toolbar/ToolbarPresenter.kt @@ -14,18 +14,19 @@ import mozilla.components.browser.session.SessionManager */ class ToolbarPresenter( private val toolbar: Toolbar, - private val sessionManager: SessionManager + private val sessionManager: SessionManager, + private val sessionId: String? = null ) : SessionManager.Observer, Session.Observer { - var session: Session = sessionManager.selectedSession + lateinit var activeSession: Session /** * Start presenter: Display data in toolbar. */ fun start() { - session = sessionManager.selectedSession + activeSession = sessionId?.let { sessionManager.findSessionById(sessionId) } ?: sessionManager.selectedSession sessionManager.register(this) - session.register(this) + activeSession.register(this) initializeView() } @@ -35,41 +36,40 @@ class ToolbarPresenter( */ fun stop() { sessionManager.unregister(this) - session.unregister(this) + activeSession.unregister(this) } /** * A new session has been selected: Update toolbar to display data of new session. */ override fun onSessionSelected(session: Session) { - this.session.unregister(this) + activeSession.unregister(this) - this.session = session + activeSession = session session.register(this) initializeView() } private fun initializeView() { - toolbar.url = session.url + toolbar.url = activeSession.url + + activeSession.customTabConfig?.toolbarColor?.let { + toolbar.asView().setBackgroundColor(it) + } + // TODO Apply remaining configurations: https://github.com/mozilla-mobile/android-components/issues/306 } override fun onUrlChanged() { - toolbar.url = session.url - toolbar.setSearchTerms(session.searchTerms) + toolbar.url = activeSession.url + toolbar.setSearchTerms(activeSession.searchTerms) } override fun onProgress() { - toolbar.displayProgress(session.progress) + toolbar.displayProgress(activeSession.progress) } override fun onSearch() { - toolbar.setSearchTerms(session.searchTerms) + toolbar.setSearchTerms(activeSession.searchTerms) } - - override fun onLoadingStateChanged() { } - - override fun onNavigationStateChanged() { /* TODO */ } - - override fun onSecurityChanged() { /* TODO */ } } diff --git a/samples/browser/src/main/AndroidManifest.xml b/samples/browser/src/main/AndroidManifest.xml index 7b0ab4c8c32..931032b0440 100644 --- a/samples/browser/src/main/AndroidManifest.xml +++ b/samples/browser/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ - 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/. --> - + android:theme="@style/Theme.AppCompat.Light.NoActionBar" + android:name=".SampleApplication"> + + + + @@ -25,6 +32,16 @@ + + + + + + + diff --git a/samples/browser/src/main/java/org/mozilla/samples/browser/MainActivity.kt b/samples/browser/src/main/java/org/mozilla/samples/browser/BrowserActivity.kt similarity index 64% rename from samples/browser/src/main/java/org/mozilla/samples/browser/MainActivity.kt rename to samples/browser/src/main/java/org/mozilla/samples/browser/BrowserActivity.kt index b415055f231..02a85b10e55 100644 --- a/samples/browser/src/main/java/org/mozilla/samples/browser/MainActivity.kt +++ b/samples/browser/src/main/java/org/mozilla/samples/browser/BrowserActivity.kt @@ -12,15 +12,14 @@ import android.view.View import kotlinx.android.synthetic.main.activity_main.* import mozilla.components.concept.engine.EngineView import mozilla.components.feature.session.SessionFeature +import mozilla.components.feature.session.SessionIntentProcessor import mozilla.components.feature.toolbar.ToolbarFeature +import mozilla.components.support.utils.SafeIntent +import org.mozilla.samples.browser.ext.components -class MainActivity : AppCompatActivity() { - private var sessionFeature: SessionFeature? = null - private var toolbarFeature: ToolbarFeature? = null - - private val components by lazy { - Components(applicationContext) - } +open class BrowserActivity : AppCompatActivity() { + private lateinit var sessionFeature: SessionFeature + private lateinit var toolbarFeature: ToolbarFeature override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -28,40 +27,42 @@ class MainActivity : AppCompatActivity() { toolbar.setMenuBuilder(components.menuBuilder) + val sessionId = SafeIntent(intent).getStringExtra(SessionIntentProcessor.ACTIVE_SESSION_ID) + sessionFeature = SessionFeature( components.sessionManager, components.sessionUseCases, engineView, - components.sessionStorage) + components.sessionStorage, + sessionId) toolbarFeature = ToolbarFeature( toolbar, components.sessionManager, components.sessionUseCases.loadUrl, - components.defaultSearchUseCase) - - components.sessionIntentProcessor.process(intent) + components.defaultSearchUseCase, + sessionId) } - override fun onResume() { - super.onResume() + override fun onStart() { + super.onStart() - sessionFeature?.start() - toolbarFeature?.start() + sessionFeature.start() + toolbarFeature.start() } - override fun onPause() { - super.onPause() + override fun onStop() { + super.onStop() - sessionFeature?.stop() - toolbarFeature?.stop() + sessionFeature.stop() + toolbarFeature.stop() } override fun onBackPressed() { - if (toolbarFeature?.handleBackPressed() == true) + if (toolbarFeature.handleBackPressed()) return - if (sessionFeature?.handleBackPressed() == true) + if (sessionFeature.handleBackPressed()) return super.onBackPressed() diff --git a/samples/browser/src/main/java/org/mozilla/samples/browser/Components.kt b/samples/browser/src/main/java/org/mozilla/samples/browser/Components.kt index 8a0b1d6163a..692c7c29be9 100644 --- a/samples/browser/src/main/java/org/mozilla/samples/browser/Components.kt +++ b/samples/browser/src/main/java/org/mozilla/samples/browser/Components.kt @@ -23,10 +23,10 @@ import mozilla.components.feature.session.SessionUseCases * Helper class for lazily instantiating components needed by the application. */ class Components(private val applicationContext: Context) { + // Engine val engine: Engine by lazy { EngineProvider.createEngine(applicationContext) } // Session - val sessionStorage by lazy { DefaultSessionStorage(applicationContext) } val sessionManager by lazy { @@ -41,7 +41,7 @@ class Components(private val applicationContext: Context) { } val sessionUseCases by lazy { SessionUseCases(sessionManager) } - val sessionIntentProcessor by lazy { SessionIntentProcessor(sessionUseCases) } + val sessionIntentProcessor by lazy { SessionIntentProcessor(sessionUseCases, sessionManager) } // Search private val searchEngineManager by lazy { diff --git a/samples/browser/src/main/java/org/mozilla/samples/browser/IntentReceiverActivity.kt b/samples/browser/src/main/java/org/mozilla/samples/browser/IntentReceiverActivity.kt new file mode 100644 index 00000000000..7b4bd0ce9c3 --- /dev/null +++ b/samples/browser/src/main/java/org/mozilla/samples/browser/IntentReceiverActivity.kt @@ -0,0 +1,17 @@ +/* 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.samples.browser + +import android.os.Bundle +import org.mozilla.samples.browser.ext.components + +class IntentReceiverActivity : BrowserActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + components.sessionIntentProcessor.process(intent) + super.onCreate(savedInstanceState) + } + +} \ No newline at end of file diff --git a/samples/browser/src/main/java/org/mozilla/samples/browser/SampleApplication.kt b/samples/browser/src/main/java/org/mozilla/samples/browser/SampleApplication.kt new file mode 100644 index 00000000000..b216521eded --- /dev/null +++ b/samples/browser/src/main/java/org/mozilla/samples/browser/SampleApplication.kt @@ -0,0 +1,11 @@ +/* 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.samples.browser + +import android.app.Application + +class SampleApplication : Application() { + val components by lazy { Components(this) } +} \ No newline at end of file diff --git a/samples/browser/src/main/java/org/mozilla/samples/browser/ext/Context.kt b/samples/browser/src/main/java/org/mozilla/samples/browser/ext/Context.kt new file mode 100644 index 00000000000..2dc924356a3 --- /dev/null +++ b/samples/browser/src/main/java/org/mozilla/samples/browser/ext/Context.kt @@ -0,0 +1,21 @@ +/* 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.samples.browser.ext + +import android.content.Context +import org.mozilla.samples.browser.Components +import org.mozilla.samples.browser.SampleApplication + +/** + * Get the SampleApplication object from a context. + */ +val Context.application: SampleApplication + get() = applicationContext as SampleApplication + +/** + * Get the components of this application. + */ +val Context.components: Components + get() = application.components diff --git a/samples/browser/src/main/res/layout/activity_main.xml b/samples/browser/src/main/res/layout/activity_main.xml index 74191d0c4c7..c80bfa2d9c5 100644 --- a/samples/browser/src/main/res/layout/activity_main.xml +++ b/samples/browser/src/main/res/layout/activity_main.xml @@ -7,7 +7,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".MainActivity"> + tools:context=".BrowserActivity">