Skip to content

Commit a1c8650

Browse files
CDRussellsubsymbolic
authored andcommitted
Add about page and stub settings page
1 parent ed393ca commit a1c8650

25 files changed

+709
-59
lines changed

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserViewModelTest.kt

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ package com.duckduckgo.app.browser
1919
import android.arch.core.executor.testing.InstantTaskExecutorRule
2020
import android.arch.lifecycle.Observer
2121
import android.net.Uri
22-
import com.duckduckgo.app.browser.BrowserViewModel.NavigationCommand
2322
import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
23+
import com.duckduckgo.app.global.StringResolver
2424
import com.duckduckgo.app.privacymonitor.model.PrivacyGrade
2525
import com.duckduckgo.app.privacymonitor.store.PrivacyMonitorRepository
2626
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceStore
@@ -43,8 +43,9 @@ class BrowserViewModelTest {
4343
var instantTaskExecutorRule = InstantTaskExecutorRule()
4444

4545
private lateinit var queryObserver: Observer<String>
46-
private lateinit var navigationObserver: Observer<NavigationCommand>
46+
private lateinit var navigationObserver: Observer<BrowserViewModel.Command>
4747
private lateinit var termsOfServiceStore: TermsOfServiceStore
48+
private lateinit var mockStringResolver: StringResolver
4849
private lateinit var testee: BrowserViewModel
4950

5051
private val testOmnibarConverter: OmnibarEntryConverter = object : OmnibarEntryConverter {
@@ -55,18 +56,22 @@ class BrowserViewModelTest {
5556

5657
@Before
5758
fun before() {
59+
mockStringResolver = mock()
5860
queryObserver = mock()
5961
navigationObserver = mock()
6062
termsOfServiceStore = mock()
61-
testee = BrowserViewModel(testOmnibarConverter, DuckDuckGoUrlDetector(), termsOfServiceStore, TrackerNetworks(), PrivacyMonitorRepository())
63+
testee = BrowserViewModel(testOmnibarConverter, DuckDuckGoUrlDetector(), termsOfServiceStore, TrackerNetworks(), PrivacyMonitorRepository(), object : StringResolver {
64+
override fun getString(stringId: Int): String = ""
65+
override fun getString(stringId: Int, vararg formatArgs: Any): String = ""
66+
})
6267
testee.query.observeForever(queryObserver)
63-
testee.navigation.observeForever(navigationObserver)
68+
testee.command.observeForever(navigationObserver)
6469
}
6570

6671
@After
6772
fun after() {
6873
testee.query.removeObserver(queryObserver)
69-
testee.navigation.removeObserver(navigationObserver)
74+
testee.command.removeObserver(navigationObserver)
7075
}
7176

7277
@Test
@@ -155,13 +160,13 @@ class BrowserViewModelTest {
155160
@Test
156161
fun whenUserDismissesKeyboardBeforeBrowserShownThenShouldNavigateToLandingPage() {
157162
testee.userDismissedKeyboard()
158-
verify(navigationObserver).onChanged(NavigationCommand.LANDING_PAGE)
163+
verify(navigationObserver).onChanged(ArgumentMatchers.any(BrowserViewModel.Command.LandingPage::class.java))
159164
}
160165

161166
@Test
162167
fun whenUserDismissesKeyboardAfterBrowserShownThenShouldNotNavigateToLandingPage() {
163168
testee.urlChanged("")
164-
verify(navigationObserver, never()).onChanged(NavigationCommand.LANDING_PAGE)
169+
verify(navigationObserver, never()).onChanged(ArgumentMatchers.any(BrowserViewModel.Command.LandingPage::class.java))
165170
}
166171

167172
@Test

app/src/main/AndroidManifest.xml

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,43 @@
1717
android:name="com.duckduckgo.app.home.HomeActivity"
1818
android:label="@string/appName"
1919
android:parentActivityName="com.duckduckgo.app.home.HomeActivity"
20-
android:screenOrientation="fullSensor"
21-
android:theme="@style/AppTheme.NoActionBar">
20+
android:screenOrientation="fullSensor">
2221
<intent-filter>
2322
<action android:name="android.intent.action.MAIN" />
23+
2424
<category android:name="android.intent.category.LAUNCHER" />
2525
</intent-filter>
2626
</activity>
2727
<activity
2828
android:name=".BrowserActivity"
2929
android:configChanges="keyboardHidden|orientation|screenSize"
30-
android:screenOrientation="fullSensor"
31-
android:theme="@style/AppTheme.NoActionBar" />
30+
android:screenOrientation="fullSensor" />
3231
<activity
3332
android:name="com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity"
3433
android:label="@string/privacyDashboardActivityTitle"
3534
android:parentActivityName=".BrowserActivity"
36-
android:screenOrientation="fullSensor"
37-
android:theme="@style/AppTheme.NoActionBar" />
35+
android:screenOrientation="fullSensor" />
3836
<activity
3937
android:name="com.duckduckgo.app.privacymonitor.ui.TrackerNetworksActivity"
4038
android:label="@string/networksActivityTitle"
41-
android:parentActivityName="com.duckduckgo.app.privacymonitor.ui.TrackerNetworksActivity"
42-
android:theme="@style/AppTheme.NoActionBar" />
39+
android:parentActivityName="com.duckduckgo.app.privacymonitor.ui.TrackerNetworksActivity" />
4340
<activity
4441
android:name="com.duckduckgo.app.privacymonitor.ui.PrivacyPracticesActivity"
4542
android:label="@string/privacyTermsActivityTitle"
4643
android:parentActivityName="com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity"
47-
android:theme="@style/AppTheme.NoActionBar" />
48-
44+
android:screenOrientation="fullSensor" />
45+
<activity
46+
android:name="com.duckduckgo.app.settings.SettingsActivity"
47+
android:label="@string/settingsActivityTitle"
48+
android:parentActivityName=".BrowserActivity"
49+
android:screenOrientation="fullSensor" />
50+
<activity
51+
android:name="com.duckduckgo.app.about.AboutDuckDuckGoActivity"
52+
android:label="@string/aboutActivityTitle"
53+
android:parentActivityName="com.duckduckgo.app.settings.SettingsActivity"
54+
android:theme="@style/AppTheme"
55+
android:screenOrientation="fullSensor" >
56+
</activity>
4957
</application>
5058

5159
</manifest>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2017 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.about
18+
19+
import android.content.Context
20+
import android.content.Intent
21+
import android.os.Bundle
22+
import android.support.v7.app.AppCompatActivity
23+
import com.duckduckgo.app.browser.R
24+
import kotlinx.android.synthetic.main.activity_about_duck_duck_go.*
25+
import kotlinx.android.synthetic.main.content_about_duck_duck_go.*
26+
27+
class AboutDuckDuckGoActivity : AppCompatActivity() {
28+
29+
override fun onCreate(savedInstanceState: Bundle?) {
30+
super.onCreate(savedInstanceState)
31+
setContentView(R.layout.activity_about_duck_duck_go)
32+
configureActionBar()
33+
34+
learnMoreLink.setOnClickListener {
35+
setResult(RESULT_CODE_LOAD_ABOUT_DDG_WEB_PAGE)
36+
finish()
37+
}
38+
}
39+
40+
private fun configureActionBar() {
41+
setSupportActionBar(toolbar)
42+
supportActionBar?.setDisplayHomeAsUpEnabled(true)
43+
}
44+
45+
companion object {
46+
fun intent(context: Context): Intent {
47+
return Intent(context, AboutDuckDuckGoActivity::class.java)
48+
}
49+
50+
const val RESULT_CODE_LOAD_ABOUT_DDG_WEB_PAGE = 100
51+
}
52+
53+
54+
55+
}

app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,14 @@ import android.view.View
3030
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
3131
import android.webkit.WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
3232
import android.widget.TextView
33-
import com.duckduckgo.app.browser.BrowserViewModel.NavigationCommand.LANDING_PAGE
3433
import com.duckduckgo.app.browser.omnibar.OnBackKeyListener
3534
import com.duckduckgo.app.global.DuckDuckGoActivity
3635
import com.duckduckgo.app.global.ViewModelFactory
3736
import com.duckduckgo.app.global.view.*
3837
import com.duckduckgo.app.privacymonitor.model.PrivacyGrade
3938
import com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity
4039
import com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity.Companion.REQUEST_DASHBOARD
41-
import com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity.Companion.RESULT_RELOAD
42-
import com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity.Companion.RESULT_TOSDR
40+
import com.duckduckgo.app.settings.SettingsActivity
4341
import kotlinx.android.synthetic.main.activity_browser.*
4442
import javax.inject.Inject
4543

@@ -57,6 +55,8 @@ class BrowserActivity : DuckDuckGoActivity() {
5755

5856
companion object {
5957
fun intent(context: Context): Intent = Intent(context, BrowserActivity::class.java)
58+
59+
private const val REQUEST_SETTINGS = 1001
6060
}
6161

6262
private val privacyGradeMenu: MenuItem?
@@ -78,9 +78,17 @@ class BrowserActivity : DuckDuckGoActivity() {
7878
it?.let { webView.loadUrl(it) }
7979
})
8080

81-
viewModel.navigation.observe(this, Observer {
82-
if (it == LANDING_PAGE) {
83-
finishActivityAnimated()
81+
viewModel.command.observe(this, Observer {
82+
when(it) {
83+
is BrowserViewModel.Command.Refresh -> webView.reload()
84+
is BrowserViewModel.Command.Navigate -> {
85+
focusDummy.requestFocus()
86+
webView.loadUrl(it.url)
87+
}
88+
is BrowserViewModel.Command.LandingPage -> {
89+
finishActivityAnimated()
90+
return@Observer
91+
}
8492
}
8593
})
8694

@@ -260,6 +268,10 @@ class BrowserActivity : DuckDuckGoActivity() {
260268
webView.goForward()
261269
return true
262270
}
271+
R.id.settings_menu_item -> {
272+
launchSettingsView()
273+
return true
274+
}
263275
}
264276
return false
265277
}
@@ -273,13 +285,15 @@ class BrowserActivity : DuckDuckGoActivity() {
273285
startActivityForResult(PrivacyDashboardActivity.intent(this), REQUEST_DASHBOARD)
274286
}
275287

288+
private fun launchSettingsView() {
289+
startActivityForResult(SettingsActivity.intent(this), REQUEST_SETTINGS)
290+
}
291+
276292
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
277-
if (requestCode != REQUEST_DASHBOARD) {
278-
super.onActivityResult(requestCode, resultCode, data)
279-
}
280-
when (resultCode) {
281-
RESULT_RELOAD -> webView.reload()
282-
RESULT_TOSDR -> webView.loadUrl(getString(R.string.tosdrUrl))
293+
when(requestCode) {
294+
REQUEST_DASHBOARD -> viewModel.receivedDashboardResult(resultCode)
295+
REQUEST_SETTINGS -> viewModel.receivedSettingsResult(resultCode)
296+
else -> super.onActivityResult(requestCode, resultCode, data)
283297
}
284298
}
285299

app/src/main/java/com/duckduckgo/app/browser/BrowserViewModel.kt

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,20 @@ package com.duckduckgo.app.browser
1818

1919
import android.arch.lifecycle.MutableLiveData
2020
import android.arch.lifecycle.ViewModel
21+
import com.duckduckgo.app.about.AboutDuckDuckGoActivity.Companion.RESULT_CODE_LOAD_ABOUT_DDG_WEB_PAGE
22+
import com.duckduckgo.app.browser.BrowserViewModel.Command.Navigate
23+
import com.duckduckgo.app.browser.BrowserViewModel.Command.Refresh
2124
import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
2225
import com.duckduckgo.app.global.SingleLiveEvent
26+
import com.duckduckgo.app.global.StringResolver
2327
import com.duckduckgo.app.privacymonitor.SiteMonitor
2428
import com.duckduckgo.app.privacymonitor.model.PrivacyGrade
2529
import com.duckduckgo.app.privacymonitor.model.TermsOfService
2630
import com.duckduckgo.app.privacymonitor.model.improvedGrade
2731
import com.duckduckgo.app.privacymonitor.store.PrivacyMonitorRepository
2832
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceStore
33+
import com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity.Companion.RESULT_RELOAD
34+
import com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity.Companion.RESULT_TOSDR
2935
import com.duckduckgo.app.trackerdetection.model.TrackerNetworks
3036
import com.duckduckgo.app.trackerdetection.model.TrackingEvent
3137
import timber.log.Timber
@@ -35,7 +41,8 @@ class BrowserViewModel(
3541
private val duckDuckGoUrlDetector: DuckDuckGoUrlDetector,
3642
private val termsOfServiceStore: TermsOfServiceStore,
3743
private val trackerNetworks: TrackerNetworks,
38-
private val privacyMonitorRepository: PrivacyMonitorRepository) :
44+
private val privacyMonitorRepository: PrivacyMonitorRepository,
45+
private val stringResolver: StringResolver) :
3946
WebViewClientListener, ViewModel() {
4047

4148
data class ViewState(
@@ -52,10 +59,12 @@ class BrowserViewModel(
5259
val viewState: MutableLiveData<ViewState> = MutableLiveData()
5360
val privacyGrade: MutableLiveData<PrivacyGrade> = MutableLiveData()
5461
val query: SingleLiveEvent<String> = SingleLiveEvent()
55-
val navigation: SingleLiveEvent<NavigationCommand> = SingleLiveEvent()
62+
val command: SingleLiveEvent<Command> = SingleLiveEvent()
5663

57-
enum class NavigationCommand {
58-
LANDING_PAGE
64+
sealed class Command {
65+
class LandingPage : Command()
66+
class Refresh : Command()
67+
class Navigate(val url: String) : Command()
5968
}
6069

6170
private var siteMonitor: SiteMonitor? = null
@@ -147,11 +156,30 @@ class BrowserViewModel(
147156
*/
148157
fun userDismissedKeyboard(): Boolean {
149158
if (!currentViewState().browserShowing) {
150-
navigation.value = NavigationCommand.LANDING_PAGE
159+
command.value = Command.LandingPage()
151160
return true
152161
}
153162
return false
154163
}
164+
165+
fun receivedDashboardResult(resultCode: Int) {
166+
when (resultCode) {
167+
RESULT_RELOAD -> command.value = Refresh()
168+
RESULT_TOSDR -> {
169+
val url = stringResolver.getString(R.string.tosdrUrl)
170+
command.value = Navigate(url)
171+
}
172+
}
173+
}
174+
175+
fun receivedSettingsResult(resultCode: Int) {
176+
when (resultCode) {
177+
RESULT_CODE_LOAD_ABOUT_DDG_WEB_PAGE -> {
178+
val url = stringResolver.getString(R.string.aboutUrl)
179+
command.value = Navigate(url)
180+
}
181+
}
182+
}
155183
}
156184

157185

app/src/main/java/com/duckduckgo/app/di/ActivityBindingModule.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.duckduckgo.app.di
1919
import com.duckduckgo.app.browser.BrowserActivity
2020
import com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardActivity
2121
import com.duckduckgo.app.privacymonitor.ui.PrivacyPracticesActivity
22+
import com.duckduckgo.app.settings.SettingsActivity
2223
import com.duckduckgo.app.privacymonitor.ui.TrackerNetworksActivity
2324

2425
import dagger.Module
@@ -42,4 +43,8 @@ abstract class ActivityBindingModule {
4243
@ActivityScoped
4344
@ContributesAndroidInjector
4445
abstract fun privacyTermsActivity(): PrivacyPracticesActivity
46+
47+
@ActivityScoped
48+
@ContributesAndroidInjector
49+
abstract fun settingsActivity(): SettingsActivity
4550
}

app/src/main/java/com/duckduckgo/app/di/AppComponent.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ import javax.inject.Singleton
3333
(NetworkModule::class),
3434
(PrivacyModule::class),
3535
(DatabaseModule::class),
36-
(JsonModule::class)
36+
(JsonModule::class),
37+
(StringModule::class)
3738
])
3839
interface AppComponent : AndroidInjector<DuckDuckGoApplication> {
3940

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2018 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.di
18+
19+
import android.content.Context
20+
import com.duckduckgo.app.global.AndroidStringResolver
21+
import com.duckduckgo.app.global.StringResolver
22+
import dagger.Module
23+
import dagger.Provides
24+
25+
@Module
26+
class StringModule {
27+
28+
@Provides
29+
fun providesStringResolver(context: Context): StringResolver {
30+
return AndroidStringResolver(context)
31+
}
32+
33+
}
34+

0 commit comments

Comments
 (0)