Skip to content

Commit 28cbd95

Browse files
Grisha Kruglovgrigoryk
Grisha Kruglov
authored andcommitted
Add feature-storage with a feature implementing HistoryTrackingDelegate
1 parent d3eb7d1 commit 28cbd95

File tree

8 files changed

+240
-0
lines changed

8 files changed

+240
-0
lines changed

automation/taskcluster/artifacts.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@
9191
public/build/feature.session.maven.zip:
9292
path: /build/android-components/components/feature/session/build/target.maven.zip
9393
name: feature-session
94+
public/build/feature.storage.maven.zip:
95+
path: /build/android-components/components/feature/storage/build/target.maven.zip
96+
name: feature-storage
9497
public/build/feature.toolbar.maven.zip:
9598
path: /build/android-components/components/feature/toolbar/build/target.maven.zip
9699
name: feature-toolbar

components/feature/storage/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# [Android Components](../../../README.md) > Feature > Storage
2+
3+
A component that connects an (concept) engine implementation with a concept storage implementation.
4+
5+
## Usage
6+
7+
### Setting up the dependency
8+
9+
Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)):
10+
11+
```Groovy
12+
implementation "org.mozilla.components:feature-storage:{latest-version}"
13+
```
14+
15+
## License
16+
17+
This Source Code Form is subject to the terms of the Mozilla Public
18+
License, v. 2.0. If a copy of the MPL was not distributed with this
19+
file, You can obtain one at http://mozilla.org/MPL/2.0/
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
apply plugin: 'com.android.library'
6+
apply plugin: 'kotlin-android'
7+
8+
android {
9+
compileSdkVersion Config.compileSdkVersion
10+
11+
defaultConfig {
12+
minSdkVersion Config.minSdkVersion
13+
targetSdkVersion Config.targetSdkVersion
14+
}
15+
16+
buildTypes {
17+
release {
18+
minifyEnabled false
19+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20+
}
21+
}
22+
}
23+
24+
dependencies {
25+
implementation project(':concept-engine')
26+
implementation project(':concept-storage')
27+
implementation project(':support-ktx')
28+
29+
implementation Deps.kotlin_stdlib
30+
31+
testImplementation Deps.testing_junit
32+
testImplementation Deps.testing_robolectric
33+
testImplementation Deps.testing_mockito
34+
35+
testImplementation project(':support-test')
36+
}
37+
38+
apply from: '../../../publish.gradle'
39+
ext.configurePublish(Config.componentsGroupId, archivesBaseName, gradle.componentDescriptions[archivesBaseName])
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<!-- This Source Code Form is subject to the terms of the Mozilla Public
2+
- License, v. 2.0. If a copy of the MPL was not distributed with this
3+
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
4+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
5+
package="mozilla.components.feature.storage" />
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package mozilla.components.feature.storage
6+
7+
import android.support.annotation.VisibleForTesting
8+
import mozilla.components.concept.engine.Engine
9+
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
10+
import mozilla.components.concept.storage.HistoryStorage
11+
import mozilla.components.concept.storage.PageObservation
12+
import mozilla.components.concept.storage.VisitType
13+
14+
/**
15+
* Feature implementation for connecting an engine implementation with a history storage implementation
16+
* in order to enable engine to track history.
17+
*/
18+
class HistoryTrackingFeature(engine: Engine, historyStorage: HistoryStorage) {
19+
init {
20+
engine.settings.historyTrackingDelegate = HistoryDelegate(historyStorage)
21+
}
22+
}
23+
24+
@VisibleForTesting
25+
internal class HistoryDelegate(private val historyStorage: HistoryStorage) : HistoryTrackingDelegate {
26+
override fun onVisited(uri: String, isReload: Boolean, privateMode: Boolean) {
27+
if (privateMode) {
28+
return
29+
}
30+
val visitType = when (isReload) {
31+
true -> VisitType.RELOAD
32+
false -> VisitType.LINK
33+
}
34+
historyStorage.recordVisit(uri, visitType)
35+
}
36+
37+
override fun onTitleChanged(uri: String, title: String, privateMode: Boolean) {
38+
if (privateMode) {
39+
return
40+
}
41+
historyStorage.recordObservation(uri, PageObservation(title = title))
42+
}
43+
44+
override fun getVisited(uris: List<String>, callback: (List<Boolean>) -> Unit, privateMode: Boolean) {
45+
if (privateMode) {
46+
callback(List(uris.size) { false })
47+
return
48+
}
49+
historyStorage.getVisited(uris, callback)
50+
}
51+
52+
override fun getVisited(callback: (List<String>) -> Unit, privateMode: Boolean) {
53+
if (privateMode) {
54+
callback(listOf())
55+
return
56+
}
57+
historyStorage.getVisited(callback)
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package mozilla.components.feature.storage
6+
7+
import mozilla.components.concept.engine.DefaultSettings
8+
import mozilla.components.concept.engine.Engine
9+
import mozilla.components.concept.storage.HistoryStorage
10+
import mozilla.components.concept.storage.PageObservation
11+
import mozilla.components.concept.storage.VisitType
12+
import mozilla.components.support.test.any
13+
import mozilla.components.support.test.mock
14+
import org.junit.Assert.assertNull
15+
import org.junit.Assert.assertNotNull
16+
import org.junit.Test
17+
import org.mockito.Mockito.`when`
18+
import org.mockito.Mockito.verify
19+
import org.mockito.Mockito.never
20+
21+
class HistoryTrackingFeatureTest {
22+
@Test
23+
fun `feature sets a history delegate on the engine`() {
24+
val engine: Engine = mock()
25+
val settings = DefaultSettings()
26+
`when`(engine.settings).thenReturn(settings)
27+
28+
assertNull(settings.historyTrackingDelegate)
29+
HistoryTrackingFeature(engine, mock())
30+
assertNotNull(settings.historyTrackingDelegate)
31+
}
32+
33+
@Test
34+
fun `history delegate doesn't interact with storage in private mode`() {
35+
val storage: HistoryStorage = mock()
36+
val delegate = HistoryDelegate(storage)
37+
38+
delegate.onVisited("http://www.mozilla.org", false, true)
39+
verify(storage, never()).recordVisit(any(), any())
40+
41+
delegate.onTitleChanged("http://www.mozilla.org", "Mozilla", true)
42+
verify(storage, never()).recordObservation(any(), any())
43+
44+
val systemCallback = mock<(List<String>) -> Unit>()
45+
delegate.getVisited(systemCallback, true)
46+
verify(systemCallback).invoke(listOf())
47+
verify(storage, never()).getVisited(systemCallback)
48+
49+
val geckoCallback = mock<(List<Boolean>) -> Unit>()
50+
delegate.getVisited(listOf("http://www.mozilla.com"), geckoCallback, true)
51+
verify(geckoCallback).invoke(listOf(false))
52+
verify(storage, never()).getVisited(listOf("http://www.mozilla.com"), geckoCallback)
53+
54+
delegate.getVisited(listOf("http://www.mozilla.com", "http://www.firefox.com"), geckoCallback, true)
55+
verify(geckoCallback).invoke(listOf(false, false))
56+
verify(storage, never()).getVisited(listOf("http://www.mozilla.com", "http://www.firefox.com"), geckoCallback)
57+
}
58+
59+
@Test
60+
fun `history delegate passes through onVisited calls`() {
61+
val storage: HistoryStorage = mock()
62+
val delegate = HistoryDelegate(storage)
63+
64+
delegate.onVisited("http://www.mozilla.org", false, false)
65+
verify(storage).recordVisit("http://www.mozilla.org", VisitType.LINK)
66+
67+
delegate.onVisited("http://www.firefox.com", true, false)
68+
verify(storage).recordVisit("http://www.firefox.com", VisitType.RELOAD)
69+
}
70+
71+
@Test
72+
fun `history delegate passes through onTitleChanged calls`() {
73+
val storage: HistoryStorage = mock()
74+
val delegate = HistoryDelegate(storage)
75+
76+
delegate.onTitleChanged("http://www.mozilla.org", "Mozilla", false)
77+
verify(storage).recordObservation("http://www.mozilla.org", PageObservation("Mozilla"))
78+
}
79+
80+
@Test
81+
fun `history delegate passes through getVisited calls`() {
82+
val storage: HistoryStorage = mock()
83+
val delegate = HistoryDelegate(storage)
84+
85+
val systemCallback = mock<(List<String>) -> Unit>()
86+
delegate.getVisited(systemCallback, false)
87+
verify(storage).getVisited(systemCallback)
88+
89+
val geckoCallback = mock<(List<Boolean>) -> Unit>()
90+
delegate.getVisited(listOf("http://www.mozilla.org", "http://www.firefox.com"), geckoCallback, false)
91+
verify(storage).getVisited(listOf("http://www.mozilla.org", "http://www.firefox.com"), geckoCallback)
92+
}
93+
}

settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ setupProject(':concept-storage', 'components/concept/storage', 'An abstract defi
3131
setupProject(':feature-intent', 'components/feature/intent', 'Combining various feature components for intent processing.')
3232
setupProject(':feature-search', 'components/feature/search', 'Feature implementation connecting an engine implementation with the search module.')
3333
setupProject(':feature-session', 'components/feature/session', 'Feature implementation connecting an engine implementation with the session module.')
34+
setupProject(':feature-storage', 'components/feature/storage', 'Feature implementation connecting a storage implementation with various consumers (like an engine implementation).')
3435
setupProject(':feature-tabs', 'components/feature/tabs', 'Feature implementation connecting a tabs tray implementation with the session and toolbar modules.')
3536
setupProject(':feature-toolbar', 'components/feature/toolbar', 'Feature implementation connecting a toolbar implementation with the session module.')
3637
setupProject(':feature-downloads', 'components/feature/downloads', 'Feature implementation for apps that want to use Android downloads manager.')

0 commit comments

Comments
 (0)