Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
240edf9
Update AGP to latest beta
CDRussell Aug 15, 2018
f7764bc
Merge branch 'develop' into feature/fire_button_visited_sites_fix
CDRussell Aug 16, 2018
8b7837a
Update to support library 28 to match target API version
CDRussell Aug 16, 2018
7f36f74
Potential interim solution for clearing process on 🔥 button press
CDRussell Aug 16, 2018
aa188d4
Add competing process-restart implementations
CDRussell Aug 20, 2018
90eefa5
Remove FireActivity approach to restarting app process
CDRussell Aug 20, 2018
26981e2
Move fire process name to strings resources
CDRussell Aug 20, 2018
def7117
Merge branch 'develop' into feature/fire_button_visited_sites_fix
CDRussell Aug 20, 2018
4cc5415
Add Lottie to project
CDRussell Aug 22, 2018
fcd58c3
Add flame animation
CDRussell Aug 22, 2018
fbfe4c2
Remove fire splash activity layer list
CDRussell Aug 22, 2018
9df8cd2
Fix BrowserActivity menu xml reference
CDRussell Aug 22, 2018
d6406d4
Show toast upon re-entering app after fire animation
CDRussell Aug 22, 2018
678c0d4
Configure FireSplashActivity to show Lottie fire animation
CDRussell Aug 22, 2018
e9f5f43
Rename FireSplashActivity to FireActivity
CDRussell Aug 22, 2018
84fd723
Subclass FireActivity from AppCompatActivity to avoid dagger binding
CDRussell Aug 22, 2018
031623b
Re-center animation vertically
CDRussell Aug 22, 2018
828da0a
Update bitrise.yml with latest config using VDT
CDRussell Aug 23, 2018
c3bcb03
Update to latest AGP plugin
CDRussell Aug 24, 2018
1ba8655
Ensure FireActivity doesn't re-spawn Browser if user navigates away
CDRussell Aug 24, 2018
867b1cf
Code tidy
CDRussell Aug 24, 2018
73f5db8
Tidy up manifest and strings file to remove redundant label
CDRussell Aug 24, 2018
89228b2
Code formatting
CDRussell Aug 24, 2018
21885df
Code formatting
CDRussell Aug 24, 2018
e214c52
Remove unused styles and colours
CDRussell Aug 24, 2018
b674f8b
Merge branch 'develop' into feature/fire_button_visited_sites_fix
CDRussell Aug 24, 2018
2cd603f
Clear data pixel fired when app restarted; avoid being killed mid-flight
CDRussell Aug 24, 2018
66de385
Rename method to `triggerRestart`
CDRussell Aug 24, 2018
384f309
Code format
CDRussell Aug 24, 2018
0715b45
Removed redundant adapter class as one is already provided
CDRussell Aug 24, 2018
873826f
Merge branch 'develop' into feature/fire_button_visited_sites_fix
CDRussell Aug 24, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ android {
}

ext {
supportLibrary = "27.1.1"
supportLibrary = "28.0.0-rc01"
architectureComponents = "1.1.1"
architectureComponentsExtensions = "1.1.1"
androidKtx = "0.3"
Expand All @@ -91,6 +91,7 @@ ext {


dependencies {
implementation "com.android.support:support-v4:$supportLibrary"
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2018 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.app.fire

import android.arch.core.executor.testing.InstantTaskExecutorRule
import android.support.test.annotation.UiThreadTest
import com.duckduckgo.app.InstantSchedulersRule
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test


class FireViewModelTest {

@Rule
@JvmField
val schedulers = InstantSchedulersRule()

@Rule
@JvmField
var instantTaskExecutorRule = InstantTaskExecutorRule()

private lateinit var viewState: FireViewModel.ViewState
private lateinit var testee: FireViewModel

@UiThreadTest
@Before
fun setup() {
testee = FireViewModel()
testee.viewState.observeForever { viewState = it!! }
}

@Test
fun whenViewModelInitialisedThenAutoStartEnabled() {
assertTrue(viewState.autoStart)
}

@Test
fun whenViewModelInitialisedThenAnimationEnabled() {
assertTrue(viewState.animate)
}

@Test
fun whenTimerReachedThenAnimationDisabled() {
testee.startDeathClock()
assertFalse(viewState.animate)
}

@Test
fun whenUserLeftActivityThenAutoStartDisabled() {
testee.onViewStopped()
assertFalse(viewState.autoStart)
}

@Test
fun whenUserLeftActivityThenReturnedThenAutoStartEnabled() {
testee.onViewStopped()
assertFalse(viewState.autoStart)

testee.onViewRestarted()
assertTrue(viewState.autoStart)
}
}
11 changes: 7 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">

<meta-data
android:name="android.webkit.WebView.MetricsOptOut"
android:value="true" />
Expand All @@ -38,12 +37,11 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

</activity>
<activity
android:name="com.duckduckgo.app.onboarding.ui.OnboardingActivity"
android:theme="@style/AppTheme.TransparentTheme"
android:label="@string/appName" />
android:label="@string/appName"
android:theme="@style/AppTheme.TransparentTheme" />
<activity
android:name=".BrowserActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
Expand Down Expand Up @@ -130,6 +128,11 @@
<activity
android:name=".defaultBrowsing.DefaultBrowserInfoActivity"
android:theme="@style/ModalCardTheme" />

<activity
android:name="com.duckduckgo.app.fire.FireActivity"
android:process="@string/fireProcessName"
android:theme="@style/SplashTheme" />
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class BookmarksActivity : DuckDuckGoActivity() {
}
}

class BookmarksViewHolder(itemView: View?, private val viewModel: BookmarksViewModel) :
class BookmarksViewHolder(itemView: View, private val viewModel: BookmarksViewModel) :
ViewHolder(itemView) {

lateinit var bookmark: BookmarkEntity
Expand Down
20 changes: 15 additions & 5 deletions app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.duckduckgo.app.global.view.FireDialog
import com.duckduckgo.app.privacy.ui.PrivacyDashboardActivity
import com.duckduckgo.app.settings.SettingsActivity
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelName.FORGET_ALL_EXECUTED
import com.duckduckgo.app.tabs.model.TabEntity
import com.duckduckgo.app.tabs.ui.TabSwitcherActivity
import org.jetbrains.anko.longToast
Expand Down Expand Up @@ -79,10 +80,11 @@ class BrowserActivity : DuckDuckGoActivity() {
private fun openNewTab(tabId: String, url: String? = null) {
val fragment = BrowserTabFragment.newInstance(tabId, url)
val transaction = supportFragmentManager.beginTransaction()
if (currentTab == null) {
val tab = currentTab
if (tab == null) {
transaction.replace(R.id.fragmentContainer, fragment, tabId)
} else {
transaction.hide(currentTab)
transaction.hide(tab)
transaction.add(R.id.fragmentContainer, fragment, tabId)
}
transaction.commit()
Expand Down Expand Up @@ -120,14 +122,20 @@ class BrowserActivity : DuckDuckGoActivity() {
return
}

if (intent.getBooleanExtra(PERFORM_FIRE_ON_ENTRY_EXTRA, false) ) {
if (intent.getBooleanExtra(PERFORM_FIRE_ON_ENTRY_EXTRA, false)) {
viewModel.onClearRequested()
clearPersonalDataAction.clear { viewModel.onClearComplete() }
clearPersonalDataAction.clear()
Toast.makeText(applicationContext, R.string.fireDataCleared, Toast.LENGTH_LONG).show()
finish()
return
}

if (intent.getBooleanExtra(LAUNCHED_FROM_FIRE_EXTRA, false)) {
Timber.i("Launched from fire")
Toast.makeText(applicationContext, R.string.fireDataCleared, Toast.LENGTH_LONG).show()
pixel.fire(FORGET_ALL_EXECUTED)
}

if (launchNewSearch(intent)) {
viewModel.onNewTabRequested()
return
Expand Down Expand Up @@ -232,15 +240,17 @@ class BrowserActivity : DuckDuckGoActivity() {

companion object {

fun intent(context: Context, queryExtra: String? = null, newSearch: Boolean = false): Intent {
fun intent(context: Context, queryExtra: String? = null, newSearch: Boolean = false, launchedFromFireAction: Boolean = false): Intent {
val intent = Intent(context, BrowserActivity::class.java)
intent.putExtra(EXTRA_TEXT, queryExtra)
intent.putExtra(NEW_SEARCH_EXTRA, newSearch)
intent.putExtra(LAUNCHED_FROM_FIRE_EXTRA, launchedFromFireAction)
return intent
}

const val NEW_SEARCH_EXTRA = "NEW_SEARCH_EXTRA"
const val PERFORM_FIRE_ON_ENTRY_EXTRA = "PERFORM_FIRE_ON_ENTRY_EXTRA"
const val LAUNCHED_FROM_FIRE_EXTRA = "LAUNCHED_FROM_FIRE_EXTRA"
private const val DASHBOARD_REQUEST_CODE = 100
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,8 +469,8 @@ class BrowserTabFragment : Fragment(), FindListener {
private fun configureToolbar() {
toolbar.inflateMenu(R.menu.menu_browser_activity)

toolbar.setOnMenuItemClickListener {
when (it.itemId) {
toolbar.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.fire -> {
browserActivity?.launchFire()
return@setOnMenuItemClickListener true
Expand Down Expand Up @@ -714,7 +714,7 @@ class BrowserTabFragment : Fragment(), FindListener {
}

private fun resetTabState() {
omnibarTextInput.text.clear()
omnibarTextInput.text?.clear()
viewModel.resetView()
destroyWebView()
configureWebView()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.duckduckgo.app.browser.BrowserActivity
import com.duckduckgo.app.browser.BrowserTabFragment
import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserInfoActivity
import com.duckduckgo.app.feedback.ui.FeedbackActivity
import com.duckduckgo.app.fire.FireActivity
import com.duckduckgo.app.job.AppConfigurationJobService
import com.duckduckgo.app.launch.LaunchActivity
import com.duckduckgo.app.onboarding.ui.OnboardingActivity
Expand Down Expand Up @@ -87,6 +88,10 @@ abstract class AndroidBindingModule {
@ContributesAndroidInjector
abstract fun defaultBrowserInfoActivity(): DefaultBrowserInfoActivity

@ActivityScoped
@ContributesAndroidInjector
abstract fun fireActivity(): FireActivity

/* Fragments */

@ContributesAndroidInjector
Expand Down
151 changes: 151 additions & 0 deletions app/src/main/java/com/duckduckgo/app/fire/FireActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright (c) 2018 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.app.fire

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.app.Activity
import android.app.ActivityManager
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Process
import android.support.v4.app.ActivityOptionsCompat
import com.duckduckgo.app.browser.BrowserActivity
import com.duckduckgo.app.browser.R
import com.duckduckgo.app.global.DuckDuckGoActivity
import com.duckduckgo.app.global.ViewModelFactory
import kotlinx.android.synthetic.main.activity_fire.*
import javax.inject.Inject

/**
* Activity which is responsible for killing the main process and restarting it. This Activity will automatically finish itself after a brief time.
*
* This Activity runs in a separate process so that it can seamlessly restart the main app process without much in the way of a jarring UX.
*
* The correct way to invoke this Activity is through its `triggerRestart(context)` method.
*
* This Activity was largely inspired by https://github.com/JakeWharton/ProcessPhoenix
*
* We need to detect the user leaving this activity and possibly returning to it:
* if the user left our app to do something else, restarting our browser activity would feel wrong
* if the user left our app but came back, we should restart the browser activity
*/
class FireActivity : DuckDuckGoActivity() {

@Inject
lateinit var viewModelFactory: ViewModelFactory

private val viewModel: FireViewModel by lazy {
ViewModelProviders.of(this, viewModelFactory).get(FireViewModel::class.java)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_fire)
if (savedInstanceState == null) {

fireAnimationView.addAnimatorListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animator: Animator?) {
viewModel.startDeathClock()
}
})
}

viewModel.viewState.observe(this, Observer<FireViewModel.ViewState> {
it?.let { viewState ->
if (!viewState.animate) {

// restart the app only if the user hasn't navigated away during the fire animation
if (viewState.autoStart) {
val intent = intent.getParcelableExtra<Intent>(KEY_RESTART_INTENTS)
startActivity(intent, activityFadeOptions(this))
}

viewModel.viewState.removeObservers(this)
finish()
killProcess()
}
}
})
}

override fun onStop() {
super.onStop()

if (!isChangingConfigurations) {
viewModel.onViewStopped()
}
}

override fun onRestart() {
super.onRestart()
viewModel.onViewRestarted()
}

override fun onBackPressed() {
// do nothing - the activity will kill itself soon enough
}

companion object {
private const val KEY_RESTART_INTENTS = "KEY_RESTART_INTENTS"

fun triggerRestart(context: Context) {
triggerRestart(context, getRestartIntent(context))
}

private fun triggerRestart(context: Context, nextIntent: Intent) {
val intent = Intent(context, FireActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.putExtra(KEY_RESTART_INTENTS, nextIntent)

context.startActivity(intent, activityFadeOptions(context))
if (context is Activity) {
context.finish()
}
killProcess()
}

private fun getRestartIntent(context: Context): Intent {
val intent = BrowserActivity.intent(context, launchedFromFireAction = true)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
return intent
}

private fun killProcess() {
Runtime.getRuntime().exit(0)
}

fun appRestarting(context: Context): Boolean {
val currentProcessId = Process.myPid()
val activityManager: ActivityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.runningAppProcesses?.forEach {
if (it.pid == currentProcessId && it.processName.endsWith(context.getString(R.string.fireProcessName))) {
return true
}
}
return false
}

private fun activityFadeOptions(context: Context): Bundle? {
val config = ActivityOptionsCompat.makeCustomAnimation(context, android.R.anim.fade_in, android.R.anim.fade_out)
return config.toBundle()
}
}
}
Loading