diff --git a/app/src/androidTest/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModelTest.kt index d0a8221a71d1..2e1cd32c9033 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModelTest.kt @@ -117,4 +117,10 @@ class BookmarksViewModelTest { testee.delete(bookmark) verify(faviconManager).deletePersistedFavicon(bookmark.url) } + + @Test + fun whenBookmarkInsertedThenDaoUpdated() { + testee.insert(bookmark) + verify(bookmarksDao).insert(bookmark) + } } diff --git a/app/src/androidTest/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesViewModelTest.kt index 9590ba085bb6..d18940899a72 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesViewModelTest.kt @@ -167,6 +167,17 @@ class FireproofWebsitesViewModelTest { verify(settingsDataStore).appLoginDetection = true } + @Test + fun whenUserUndosDeleteFireproofThenSiteIsAddedBack() { + + val entity = FireproofWebsiteEntity("domain.com") + + viewModel.onSnackBarUndoFireproof(entity) + + verify(mockViewStateObserver, atLeastOnce()).onChanged(viewStateCaptor.capture()) + assertTrue(viewStateCaptor.value.fireproofWebsitesEntities.isNotEmpty()) + } + private inline fun assertCommandIssued(instanceAssertions: T.() -> Unit = {}) { verify(mockCommandObserver, atLeastOnce()).onChanged(commandCaptor.capture()) val issuedCommand = commandCaptor.allValues.find { it is T } diff --git a/app/src/main/java/com/duckduckgo/app/bookmarks/ui/BookmarksActivity.kt b/app/src/main/java/com/duckduckgo/app/bookmarks/ui/BookmarksActivity.kt index eeda44cac6df..81e274186dcc 100644 --- a/app/src/main/java/com/duckduckgo/app/bookmarks/ui/BookmarksActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/bookmarks/ui/BookmarksActivity.kt @@ -43,6 +43,8 @@ import com.duckduckgo.app.global.baseHost import com.duckduckgo.app.global.view.gone import com.duckduckgo.app.global.view.html import com.duckduckgo.app.global.view.show +import com.google.android.material.snackbar.Snackbar +import kotlinx.android.synthetic.main.activity_bookmarks.* import kotlinx.android.synthetic.main.content_bookmarks.emptyBookmarks import kotlinx.android.synthetic.main.content_bookmarks.recycler import kotlinx.android.synthetic.main.include_toolbar.toolbar @@ -138,18 +140,16 @@ class BookmarksActivity : DuckDuckGoActivity() { } private fun confirmDeleteBookmark(bookmark: BookmarkEntity) { - val message = getString(R.string.bookmarkDeleteConfirmMessage, bookmark.title).html(this) - val title = getString(R.string.dialogConfirmTitle) - deleteDialog = AlertDialog.Builder(this) - .setTitle(title) - .setMessage(message) - .setPositiveButton(android.R.string.yes) { _, _ -> - delete(bookmark) - } - .setNegativeButton(android.R.string.no) { dialog, _ -> - dialog.dismiss() - } - .show() + val message = getString(R.string.bookmarkDeleteConfirmationMessage, bookmark.title).html(this) + viewModel.delete(bookmark) + Snackbar.make( + bookmarkRootView, + message, + Snackbar.LENGTH_LONG + ).setAction(R.string.fireproofWebsiteSnackbarAction) { + viewModel.insert(bookmark) + }.show() + } private fun delete(bookmark: BookmarkEntity) { diff --git a/app/src/main/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModel.kt b/app/src/main/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModel.kt index 6809afd15c90..b550a3f36744 100644 --- a/app/src/main/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModel.kt @@ -105,4 +105,10 @@ class BookmarksViewModel( } } + fun insert(bookmark: BookmarkEntity) { + viewModelScope.launch(dispatcherProvider.io()) { + dao.insert(BookmarkEntity(title = bookmark.title, url = bookmark.url)) + } + } + } diff --git a/app/src/main/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesActivity.kt b/app/src/main/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesActivity.kt index 4a1605fb9435..9ec7bd13cace 100644 --- a/app/src/main/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesActivity.kt @@ -20,12 +20,15 @@ import android.app.AlertDialog import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.core.text.HtmlCompat import androidx.lifecycle.Observer import com.duckduckgo.app.browser.R import com.duckduckgo.app.browser.favicon.FaviconManager import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteEntity +import com.duckduckgo.app.fire.fireproofwebsite.data.website import com.duckduckgo.app.global.DuckDuckGoActivity -import com.duckduckgo.app.global.view.html +import com.google.android.material.snackbar.Snackbar +import kotlinx.android.synthetic.main.activity_fireproof_websites.* import kotlinx.android.synthetic.main.content_fireproof_websites.* import kotlinx.android.synthetic.main.include_toolbar.* import javax.inject.Inject @@ -76,15 +79,15 @@ class FireproofWebsitesActivity : DuckDuckGoActivity() { @Suppress("deprecation") private fun confirmDeleteWebsite(entity: FireproofWebsiteEntity) { - val message = getString(R.string.fireproofWebsiteDeleteConfirmMessage, entity.domain).html(this) - val title = getString(R.string.dialogConfirmTitle) - deleteDialog = AlertDialog.Builder(this) - .setTitle(title) - .setMessage(message) - .setPositiveButton(android.R.string.yes) { _, _ -> viewModel.delete(entity) } - .setNegativeButton(android.R.string.no) { _, _ -> } - .create() - deleteDialog?.show() + val message = HtmlCompat.fromHtml(getString(R.string.fireproofWebsiteRemovalConfirmation, entity.website()), HtmlCompat.FROM_HTML_MODE_LEGACY) + viewModel.delete(entity) + Snackbar.make( + fireActivityRoot, + message, + Snackbar.LENGTH_LONG + ).setAction(R.string.fireproofWebsiteSnackbarAction) { + viewModel.onSnackBarUndoFireproof(entity) + }.show() } override fun onDestroy() { diff --git a/app/src/main/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesViewModel.kt b/app/src/main/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesViewModel.kt index 02f6f4f486a0..b4878f8d1924 100644 --- a/app/src/main/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/fire/fireproofwebsite/ui/FireproofWebsitesViewModel.kt @@ -73,6 +73,13 @@ class FireproofWebsitesViewModel( command.value = ConfirmDeleteFireproofWebsite(entity) } + fun onSnackBarUndoFireproof(entity: FireproofWebsiteEntity) { + val domain = entity.domain + viewModelScope.launch(dispatcherProvider.io()) { + fireproofWebsiteRepository.fireproofWebsite(domain) + } + } + fun delete(entity: FireproofWebsiteEntity) { viewModelScope.launch(dispatcherProvider.io()) { fireproofWebsiteRepository.removeFireproofWebsite(entity) diff --git a/app/src/main/res/layout/activity_bookmarks.xml b/app/src/main/res/layout/activity_bookmarks.xml index 641398049a86..ccac711ec33e 100644 --- a/app/src/main/res/layout/activity_bookmarks.xml +++ b/app/src/main/res/layout/activity_bookmarks.xml @@ -17,6 +17,7 @@ --> diff --git a/app/src/main/res/values/string-untranslated.xml b/app/src/main/res/values/string-untranslated.xml index 80260c9e6c33..fd6de98d180b 100644 --- a/app/src/main/res/values/string-untranslated.xml +++ b/app/src/main/res/values/string-untranslated.xml @@ -25,4 +25,9 @@ Success! %s has been added to your home screen. Checking your feed in DuckDuckGo is a great alternative to using the Facebook app!<br/><br/>But if the Facebook app is on your phone, it can make requests for data even when you\'re not using it.<br/><br/>Prevent this by deleting it now! + + Fireproofing removed for <b>%s</b> + + + Deleted <b>%s</b> diff --git a/versions.properties b/versions.properties new file mode 100644 index 000000000000..5406f4d1d2af --- /dev/null +++ b/versions.properties @@ -0,0 +1,41 @@ +## suppress inspection "SpellCheckingInspection" for whole file +## suppress inspection "UnusedProperty" for whole file +## +## Dependencies and Plugin versions with their available updates +## Generated by $ ./gradlew refreshVersions +## Please, don't put extra comments in that file yet, keeping them is not supported yet. + +version.androidx.appcompat=1.2.0 +version.androidx.arch.core=2.1.0 +version.androidx.constraintlayout=2.0.4 +version.androidx.core=1.3.2 +version.androidx.fragment=1.2.5 +version.androidx.legacy=1.0.0 +version.androidx.lifecycle=2.2.0 +version.androidx.room=2.2.5 +version.androidx.swiperefreshlayout=1.1.0 +version.androidx.test=1.3.0 +version.androidx.webkit=1.3.0 +version.androidx.work=2.4.0 +version.com.airbnb.android..lottie=3.4.0 +version.com.android.installreferrer..installreferrer=1.1.2 +version.com.github.bumptech.glide..compiler=4.11.0 +version.com.github.bumptech.glide..glide=4.11.0 +version.com.github.bumptech.glide..okhttp3-integration=4.11.0 +version.com.jakewharton.retrofit..retrofit2-kotlin-coroutines-adapter=0.9.2 +version.com.jakewharton.rxrelay2..rxrelay=2.0.0 +version.com.jakewharton.timber..timber=4.7.1 +version.com.nhaarman.mockitokotlin2..mockito-kotlin=2.1.0 +version.google.android.material=1.2.1 +version.google.dagger=2.27 +version.io.reactivex.rxjava2..rxandroid=2.0.2 +version.io.reactivex.rxjava2..rxjava=2.1.10 +version.kotlin=1.4.10 +version.kotlinx.coroutines=1.3.5 +version.leakcanary=2.5 +version.okhttp3=4.9.0 +version.org.apache.commons..commons-math3=3.6.1 +version.org.jetbrains.anko..anko-commons=0.10.4 +version.org.jetbrains.anko..anko-design=0.10.4 +version.org.mockito..mockito-android=3.4.6 +version.retrofit2=2.8.1 \ No newline at end of file