diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index c3b944f2e..1a3d4d01d 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -48,4 +48,9 @@ object FeatureFlags { * Enables downloads with external download managers. */ val externalDownloadManager = Config.channel.isNightlyOrDebug + + /** + * Enables viewing downloads in browser. + */ + val viewDownloads = Config.channel.isNightlyOrDebug } diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt index 83d5ef93d..6039adc8c 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt @@ -488,7 +488,7 @@ sealed class Event { NEW_PRIVATE_TAB, SHARE, BACK, FORWARD, RELOAD, STOP, OPEN_IN_FENIX, SAVE_TO_COLLECTION, ADD_TO_TOP_SITES, ADD_TO_HOMESCREEN, QUIT, READER_MODE_ON, READER_MODE_OFF, OPEN_IN_APP, BOOKMARK, READER_MODE_APPEARANCE, ADDONS_MANAGER, - BOOKMARKS, HISTORY, SYNC_TABS + BOOKMARKS, HISTORY, SYNC_TABS, DOWNLOADS } override val extras: Map? diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt index 5fd5b8ec6..436b8136d 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt @@ -380,6 +380,13 @@ class DefaultBrowserToolbarController( BrowserFragmentDirections.actionGlobalHistoryFragment() ) } + + ToolbarMenu.Item.Downloads -> browserAnimator.captureEngineViewAndDrawStatically { + navController.nav( + R.id.browserFragment, + BrowserFragmentDirections.actionGlobalDownloadsFragment() + ) + } } } @@ -414,6 +421,7 @@ class DefaultBrowserToolbarController( ToolbarMenu.Item.AddonsManager -> Event.BrowserMenuItemTapped.Item.ADDONS_MANAGER ToolbarMenu.Item.Bookmarks -> Event.BrowserMenuItemTapped.Item.BOOKMARKS ToolbarMenu.Item.History -> Event.BrowserMenuItemTapped.Item.HISTORY + ToolbarMenu.Item.Downloads -> Event.BrowserMenuItemTapped.Item.DOWNLOADS } activity.components.analytics.metrics.track(Event.BrowserMenuItemTapped(eventItem)) diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt index dc98afa23..2ee5693fe 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt @@ -179,6 +179,7 @@ class DefaultToolbarMenu( .shouldDeleteBrowsingDataOnQuit val menuItems = listOfNotNull( + if (FeatureFlags.viewDownloads) downloadsItem else null, historyItem, bookmarksItem, if (FeatureFlags.syncedTabs) syncedTabs else null, @@ -333,6 +334,14 @@ class DefaultToolbarMenu( onItemTapped.invoke(ToolbarMenu.Item.Bookmarks) } + val downloadsItem = BrowserMenuImageText( + "Downloads", + R.drawable.ic_download, + primaryTextColor() + ) { + onItemTapped.invoke(ToolbarMenu.Item.Downloads) + } + @ColorRes private fun primaryTextColor() = ThemeManager.resolveAttribute(R.attr.primaryText, context) diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt index 5a8d88b13..27b47c309 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt @@ -30,6 +30,7 @@ interface ToolbarMenu { object ReaderModeAppearance : Item() object Bookmarks : Item() object History : Item() + object Downloads : Item() } val menuBuilder: BrowserMenuBuilder diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 1becb7b8d..a914d33b1 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -771,6 +771,15 @@ class HomeFragment : Fragment() { HomeFragmentDirections.actionGlobalHistoryFragment() ) } + + HomeMenu.Item.Downloads -> { + hideOnboardingIfNeeded() + nav( + R.id.homeFragment, + HomeFragmentDirections.actionGlobalDownloadsFragment() + ) + } + HomeMenu.Item.Help -> { hideOnboardingIfNeeded() (activity as HomeActivity).openToBrowserAndLoad( diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt b/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt index 1ab68040b..a90dc3b50 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt @@ -43,6 +43,7 @@ class HomeMenu( object SyncedTabs : Item() object History : Item() object Bookmarks : Item() + object Downloads : Item() object Quit : Item() object Sync : Item() } @@ -144,6 +145,14 @@ class HomeMenu( onItemTapped.invoke(Item.Help) } + val downloadsItem = BrowserMenuImageText( + "Downloads", + R.drawable.ic_download, + primaryTextColor + ) { + onItemTapped.invoke(Item.Downloads) + } + // Only query account manager if it has been initialized. // We don't want to cause its initialization just for this check. val accountAuthItem = if (context.components.backgroundServices.accountManagerAvailableQueue.isReady()) { @@ -161,6 +170,7 @@ class HomeMenu( if (FeatureFlags.syncedTabs) syncedTabsItem else null, bookmarksItem, historyItem, + if (FeatureFlags.viewDownloads) downloadsItem else null, BrowserMenuDivider(), addons, BrowserMenuDivider(), diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadAdapter.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadAdapter.kt new file mode 100644 index 000000000..95ce6f247 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadAdapter.kt @@ -0,0 +1,41 @@ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import org.mozilla.fenix.library.SelectionHolder +import org.mozilla.fenix.library.downloads.viewholders.DownloadsListItemViewHolder + +class DownloadAdapter( + private val downloadInteractor: DownloadInteractor +) : RecyclerView.Adapter(), SelectionHolder { + private var downloads: List = listOf() + private var mode: DownloadFragmentState.Mode = DownloadFragmentState.Mode.Normal + override val selectedItems get() = mode.selectedItems + + override fun getItemCount(): Int = downloads.size + override fun getItemViewType(position: Int): Int = DownloadsListItemViewHolder.LAYOUT_ID + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadsListItemViewHolder { + val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) + return DownloadsListItemViewHolder(view, downloadInteractor, this) + } + + fun updateMode(mode: DownloadFragmentState.Mode) { + this.mode = mode + } + + override fun onBindViewHolder(holder: DownloadsListItemViewHolder, position: Int) { + holder.bind(downloads[position]) + } + + fun updateDownloads(downloads: List) { + this.downloads = downloads + notifyDataSetChanged() + } +} diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadController.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadController.kt new file mode 100644 index 000000000..9cc676272 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadController.kt @@ -0,0 +1,31 @@ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads + +import org.mozilla.fenix.browser.browsingmode.BrowsingMode + +interface DownloadController { + fun handleOpen(item: DownloadItem, mode: BrowsingMode? = null) + fun handleBackPressed(): Boolean +} + +class DefaultDownloadController( + private val store: DownloadFragmentStore, + private val openToFileManager: (item: DownloadItem, mode: BrowsingMode?) -> Unit +) : DownloadController { + override fun handleOpen(item: DownloadItem, mode: BrowsingMode?) { + openToFileManager(item, mode) + } + + override fun handleBackPressed(): Boolean { + return if (store.state.mode is DownloadFragmentState.Mode.Editing) { + store.dispatch(DownloadFragmentAction.ExitEditMode) + true + } else { + false + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragment.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragment.kt new file mode 100644 index 000000000..82ed98e0a --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragment.kt @@ -0,0 +1,108 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import kotlinx.android.synthetic.main.fragment_downloads.view.* +import kotlinx.coroutines.ExperimentalCoroutinesApi +import mozilla.components.feature.downloads.AbstractFetchDownloadService +import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.support.base.feature.UserInteractionHandler +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.R +import org.mozilla.fenix.browser.browsingmode.BrowsingMode +import org.mozilla.fenix.components.StoreProvider +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.ext.requireComponents +import org.mozilla.fenix.ext.showToolbar +import org.mozilla.fenix.library.LibraryPageFragment + +@SuppressWarnings("TooManyFunctions", "LargeClass") +class DownloadFragment : LibraryPageFragment(), UserInteractionHandler { + private lateinit var downloadStore: DownloadFragmentStore + private lateinit var downloadView: DownloadView + private lateinit var downloadInteractor: DownloadInteractor + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val view = inflater.inflate(R.layout.fragment_downloads, container, false) + + val items = requireComponents.core.store.state.downloads.map { + DownloadItem( + it.value.id, + it.value.fileName, + it.value.filePath, + it.value.contentLength.toString(), + it.value.contentType + ) + } + + downloadStore = StoreProvider.get(this) { + DownloadFragmentStore( + DownloadFragmentState( + items = items, + mode = DownloadFragmentState.Mode.Normal + ) + ) + } + + val downloadController: DownloadController = DefaultDownloadController( + downloadStore, + ::openItem + ) + downloadInteractor = DownloadInteractor( + downloadController + ) + downloadView = DownloadView(view.downloadsLayout, downloadInteractor) + + return view + } + + override val selectedItems get() = downloadStore.state.mode.selectedItems + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + requireComponents.analytics.metrics.track(Event.HistoryOpened) + + setHasOptionsMenu(false) + } + + @ExperimentalCoroutinesApi + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + consumeFrom(downloadStore) { + downloadView.update(it) + } + } + + override fun onResume() { + super.onResume() + showToolbar(getString(R.string.library_downloads)) + } + + override fun onBackPressed(): Boolean { + return downloadView.onBackPressed() + } + + private fun openItem(item: DownloadItem, mode: BrowsingMode? = null) { + + mode?.let { (activity as HomeActivity).browsingModeManager.mode = it } + context?.let { + AbstractFetchDownloadService.openFile( + context = it, + contentType = item.contentType, + filePath = item.filePath + ) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragmentStore.kt new file mode 100644 index 000000000..2078064a7 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragmentStore.kt @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads + +import mozilla.components.lib.state.Action +import mozilla.components.lib.state.State +import mozilla.components.lib.state.Store + +/** + * Class representing a history entry + * @property id Unique id of the download item + * @property fileName File name of the download item + * @property filePath Full path of the download item + * @property size The size in bytes of the download item + * @property contentType The type of file the download is + */ +data class DownloadItem(val id: Long, val fileName: String?, val filePath: String, val size: String, val contentType: String?) + +/** + * The [Store] for holding the [DownloadFragmentState] and applying [DownloadFragmentAction]s. + */ +class DownloadFragmentStore(initialState: DownloadFragmentState) : + Store(initialState, ::downloadStateReducer) + +/** + * Actions to dispatch through the `DownloadStore` to modify `DownloadState` through the reducer. + */ +sealed class DownloadFragmentAction : Action { + object ExitEditMode : DownloadFragmentAction() +} + +/** + * The state for the Download Screen + * @property items List of DownloadItem to display + * @property mode Current Mode of Download + */ +data class DownloadFragmentState( + val items: List, + val mode: Mode +) : State { + sealed class Mode { + open val selectedItems = emptySet() + + object Normal : Mode() + data class Editing(override val selectedItems: Set) : DownloadFragmentState.Mode() + } +} + +/** + * The DownloadState Reducer. + */ +private fun downloadStateReducer( + state: DownloadFragmentState, + action: DownloadFragmentAction +): DownloadFragmentState { + return when (action) { + is DownloadFragmentAction.ExitEditMode -> state.copy(mode = DownloadFragmentState.Mode.Normal) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadInteractor.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadInteractor.kt new file mode 100644 index 000000000..244444634 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadInteractor.kt @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads +/** + * Interactor for the download screen + * Provides implementations for the DownloadViewInteractor + */ +@SuppressWarnings("TooManyFunctions") +class DownloadInteractor( + private val downloadController: DownloadController +) : DownloadViewInteractor { + override fun open(item: DownloadItem) { + downloadController.handleOpen(item) + } + + override fun select(item: DownloadItem) { + TODO("Not yet implemented") + } + + override fun deselect(item: DownloadItem) { + TODO("Not yet implemented") + } + + override fun onBackPressed(): Boolean { + return downloadController.handleBackPressed() + } +} diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadView.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadView.kt new file mode 100644 index 000000000..3261ee91e --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadView.kt @@ -0,0 +1,74 @@ + + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.SimpleItemAnimator +import kotlinx.android.synthetic.main.component_downloads.view.* +import mozilla.components.support.base.feature.UserInteractionHandler +import org.mozilla.fenix.R +import org.mozilla.fenix.library.LibraryPageView +import org.mozilla.fenix.library.SelectionInteractor + +/** + * Interface for the DownloadViewInteractor. This interface is implemented by objects that want + * to respond to user interaction on the DownloadView + */ +interface DownloadViewInteractor : SelectionInteractor { + + /** + * Called on backpressed to exit edit mode + */ + fun onBackPressed(): Boolean +} + +/** + * View that contains and configures the Downloads List + */ +class DownloadView( + container: ViewGroup, + val interactor: DownloadInteractor +) : LibraryPageView(container), UserInteractionHandler { + + val view: View = LayoutInflater.from(container.context) + .inflate(R.layout.component_downloads, container, true) + + var mode: DownloadFragmentState.Mode = DownloadFragmentState.Mode.Normal + private set + + val downloadAdapter = DownloadAdapter(interactor) + private val layoutManager = LinearLayoutManager(container.context) + + init { + view.download_list.apply { + layoutManager = this@DownloadView.layoutManager + adapter = downloadAdapter + (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false + } + } + + fun update(state: DownloadFragmentState) { + + view.swipe_refresh.isEnabled = + state.mode === DownloadFragmentState.Mode.Normal + mode = state.mode + + downloadAdapter.updateMode(state.mode) + downloadAdapter.updateDownloads(state.items) + + setUiForNormalMode( + context.getString(R.string.library_downloads) + ) + } + + override fun onBackPressed(): Boolean { + return interactor.onBackPressed() + } +} diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/viewholders/DownloadsListItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/viewholders/DownloadsListItemViewHolder.kt new file mode 100644 index 000000000..4126b2cd7 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/downloads/viewholders/DownloadsListItemViewHolder.kt @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads.viewholders + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.download_list_item.view.* +import kotlinx.android.synthetic.main.library_site_item.view.* +import org.mozilla.fenix.R +import org.mozilla.fenix.ext.hideAndDisable +import org.mozilla.fenix.library.SelectionHolder +import org.mozilla.fenix.library.downloads.DownloadInteractor +import org.mozilla.fenix.library.downloads.DownloadItem +import mozilla.components.feature.downloads.toMegabyteString + +class DownloadsListItemViewHolder( + view: View, + private val downloadInteractor: DownloadInteractor, + private val selectionHolder: SelectionHolder +) : RecyclerView.ViewHolder(view) { + + private var item: DownloadItem? = null + + fun bind( + item: DownloadItem + ) { + itemView.download_layout.visibility = View.VISIBLE + itemView.download_layout.titleView.text = item.fileName + itemView.download_layout.urlView.text = item.size.toLong().toMegabyteString() + + itemView.download_layout.setSelectionInteractor(item, selectionHolder, downloadInteractor) + itemView.download_layout.changeSelected(item in selectionHolder.selectedItems) + + itemView.overflow_menu.hideAndDisable() + itemView.favicon.setImageResource(R.drawable.ic_download_default) + itemView.favicon.isClickable = false + + this.item = item + } + + companion object { + const val LAYOUT_ID = R.layout.download_list_item + } +} diff --git a/app/src/main/res/drawable/ic_download_default.xml b/app/src/main/res/drawable/ic_download_default.xml new file mode 100644 index 000000000..c7387d54b --- /dev/null +++ b/app/src/main/res/drawable/ic_download_default.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/layout/component_downloads.xml b/app/src/main/res/layout/component_downloads.xml new file mode 100644 index 000000000..a6bcbe35e --- /dev/null +++ b/app/src/main/res/layout/component_downloads.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/download_list_item.xml b/app/src/main/res/layout/download_list_item.xml new file mode 100644 index 000000000..7e9ddafd4 --- /dev/null +++ b/app/src/main/res/layout/download_list_item.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_downloads.xml b/app/src/main/res/layout/fragment_downloads.xml new file mode 100644 index 000000000..11fc15df2 --- /dev/null +++ b/app/src/main/res/layout/fragment_downloads.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index a142bf363..d8f6cef7d 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -70,6 +70,9 @@ + + @@ -239,6 +242,12 @@ android:label="@string/library_history" tools:layout="@layout/fragment_history" /> + + No history here + + + No downloads here + + %1$d selected + Sorry. %1$s can’t load that page. diff --git a/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadAdapterTest.kt b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadAdapterTest.kt new file mode 100644 index 000000000..79ba1a8ab --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadAdapterTest.kt @@ -0,0 +1,56 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads + +import androidx.recyclerview.widget.RecyclerView +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.verify +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner + +@RunWith(FenixRobolectricTestRunner::class) +class DownloadAdapterTest { + + private lateinit var interactor: DownloadInteractor + private lateinit var adapter: DownloadAdapter + + @Before + fun setup() { + interactor = mockk() + adapter = DownloadAdapter(interactor) + + every { interactor.select(any()) } just Runs + } + + @Test + fun `getItemCount should return the number of tab collections`() { + val download = mockk() + + assertEquals(0, adapter.itemCount) + + adapter.updateDownloads( + downloads = listOf(download) + ) + assertEquals(1, adapter.itemCount) + } + + @Test + fun `updateData inserts item`() { + val download = mockk { + } + val observer = mockk(relaxed = true) + adapter.registerAdapterDataObserver(observer) + adapter.updateDownloads( + downloads = listOf(download) + ) + verify { observer.onChanged() } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadControllerTest.kt b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadControllerTest.kt new file mode 100644 index 000000000..68ae555dd --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadControllerTest.kt @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineScope +import org.junit.Assert.assertFalse +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.browser.browsingmode.BrowsingMode +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner + +@ExperimentalCoroutinesApi +@RunWith(FenixRobolectricTestRunner::class) +class DownloadControllerTest { + private val downloadItem = DownloadItem(0, "title", "url", "77", "jpg") + private val scope: CoroutineScope = TestCoroutineScope() + private val store: DownloadFragmentStore = mockk(relaxed = true) + private val state: DownloadFragmentState = mockk(relaxed = true) + private val openToFileManager: (DownloadItem, BrowsingMode?) -> Unit = mockk(relaxed = true) + private val invalidateOptionsMenu: () -> Unit = mockk(relaxed = true) + private val controller = DefaultDownloadController( + store, + openToFileManager + ) + + @Before + fun setUp() { + every { store.state } returns state + } + + @Test + fun onPressDownloadItemInNormalMode() { + controller.handleOpen(downloadItem) + + verify { + openToFileManager(downloadItem, null) + } + } + + @Test + fun onOpenItemInNormalMode() { + controller.handleOpen(downloadItem, BrowsingMode.Normal) + + verify { + openToFileManager(downloadItem, BrowsingMode.Normal) + } + } + + @Test + fun onBackPressedInNormalMode() { + every { state.mode } returns DownloadFragmentState.Mode.Normal + + assertFalse(controller.handleBackPressed()) + } +} diff --git a/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadInteractorTest.kt new file mode 100644 index 000000000..3e552d6ab --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadInteractorTest.kt @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.downloads + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verifyAll +import org.junit.Assert.assertTrue +import org.junit.Test + +class DownloadInteractorTest { + private val downloadItem = DownloadItem(0, "title", "url", "5.6 mb", "png") + val controller: DownloadController = mockk(relaxed = true) + val interactor = DownloadInteractor(controller) + + @Test + fun onOpen() { + interactor.open(downloadItem) + + verifyAll { + controller.handleOpen(downloadItem) + } + } + + @Test + fun onBackPressed() { + every { + controller.handleBackPressed() + } returns true + + val backpressHandled = interactor.onBackPressed() + + verifyAll { + controller.handleBackPressed() + } + assertTrue(backpressHandled) + } +}