Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Commit

Permalink
For #349: View Downloads
Browse files Browse the repository at this point in the history
  • Loading branch information
Kate Glazko authored and kglazko committed Aug 19, 2020
1 parent 929ec54 commit f83372b
Show file tree
Hide file tree
Showing 23 changed files with 701 additions and 1 deletion.
5 changes: 5 additions & 0 deletions app/src/main/java/org/mozilla/fenix/FeatureFlags.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Events.browserMenuActionKeys, String>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,13 @@ class DefaultBrowserToolbarController(
BrowserFragmentDirections.actionGlobalHistoryFragment()
)
}

ToolbarMenu.Item.Downloads -> browserAnimator.captureEngineViewAndDrawStatically {
navController.nav(
R.id.browserFragment,
BrowserFragmentDirections.actionGlobalDownloadsFragment()
)
}
}
}

Expand Down Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ class DefaultToolbarMenu(
.shouldDeleteBrowsingDataOnQuit

val menuItems = listOfNotNull(
if (FeatureFlags.viewDownloads) downloadsItem else null,
historyItem,
bookmarksItem,
if (FeatureFlags.syncedTabs) syncedTabs else null,
Expand Down Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface ToolbarMenu {
object ReaderModeAppearance : Item()
object Bookmarks : Item()
object History : Item()
object Downloads : Item()
}

val menuBuilder: BrowserMenuBuilder
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class HomeMenu(
object SyncedTabs : Item()
object History : Item()
object Bookmarks : Item()
object Downloads : Item()
object Quit : Item()
object Sync : Item()
}
Expand Down Expand Up @@ -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()) {
Expand All @@ -161,6 +170,7 @@ class HomeMenu(
if (FeatureFlags.syncedTabs) syncedTabsItem else null,
bookmarksItem,
historyItem,
if (FeatureFlags.viewDownloads) downloadsItem else null,
BrowserMenuDivider(),
addons,
BrowserMenuDivider(),
Expand Down
Original file line number Diff line number Diff line change
@@ -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<DownloadsListItemViewHolder>(), SelectionHolder<DownloadItem> {
private var downloads: List<DownloadItem> = 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<DownloadItem>) {
this.downloads = downloads
notifyDataSetChanged()
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
}
Original file line number Diff line number Diff line change
@@ -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<DownloadItem>(), 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
)
}
}
}
Original file line number Diff line number Diff line change
@@ -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<DownloadFragmentState, DownloadFragmentAction>(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<DownloadItem>,
val mode: Mode
) : State {
sealed class Mode {
open val selectedItems = emptySet<DownloadItem>()

object Normal : Mode()
data class Editing(override val selectedItems: Set<DownloadItem>) : 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)
}
}
Original file line number Diff line number Diff line change
@@ -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()
}
}
Loading

0 comments on commit f83372b

Please sign in to comment.