From bb6d6d300b67759febb7d238774bd0b99fd59b47 Mon Sep 17 00:00:00 2001 From: Craig Russell Date: Fri, 15 Jun 2018 14:56:48 +0100 Subject: [PATCH 01/31] Add permission required to create home screen shortcuts --- app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index daabbe636b48..d490f227f8a9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + Date: Fri, 15 Jun 2018 15:00:42 +0100 Subject: [PATCH 02/31] Add class to allow downloading favicons --- .../com/duckduckgo/app/di/TestAppComponent.kt | 1 + .../app/browser/favicon/FaviconDownloader.kt | 54 +++++++++++++++++++ .../com/duckduckgo/app/di/AppComponent.kt | 1 + .../com/duckduckgo/app/di/FaviconModule.kt | 31 +++++++++++ 4 files changed, 87 insertions(+) create mode 100644 app/src/main/java/com/duckduckgo/app/browser/favicon/FaviconDownloader.kt create mode 100644 app/src/main/java/com/duckduckgo/app/di/FaviconModule.kt diff --git a/app/src/androidTest/java/com/duckduckgo/app/di/TestAppComponent.kt b/app/src/androidTest/java/com/duckduckgo/app/di/TestAppComponent.kt index d0c7df8cb87b..59e1e06a199e 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/di/TestAppComponent.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/di/TestAppComponent.kt @@ -56,6 +56,7 @@ import javax.inject.Singleton NotificationModule::class, DefaultBrowserModule::class, OnboardingModule::class, + FaviconModule::class, VariantModule::class ]) interface TestAppComponent : AndroidInjector { diff --git a/app/src/main/java/com/duckduckgo/app/browser/favicon/FaviconDownloader.kt b/app/src/main/java/com/duckduckgo/app/browser/favicon/FaviconDownloader.kt new file mode 100644 index 000000000000..369bb0859f05 --- /dev/null +++ b/app/src/main/java/com/duckduckgo/app/browser/favicon/FaviconDownloader.kt @@ -0,0 +1,54 @@ +/* + * 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.browser.favicon + +import android.content.Context +import android.graphics.Bitmap +import android.net.Uri +import com.bumptech.glide.Glide +import com.duckduckgo.app.global.view.toPx +import io.reactivex.Single +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +interface FaviconDownloader { + + fun download(url: Uri): Single +} + +class GlideFaviconDownloader @Inject constructor(private val context: Context) : FaviconDownloader { + + override fun download(url: Uri): Single { + + return Single.fromCallable { + + val desiredImageSizePx = DESIRED_IMAGE_SIZE_DP.toPx() + + Glide.with(context) + .asBitmap() + .load(url) + .submit(desiredImageSizePx, desiredImageSizePx) + .get(TIMEOUT_PERIOD_SECONDS, TimeUnit.SECONDS) + + } + } + + companion object { + private const val DESIRED_IMAGE_SIZE_DP = 24 + private const val TIMEOUT_PERIOD_SECONDS: Long = 3 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duckduckgo/app/di/AppComponent.kt b/app/src/main/java/com/duckduckgo/app/di/AppComponent.kt index 230e672f4e13..10829ced7217 100644 --- a/app/src/main/java/com/duckduckgo/app/di/AppComponent.kt +++ b/app/src/main/java/com/duckduckgo/app/di/AppComponent.kt @@ -53,6 +53,7 @@ import javax.inject.Singleton NotificationModule::class, DefaultBrowserModule::class, OnboardingModule::class, + FaviconModule::class, VariantModule::class ]) interface AppComponent : AndroidInjector { diff --git a/app/src/main/java/com/duckduckgo/app/di/FaviconModule.kt b/app/src/main/java/com/duckduckgo/app/di/FaviconModule.kt new file mode 100644 index 000000000000..fe88b8699372 --- /dev/null +++ b/app/src/main/java/com/duckduckgo/app/di/FaviconModule.kt @@ -0,0 +1,31 @@ +/* + * 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.di + +import android.content.Context +import com.duckduckgo.app.browser.favicon.FaviconDownloader +import com.duckduckgo.app.browser.favicon.GlideFaviconDownloader +import dagger.Module +import dagger.Provides + + +@Module +class FaviconModule { + + @Provides + fun faviconDownload(context: Context) : FaviconDownloader = GlideFaviconDownloader(context) +} \ No newline at end of file From 867896ca37efb0f715219b87a3f00d983f86c81e Mon Sep 17 00:00:00 2001 From: Craig Russell Date: Fri, 15 Jun 2018 16:18:42 +0100 Subject: [PATCH 03/31] Add "add to home" functionality as a first pass. --- .../app/browser/BrowserTabViewModelTest.kt | 5 ++ .../app/browser/BrowserTabFragment.kt | 51 +++++++++++++++++-- .../app/browser/BrowserTabViewModel.kt | 26 ++++++++++ .../duckduckgo/app/global/ViewModelFactory.kt | 5 +- .../res/layout/popup_window_browser_menu.xml | 5 ++ app/src/main/res/values/strings.xml | 3 ++ 6 files changed, 91 insertions(+), 4 deletions(-) diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt index 9df2d57d8095..1a05947ade19 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt @@ -33,6 +33,7 @@ import com.duckduckgo.app.browser.LongPressHandler.RequiredAction.DownloadFile import com.duckduckgo.app.browser.LongPressHandler.RequiredAction.OpenInNewTab import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserDetector import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserNotification +import com.duckduckgo.app.browser.favicon.FaviconDownloader import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter import com.duckduckgo.app.global.db.AppConfigurationDao import com.duckduckgo.app.global.db.AppConfigurationEntity @@ -98,6 +99,9 @@ class BrowserTabViewModelTest { @Mock private lateinit var mockLongPressHandler: LongPressHandler + @Mock + private lateinit var mockFaviconDownloader: FaviconDownloader + @Mock private lateinit var mockOmnibarConverter: OmnibarEntryConverter @@ -143,6 +147,7 @@ class BrowserTabViewModelTest { defaultBrowserNotification = mockDefaultBrowserNotification, defaultBrowserDetector = mockDefaultBrowserDetector, longPressHandler = mockLongPressHandler, + faviconDownloader = mockFaviconDownloader, appConfigurationDao = appConfigurationDao ) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 22579d51a649..eff06ae649f6 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -36,6 +36,9 @@ import android.support.annotation.StringRes import android.support.design.widget.Snackbar import android.support.v4.app.Fragment import android.support.v4.content.ContextCompat +import android.support.v4.content.pm.ShortcutInfoCompat +import android.support.v4.content.pm.ShortcutManagerCompat +import android.support.v4.graphics.drawable.IconCompat import android.support.v7.widget.LinearLayoutManager import android.text.Editable import android.view.* @@ -48,6 +51,7 @@ import android.webkit.WebView import android.webkit.WebView.FindListener import android.widget.EditText import android.widget.TextView +import android.widget.Toast import androidx.core.view.isVisible import androidx.core.view.postDelayed import com.duckduckgo.app.bookmarks.ui.SaveBookmarkDialogFragment @@ -77,6 +81,7 @@ import org.jetbrains.anko.longToast import org.jetbrains.anko.share import timber.log.Timber import java.io.File +import java.util.* import javax.inject.Inject import kotlin.concurrent.thread @@ -196,13 +201,25 @@ class BrowserTabFragment : Fragment(), FindListener { onMenuItemClicked(view.addBookmarksPopupMenuItem) { addBookmark() } onMenuItemClicked(view.settingsPopupMenuItem) { browserActivity?.launchSettings() } onMenuItemClicked(view.findInPageMenuItem) { viewModel.userRequestingToFindInPage() } + onMenuItemClicked(view.addToHome) { + context?.let { + if (!ShortcutManagerCompat.isRequestPinShortcutSupported(it)) { + Toast.makeText(it, "Pinning not supported", Toast.LENGTH_SHORT).show() + return@let + } + + viewModel.addToHomeScreen(omnibarTextInput.text.toString()) + } + } onMenuItemClicked(view.requestDesktopSiteCheckMenuItem) { viewModel.desktopSiteModeToggled( - urlString = webView?.url, - desktopSiteRequested = view.requestDesktopSiteCheckMenuItem.isChecked + urlString = webView?.url, + desktopSiteRequested = view.requestDesktopSiteCheckMenuItem.isChecked ) } - onMenuItemClicked(view.sharePageMenuItem) { viewModel.userSharingLink(webView?.url) } + onMenuItemClicked(view.sharePageMenuItem) { + viewModel.userSharingLink(webView?.url) + } } } @@ -285,6 +302,11 @@ class BrowserTabFragment : Fragment(), FindListener { launchFilePicker(it) } is Command.LaunchDefaultAppSystemSettings -> { launchDefaultAppSystemSettings() } + is Command.AddHomeShortcut -> { + context?.let { context -> + addHomeShortcut(it, context) + } + } } } @@ -392,6 +414,7 @@ class BrowserTabFragment : Fragment(), FindListener { popupMenu.contentView.newTabPopupMenuItem.isEnabled = viewState.browserShowing popupMenu.contentView.addBookmarksPopupMenuItem?.isEnabled = viewState.canAddBookmarks popupMenu.contentView.sharePageMenuItem?.isEnabled = viewState.canSharePage + popupMenu.contentView.addToHome.isEnabled = true } private fun renderFindInPageState(viewState: FindInPage) { @@ -636,6 +659,28 @@ class BrowserTabFragment : Fragment(), FindListener { addBookmarkDialog.listener = viewModel } + private fun addHomeShortcut(homeShortcut: Command.AddHomeShortcut, context: Context) { + val intent = Intent(context, BrowserActivity::class.java) + intent.action = Intent.ACTION_VIEW + intent.putExtra(Intent.EXTRA_TEXT, homeShortcut.title) + + val icon = + if (homeShortcut.icon != null) { + IconCompat.createWithBitmap(homeShortcut.icon) + } else { + IconCompat.createWithResource(context, R.drawable.ic_icon_smiley_face) + } + + val shortcutInfo = ShortcutInfoCompat.Builder(context, UUID.randomUUID().toString()) + .setShortLabel(homeShortcut.title) + .setIntent(intent) + .setIcon(icon) + .build() + + ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null) + + } + override fun onFindResultReceived(activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Boolean) { viewModel.onFindResultsReceived(activeMatchOrdinal, numberOfMatches) } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index 9e50acba351b..ed9a0a919fa8 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -20,6 +20,7 @@ import android.arch.lifecycle.LiveData import android.arch.lifecycle.MutableLiveData import android.arch.lifecycle.Observer import android.arch.lifecycle.ViewModel +import android.graphics.Bitmap import android.net.Uri import android.support.annotation.AnyThread import android.support.annotation.StringRes @@ -40,10 +41,12 @@ import com.duckduckgo.app.browser.BrowserTabViewModel.Command.* import com.duckduckgo.app.browser.LongPressHandler.RequiredAction import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserDetector import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserNotification +import com.duckduckgo.app.browser.favicon.FaviconDownloader import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter import com.duckduckgo.app.global.SingleLiveEvent import com.duckduckgo.app.global.db.AppConfigurationDao import com.duckduckgo.app.global.db.AppConfigurationEntity +import com.duckduckgo.app.global.faviconLocation import com.duckduckgo.app.global.isMobileSite import com.duckduckgo.app.global.model.Site import com.duckduckgo.app.global.model.SiteFactory @@ -67,6 +70,7 @@ import java.util.concurrent.TimeUnit class BrowserTabViewModel( private val statisticsUpdater: StatisticsUpdater, private val queryUrlConverter: OmnibarEntryConverter, + private val faviconDownloader: FaviconDownloader, private val duckDuckGoUrlDetector: DuckDuckGoUrlDetector, private val siteFactory: SiteFactory, private val tabRepository: TabRepository, @@ -118,6 +122,7 @@ class BrowserTabViewModel( object DismissFindInPage : Command() class ShowFileChooser(val filePathCallback: ValueCallback>, val fileChooserParams: WebChromeClient.FileChooserParams) : Command() object LaunchDefaultAppSystemSettings : Command() + class AddHomeShortcut(val title: String, val icon: Bitmap? = null) : Command() } val viewState: MutableLiveData = MutableLiveData() @@ -468,6 +473,27 @@ class BrowserTabViewModel( } } + fun addToHomeScreen(query: String) { + + val faviconUrl = query.toUri().faviconLocation() + if (faviconUrl == null) { + command.value = AddHomeShortcut(query) + return + } + + faviconDownloader.download(faviconUrl) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + Timber.i("Successfully got favicon") + command.value = AddHomeShortcut(title = query, icon = it) + }, { throwable -> + Timber.w(throwable, "Failed to obtain favicon") + command.value = AddHomeShortcut(query) + }) + + } + fun resetView() { site = null onSiteChanged() diff --git a/app/src/main/java/com/duckduckgo/app/global/ViewModelFactory.kt b/app/src/main/java/com/duckduckgo/app/global/ViewModelFactory.kt index d3129e746347..504daf8904bf 100644 --- a/app/src/main/java/com/duckduckgo/app/global/ViewModelFactory.kt +++ b/app/src/main/java/com/duckduckgo/app/global/ViewModelFactory.kt @@ -27,6 +27,7 @@ import com.duckduckgo.app.browser.DuckDuckGoUrlDetector import com.duckduckgo.app.browser.LongPressHandler import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserDetector import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserNotification +import com.duckduckgo.app.browser.favicon.FaviconDownloader import com.duckduckgo.app.browser.omnibar.QueryUrlConverter import com.duckduckgo.app.global.db.AppConfigurationDao import com.duckduckgo.app.global.model.SiteFactory @@ -65,6 +66,7 @@ class ViewModelFactory @Inject constructor( private val defaultBrowserNotification: DefaultBrowserNotification, private val webViewLongPressHandler: LongPressHandler, private val defaultBrowserDetector: DefaultBrowserDetector, + private val faviconDownloader: FaviconDownloader, private val variantManager: VariantManager ) : ViewModelProvider.NewInstanceFactory() { @@ -100,6 +102,7 @@ class ViewModelFactory @Inject constructor( defaultBrowserNotification = defaultBrowserNotification, appConfigurationDao = appConfigurationDao, longPressHandler = webViewLongPressHandler, - autoCompleteApi = autoCompleteApi + autoCompleteApi = autoCompleteApi, + faviconDownloader = faviconDownloader ) } diff --git a/app/src/main/res/layout/popup_window_browser_menu.xml b/app/src/main/res/layout/popup_window_browser_menu.xml index 1afa8c4cb7e4..2f999eae400e 100644 --- a/app/src/main/res/layout/popup_window_browser_menu.xml +++ b/app/src/main/res/layout/popup_window_browser_menu.xml @@ -103,6 +103,11 @@ style="@style/BrowserTextMenuItem" android:text="@string/find_in_page" /> + + Find in page %d/%d + + Add to Home + Edit query before searching Autocomplete Suggestions From 19bbeab1574179c4cdadab9d4ab1b40a27b56de7 Mon Sep 17 00:00:00 2001 From: Craig Russell Date: Fri, 15 Jun 2018 17:46:24 +0100 Subject: [PATCH 04/31] Choose better shortcut title; appropriately enable/disable menu item --- .../app/browser/BrowserTabFragment.kt | 4 +-- .../app/browser/BrowserTabViewModel.kt | 28 +++++++++++-------- .../app/browser/favicon/FaviconDownloader.kt | 8 ++++-- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index eff06ae649f6..28cb472c2cf0 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -414,7 +414,7 @@ class BrowserTabFragment : Fragment(), FindListener { popupMenu.contentView.newTabPopupMenuItem.isEnabled = viewState.browserShowing popupMenu.contentView.addBookmarksPopupMenuItem?.isEnabled = viewState.canAddBookmarks popupMenu.contentView.sharePageMenuItem?.isEnabled = viewState.canSharePage - popupMenu.contentView.addToHome.isEnabled = true + popupMenu.contentView.addToHome.isEnabled = viewState.canAddToHome } private fun renderFindInPageState(viewState: FindInPage) { @@ -662,7 +662,7 @@ class BrowserTabFragment : Fragment(), FindListener { private fun addHomeShortcut(homeShortcut: Command.AddHomeShortcut, context: Context) { val intent = Intent(context, BrowserActivity::class.java) intent.action = Intent.ACTION_VIEW - intent.putExtra(Intent.EXTRA_TEXT, homeShortcut.title) + intent.putExtra(Intent.EXTRA_TEXT, homeShortcut.url) val icon = if (homeShortcut.icon != null) { diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index ed9a0a919fa8..14781afd9c62 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -44,9 +44,9 @@ import com.duckduckgo.app.browser.defaultBrowsing.DefaultBrowserNotification import com.duckduckgo.app.browser.favicon.FaviconDownloader import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter import com.duckduckgo.app.global.SingleLiveEvent +import com.duckduckgo.app.global.baseHost import com.duckduckgo.app.global.db.AppConfigurationDao import com.duckduckgo.app.global.db.AppConfigurationEntity -import com.duckduckgo.app.global.faviconLocation import com.duckduckgo.app.global.isMobileSite import com.duckduckgo.app.global.model.Site import com.duckduckgo.app.global.model.SiteFactory @@ -101,7 +101,8 @@ class BrowserTabViewModel( val findInPage: FindInPage = FindInPage(canFindInPage = false), val isDesktopBrowsingMode: Boolean = false, val canSharePage: Boolean = false, - val showDefaultBrowserBanner: Boolean = false + val showDefaultBrowserBanner: Boolean = false, + val canAddToHome: Boolean = false ) sealed class Command { @@ -122,7 +123,7 @@ class BrowserTabViewModel( object DismissFindInPage : Command() class ShowFileChooser(val filePathCallback: ValueCallback>, val fileChooserParams: WebChromeClient.FileChooserParams) : Command() object LaunchDefaultAppSystemSettings : Command() - class AddHomeShortcut(val title: String, val icon: Bitmap? = null) : Command() + class AddHomeShortcut(val title: String, val url: String, val icon: Bitmap? = null) : Command() } val viewState: MutableLiveData = MutableLiveData() @@ -285,6 +286,7 @@ class BrowserTabViewModel( if (url == null) { viewState.value = viewState.value?.copy( canAddBookmarks = false, + canAddToHome = false, findInPage = FindInPage(visible = false, canFindInPage = false) ) return @@ -295,6 +297,7 @@ class BrowserTabViewModel( omnibarText = omnibarTextForUrl(url), browserShowing = true, canSharePage = true, + canAddToHome = true, showPrivacyGrade = appConfigurationDownloaded, findInPage = FindInPage(visible = false, canFindInPage = true) ) @@ -473,23 +476,24 @@ class BrowserTabViewModel( } } - fun addToHomeScreen(query: String) { + fun addToHomeScreen(currentPage: String) { - val faviconUrl = query.toUri().faviconLocation() - if (faviconUrl == null) { - command.value = AddHomeShortcut(query) - return - } + val title = + if (duckDuckGoUrlDetector.isDuckDuckGoQueryUrl(currentPage)) { + duckDuckGoUrlDetector.extractQuery(currentPage) ?: currentPage + } else { + currentPage.toUri().baseHost ?: currentPage + } - faviconDownloader.download(faviconUrl) + faviconDownloader.download(currentPage.toUri()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ Timber.i("Successfully got favicon") - command.value = AddHomeShortcut(title = query, icon = it) + command.value = AddHomeShortcut(title, currentPage, it) }, { throwable -> Timber.w(throwable, "Failed to obtain favicon") - command.value = AddHomeShortcut(query) + command.value = AddHomeShortcut(title, currentPage) }) } diff --git a/app/src/main/java/com/duckduckgo/app/browser/favicon/FaviconDownloader.kt b/app/src/main/java/com/duckduckgo/app/browser/favicon/FaviconDownloader.kt index 369bb0859f05..dcae196d3e91 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/favicon/FaviconDownloader.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/favicon/FaviconDownloader.kt @@ -20,6 +20,7 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import com.bumptech.glide.Glide +import com.duckduckgo.app.global.faviconLocation import com.duckduckgo.app.global.view.toPx import io.reactivex.Single import java.util.concurrent.TimeUnit @@ -27,20 +28,21 @@ import javax.inject.Inject interface FaviconDownloader { - fun download(url: Uri): Single + fun download(currentPageUrl: Uri): Single } class GlideFaviconDownloader @Inject constructor(private val context: Context) : FaviconDownloader { - override fun download(url: Uri): Single { + override fun download(currentPageUrl: Uri): Single { return Single.fromCallable { + val faviconUrl = currentPageUrl.faviconLocation() ?: throw IllegalArgumentException("Invalid favicon currentPageUrl") val desiredImageSizePx = DESIRED_IMAGE_SIZE_DP.toPx() Glide.with(context) .asBitmap() - .load(url) + .load(faviconUrl) .submit(desiredImageSizePx, desiredImageSizePx) .get(TIMEOUT_PERIOD_SECONDS, TimeUnit.SECONDS) From a4d0b97d837763b2f17f2bd760b6aaa5fc3a2ed7 Mon Sep 17 00:00:00 2001 From: Craig Russell Date: Tue, 3 Jul 2018 12:34:45 +0100 Subject: [PATCH 05/31] Add support for two distinct layout states; browser and blank tab These have slightly different requirements in terms of their scroll-observing behaviours. We do want the browser layout mode to respect the toolbar scrolling for instance, but we don't want the same behaviour for the new tab view. Trying to use the same `app:layout_behavior="@string/appbar_scrolling_view_behavior"` behaviour in both modes means that the new tab layout is essentially forced off screen vertically and it makes it difficult to add a button that anchors to the bottom. Splitting these into two distinct modes lets us better control both modes without trying to find one set of views and flags which works well enough for both. --- .../app/browser/BrowserTabFragment.kt | 19 ++++- .../app/browser/BrowserTabViewModel.kt | 7 ++ .../main/res/layout/fragment_browser_tab.xml | 80 ++++++++++++------- 3 files changed, 75 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 1d2bf409e5af..c6578a746852 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -219,6 +219,10 @@ class BrowserTabFragment : Fragment(), FindListener { it?.let { renderer.renderAutocomplete(it) } }) + viewModel.globalLayoutState.observe(this, Observer { + it?.let { renderer.renderGlobalViewState(it) } + }) + viewModel.browserViewState.observe(this, Observer { it?.let { renderer.renderBrowserViewState(it) } }) @@ -438,7 +442,7 @@ class BrowserTabFragment : Fragment(), FindListener { } private fun configureKeyboardAwareLogoAnimation() { - logoParent.layoutTransition.enableTransitionType(CHANGING) + newTabLayout.layoutTransition.enableTransitionType(CHANGING) } private fun userEnteredQuery(query: String) { @@ -730,6 +734,7 @@ class BrowserTabFragment : Fragment(), FindListener { private var lastSeenLoadingViewState: LoadingViewState? = null private var lastSeenFindInPageViewState: FindInPageViewState? = null private var lastSeenBrowserViewState: BrowserViewState? = null + private var lastSeenGlobalViewState: GlobalLayoutViewState? = null private var lastSeenDefaultBrowserViewState: DefaultBrowserViewState? = null private var lastSeenAutoCompleteViewState: AutoCompleteViewState? = null @@ -787,6 +792,18 @@ class BrowserTabFragment : Fragment(), FindListener { } } + fun renderGlobalViewState(viewState: GlobalLayoutViewState) { + renderIfChanged(viewState, lastSeenGlobalViewState) { + if (viewState.isNewTabState) { + newTabLayout.show() + browserLayout.hide() + } else { + newTabLayout.hide() + browserLayout.show() + } + } + } + fun renderBrowserViewState(viewState: BrowserViewState) { renderIfChanged(viewState, lastSeenBrowserViewState) { lastSeenBrowserViewState = viewState diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index 7012fd550e27..9d55d57c49cb 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -80,6 +80,10 @@ class BrowserTabViewModel( appConfigurationDao: AppConfigurationDao ) : WebViewClientListener, SaveBookmarkListener, ViewModel() { + data class GlobalLayoutViewState( + val isNewTabState: Boolean = true + ) + data class BrowserViewState( val browserShowing: Boolean = false, val isFullScreen: Boolean = false, @@ -144,6 +148,7 @@ class BrowserTabViewModel( val autoCompleteViewState: MutableLiveData = MutableLiveData() val browserViewState: MutableLiveData = MutableLiveData() + val globalLayoutState: MutableLiveData = MutableLiveData() val loadingViewState: MutableLiveData = MutableLiveData() val omnibarViewState: MutableLiveData = MutableLiveData() val defaultBrowserViewState: MutableLiveData = MutableLiveData() @@ -238,6 +243,7 @@ class BrowserTabViewModel( val trimmedInput = input.trim() url.value = queryUrlConverter.convertQueryToUrl(trimmedInput) + globalLayoutState.value = GlobalLayoutViewState(isNewTabState = false) findInPageViewState.value = FindInPageViewState(visible = false, canFindInPage = true) omnibarViewState.value = currentOmnibarViewState().copy(omnibarText = trimmedInput) browserViewState.value = currentBrowserViewState().copy(browserShowing = true, showClearButton = false) @@ -521,6 +527,7 @@ class BrowserTabViewModel( } private fun initializeViewStates() { + globalLayoutState.value = GlobalLayoutViewState() defaultBrowserViewState.value = DefaultBrowserViewState() browserViewState.value = BrowserViewState() loadingViewState.value = LoadingViewState() diff --git a/app/src/main/res/layout/fragment_browser_tab.xml b/app/src/main/res/layout/fragment_browser_tab.xml index 5c1605486863..8ce41e081c3f 100644 --- a/app/src/main/res/layout/fragment_browser_tab.xml +++ b/app/src/main/res/layout/fragment_browser_tab.xml @@ -37,8 +37,25 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + - - - - + + + + + +