* For #1574: Adds collections to home view * Adds colored icons and expansion * Adds state change * Adds more styling * Adds ItsNotBrokenSnacks * Adds chevron * Improves styling of swipe to delete and adds delete action * Fix nits * Try to add real saving
This commit is contained in:
parent
282ad31345
commit
7d577e5953
|
@ -60,7 +60,6 @@ import org.mozilla.fenix.R
|
|||
import org.mozilla.fenix.collections.CreateCollectionFragment
|
||||
import org.mozilla.fenix.collections.CreateCollectionViewModel
|
||||
import org.mozilla.fenix.collections.SaveCollectionStep
|
||||
import org.mozilla.fenix.collections.Tab
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.components.FindInPageIntegration
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
|
@ -75,7 +74,8 @@ import org.mozilla.fenix.customtabs.CustomTabsIntegration
|
|||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.ext.share
|
||||
import org.mozilla.fenix.ext.urlToHost
|
||||
import org.mozilla.fenix.ext.urlToTrimmedHost
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import org.mozilla.fenix.lib.Do
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||
|
@ -606,7 +606,7 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope,
|
|||
|
||||
private fun showSaveToCollection() {
|
||||
getSessionById()?.let {
|
||||
val tabs = Tab(it.id, it.url, it.url.urlToHost(), it.title)
|
||||
val tabs = Tab(it.id, it.url, it.url.urlToTrimmedHost(), it.title)
|
||||
val viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(CreateCollectionViewModel::class.java)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ package org.mozilla.fenix.collections
|
|||
file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import org.mozilla.fenix.mvi.Action
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
|
@ -12,18 +14,6 @@ import org.mozilla.fenix.mvi.Reducer
|
|||
import org.mozilla.fenix.mvi.UIComponent
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
|
||||
data class Tab(
|
||||
val sessionId: String,
|
||||
val url: String,
|
||||
val hostname: String,
|
||||
val title: String
|
||||
)
|
||||
|
||||
data class Collection(
|
||||
val collectionId: String,
|
||||
val title: String
|
||||
)
|
||||
|
||||
sealed class SaveCollectionStep {
|
||||
object SelectTabs : SaveCollectionStep()
|
||||
object SelectCollection : SaveCollectionStep()
|
||||
|
@ -55,7 +45,7 @@ sealed class CollectionCreationAction : Action {
|
|||
data class SaveCollectionName(val tabs: List<Tab>, val name: String) :
|
||||
CollectionCreationAction()
|
||||
|
||||
data class SelectCollection(val collection: Collection) :
|
||||
data class SelectCollection(val collection: TabCollection) :
|
||||
CollectionCreationAction()
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import kotlinx.coroutines.launch
|
|||
import mozilla.components.browser.icons.IconRequest
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class CollectionCreationTabListAdapter(
|
||||
|
|
|
@ -25,6 +25,7 @@ import mozilla.components.support.ktx.android.view.hideKeyboard
|
|||
import mozilla.components.support.ktx.android.view.showKeyboard
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.increaseTapArea
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
|
||||
class CollectionCreationUIView(
|
||||
|
|
|
@ -17,11 +17,15 @@ import kotlinx.android.synthetic.main.fragment_create_collection.view.*
|
|||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.ext.getRootView
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
import java.util.Random
|
||||
|
||||
class CreateCollectionFragment : DialogFragment() {
|
||||
// Temporary callback. In the future we will just directly add the collection to the core session manager.
|
||||
var onCollectionSaved: ((TabCollection) -> Unit)? = null
|
||||
private lateinit var collectionCreationComponent: CollectionCreationComponent
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -91,6 +95,8 @@ class CreateCollectionFragment : DialogFragment() {
|
|||
is CollectionCreationAction.SaveCollectionName -> {
|
||||
showSavedSnackbar(it.tabs.size)
|
||||
dismiss()
|
||||
val newCollection = TabCollection(Random().nextInt(), it.name, it.tabs.toMutableList())
|
||||
onCollectionSaved?.invoke(newCollection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ package org.mozilla.fenix.collections
|
|||
file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
|
||||
class CreateCollectionViewModel : ViewModel() {
|
||||
var selectedTabs = setOf<Tab>()
|
||||
|
|
|
@ -14,13 +14,14 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class SaveCollectionListAdapter(
|
||||
val actionEmitter: Observer<CollectionCreationAction>
|
||||
) : RecyclerView.Adapter<CollectionViewHolder>() {
|
||||
|
||||
private var collections: List<Collection> = listOf()
|
||||
private var collections: List<TabCollection> = listOf()
|
||||
private lateinit var job: Job
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CollectionViewHolder {
|
||||
|
@ -58,7 +59,7 @@ class CollectionViewHolder(
|
|||
override val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.IO + job
|
||||
|
||||
private var collection: Collection? = null
|
||||
private var collection: TabCollection? = null
|
||||
|
||||
private val listener = View.OnClickListener {
|
||||
collection?.apply {
|
||||
|
@ -71,7 +72,7 @@ class CollectionViewHolder(
|
|||
view.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
fun bind(collection: Collection) {
|
||||
fun bind(collection: TabCollection) {
|
||||
this.collection = collection
|
||||
view.collection_item.text = collection.title
|
||||
}
|
||||
|
|
|
@ -26,3 +26,20 @@ fun String?.urlToHost(): String {
|
|||
""
|
||||
}
|
||||
}
|
||||
|
||||
fun String?.urlToTrimmedHost(): String {
|
||||
return try {
|
||||
val url = URL(this)
|
||||
val firstIndex = url.host.indexOfFirst { it == '.' } + 1
|
||||
val lastIndex = url.host.indexOfLast { it == '.' }
|
||||
|
||||
// Trim all but the title of the website from the hostname. 'www.mozilla.org' becomes 'mozilla'
|
||||
when {
|
||||
firstIndex - 1 == lastIndex -> url.host.substring(0, lastIndex)
|
||||
firstIndex < lastIndex -> url.host.substring(firstIndex, lastIndex)
|
||||
else -> url.host
|
||||
}
|
||||
} catch (e: MalformedURLException) {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,18 +38,20 @@ import org.mozilla.fenix.R
|
|||
import org.mozilla.fenix.collections.CreateCollectionFragment
|
||||
import org.mozilla.fenix.collections.CreateCollectionViewModel
|
||||
import org.mozilla.fenix.collections.SaveCollectionStep
|
||||
import org.mozilla.fenix.collections.Tab
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.allowUndo
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.ext.share
|
||||
import org.mozilla.fenix.ext.urlToHost
|
||||
import org.mozilla.fenix.ext.urlToTrimmedHost
|
||||
import org.mozilla.fenix.home.sessioncontrol.Mode
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlComponent
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlState
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabAction
|
||||
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import org.mozilla.fenix.lib.Do
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||
|
@ -73,6 +75,9 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
override val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.Main + job
|
||||
|
||||
// TODO Remove this stub when we have the a-c version!
|
||||
var storedCollections = mutableListOf<TabCollection>()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
@ -80,12 +85,12 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
): View? {
|
||||
job = Job()
|
||||
val view = inflater.inflate(R.layout.fragment_home, container, false)
|
||||
val mode =
|
||||
if ((activity as HomeActivity).browsingModeManager.isPrivate) Mode.Private else Mode.Normal
|
||||
val mode = if ((activity as HomeActivity).browsingModeManager.isPrivate) Mode.Private else Mode.Normal
|
||||
|
||||
sessionControlComponent = SessionControlComponent(
|
||||
view.homeLayout,
|
||||
bus,
|
||||
SessionControlState(listOf(), mode)
|
||||
SessionControlState(listOf(), listOf(), mode)
|
||||
)
|
||||
|
||||
view.homeLayout.applyConstraintSet {
|
||||
|
@ -178,6 +183,7 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
.subscribe {
|
||||
when (it) {
|
||||
is SessionControlAction.Tab -> handleTabAction(it.action)
|
||||
is SessionControlAction.Collection -> handleCollectionAction(it.action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,6 +250,44 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun handleCollectionAction(action: CollectionAction) {
|
||||
when (action) {
|
||||
is CollectionAction.Expand -> {
|
||||
storedCollections.find { it.id == action.collection.id }?.apply { expanded = true }
|
||||
}
|
||||
is CollectionAction.Collapse -> {
|
||||
storedCollections.find { it.id == action.collection.id }?.apply { expanded = false }
|
||||
}
|
||||
is CollectionAction.Delete -> {
|
||||
storedCollections.find { it.id == action.collection.id }?.let { storedCollections.remove(it) }
|
||||
}
|
||||
is CollectionAction.AddTab -> {
|
||||
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1575")
|
||||
}
|
||||
is CollectionAction.Rename -> {
|
||||
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1575")
|
||||
}
|
||||
is CollectionAction.OpenTabs -> {
|
||||
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "2205")
|
||||
}
|
||||
is CollectionAction.ShareTabs -> {
|
||||
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1585")
|
||||
}
|
||||
is CollectionAction.RemoveTab -> {
|
||||
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1578")
|
||||
}
|
||||
}
|
||||
|
||||
emitCollectionChange()
|
||||
}
|
||||
|
||||
private fun emitCollectionChange() {
|
||||
storedCollections.map { it.copy() }.let {
|
||||
getManagedEmitter<SessionControlChange>().onNext(SessionControlChange.CollectionsChange(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
sessionObserver?.let {
|
||||
|
@ -326,7 +370,7 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
org.mozilla.fenix.home.sessioncontrol.Tab(
|
||||
it.id,
|
||||
it.url,
|
||||
it.url.urlToHost(),
|
||||
it.url.urlToTrimmedHost(),
|
||||
it.title,
|
||||
selected,
|
||||
it.thumbnail
|
||||
|
@ -364,7 +408,7 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
org.mozilla.fenix.home.sessioncontrol.Tab(
|
||||
it.id,
|
||||
it.url,
|
||||
it.url.urlToHost(),
|
||||
it.url.urlToTrimmedHost(),
|
||||
it.title,
|
||||
selected,
|
||||
it.thumbnail
|
||||
|
@ -376,7 +420,7 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
|
||||
private fun showCollectionCreationFragment(selectedTabId: String?) {
|
||||
val tabs = requireComponents.core.sessionManager.sessions
|
||||
.map { Tab(it.id, it.url, it.url.urlToHost(), it.title) }
|
||||
.map { Tab(it.id, it.url, it.url.urlToTrimmedHost(), it.title) }
|
||||
|
||||
val viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(CreateCollectionViewModel::class.java)
|
||||
|
@ -387,11 +431,17 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
viewModel?.selectedTabs = selectedSet
|
||||
viewModel?.saveCollectionStep = SaveCollectionStep.SelectTabs
|
||||
|
||||
CreateCollectionFragment()
|
||||
.show(
|
||||
CreateCollectionFragment().also {
|
||||
it.onCollectionSaved = {
|
||||
storedCollections.add(it)
|
||||
emitCollectionChange()
|
||||
}
|
||||
|
||||
it.show(
|
||||
requireActivity().supportFragmentManager,
|
||||
CreateCollectionFragment.createCollectionTag
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -15,6 +15,10 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.NoTabMessageViewHolder
|
|||
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabHeaderViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionMessageViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
sealed class AdapterItem {
|
||||
|
@ -24,6 +28,10 @@ sealed class AdapterItem {
|
|||
object PrivateBrowsingDescription : AdapterItem()
|
||||
object SaveTabGroup : AdapterItem()
|
||||
object DeleteTabs : AdapterItem()
|
||||
object CollectionHeader : AdapterItem()
|
||||
object NoCollectionMessage : AdapterItem()
|
||||
data class CollectionItem(val collection: TabCollection) : AdapterItem()
|
||||
data class TabInCollectionItem(val collection: TabCollection, val tab: Tab, val isLastTab: Boolean) : AdapterItem()
|
||||
|
||||
val viewType: Int
|
||||
get() = when (this) {
|
||||
|
@ -33,6 +41,10 @@ sealed class AdapterItem {
|
|||
SaveTabGroup -> SaveTabGroupViewHolder.LAYOUT_ID
|
||||
PrivateBrowsingDescription -> PrivateBrowsingDescriptionViewHolder.LAYOUT_ID
|
||||
DeleteTabs -> DeleteTabsViewHolder.LAYOUT_ID
|
||||
CollectionHeader -> CollectionHeaderViewHolder.LAYOUT_ID
|
||||
NoCollectionMessage -> NoCollectionMessageViewHolder.LAYOUT_ID
|
||||
is CollectionItem -> CollectionViewHolder.LAYOUT_ID
|
||||
is TabInCollectionItem -> TabInCollectionViewHolder.LAYOUT_ID
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,6 +74,12 @@ class SessionControlAdapter(
|
|||
actionEmitter
|
||||
)
|
||||
DeleteTabsViewHolder.LAYOUT_ID -> DeleteTabsViewHolder(view, actionEmitter)
|
||||
CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view)
|
||||
NoCollectionMessageViewHolder.LAYOUT_ID -> NoCollectionMessageViewHolder(
|
||||
view
|
||||
)
|
||||
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, actionEmitter, job)
|
||||
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(view, actionEmitter, job)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +103,13 @@ class SessionControlAdapter(
|
|||
is TabViewHolder -> holder.bindSession(
|
||||
(items[position] as AdapterItem.TabItem).tab
|
||||
)
|
||||
is CollectionViewHolder -> holder.bindSession(
|
||||
(items[position] as AdapterItem.CollectionItem).collection
|
||||
)
|
||||
is TabInCollectionViewHolder -> {
|
||||
val item = (items[position] as AdapterItem.TabInCollectionItem)
|
||||
holder.bindSession(item.collection, item.tab, item.isLastTab)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import org.mozilla.fenix.mvi.ViewState
|
|||
class SessionControlComponent(
|
||||
private val container: ViewGroup,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: SessionControlState = SessionControlState(emptyList(), Mode.Normal)
|
||||
override var initialState: SessionControlState = SessionControlState(emptyList(), emptyList(), Mode.Normal)
|
||||
) :
|
||||
UIComponent<SessionControlState, SessionControlAction, SessionControlChange>(
|
||||
bus.getManagedEmitter(SessionControlAction::class.java),
|
||||
|
@ -26,6 +26,7 @@ class SessionControlComponent(
|
|||
|
||||
override val reducer: (SessionControlState, SessionControlChange) -> SessionControlState = { state, change ->
|
||||
when (change) {
|
||||
is SessionControlChange.CollectionsChange -> state.copy(collections = change.collections)
|
||||
is SessionControlChange.TabsChange -> state.copy(tabs = change.tabs)
|
||||
is SessionControlChange.ModeChange -> state.copy(mode = change.mode)
|
||||
}
|
||||
|
@ -45,10 +46,18 @@ data class Tab(
|
|||
val url: String,
|
||||
val hostname: String,
|
||||
val title: String,
|
||||
val selected: Boolean,
|
||||
val selected: Boolean? = null,
|
||||
val thumbnail: Bitmap? = null
|
||||
)
|
||||
|
||||
data class TabCollection(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
val tabs: MutableList<Tab>,
|
||||
val iconColor: Int = 0,
|
||||
var expanded: Boolean = false
|
||||
)
|
||||
|
||||
sealed class Mode {
|
||||
object Normal : Mode()
|
||||
object Private : Mode()
|
||||
|
@ -56,6 +65,7 @@ sealed class Mode {
|
|||
|
||||
data class SessionControlState(
|
||||
val tabs: List<Tab>,
|
||||
val collections: List<TabCollection>,
|
||||
val mode: Mode
|
||||
) : ViewState
|
||||
|
||||
|
@ -70,15 +80,32 @@ sealed class TabAction : Action {
|
|||
object PrivateBrowsingLearnMore : TabAction()
|
||||
}
|
||||
|
||||
sealed class CollectionAction : Action {
|
||||
data class Expand(val collection: TabCollection) : CollectionAction()
|
||||
data class Collapse(val collection: TabCollection) : CollectionAction()
|
||||
data class Delete(val collection: TabCollection) : CollectionAction()
|
||||
data class AddTab(val collection: TabCollection) : CollectionAction()
|
||||
data class Rename(val collection: TabCollection) : CollectionAction()
|
||||
data class OpenTabs(val collection: TabCollection) : CollectionAction()
|
||||
data class ShareTabs(val collection: TabCollection) : CollectionAction()
|
||||
data class RemoveTab(val collection: TabCollection, val tab: Tab) : CollectionAction()
|
||||
}
|
||||
|
||||
sealed class SessionControlAction : Action {
|
||||
data class Tab(val action: TabAction) : SessionControlAction()
|
||||
data class Collection(val action: CollectionAction) : SessionControlAction()
|
||||
}
|
||||
|
||||
fun Observer<SessionControlAction>.onNext(tabAction: TabAction) {
|
||||
onNext(SessionControlAction.Tab(tabAction))
|
||||
}
|
||||
|
||||
fun Observer<SessionControlAction>.onNext(collectionAction: CollectionAction) {
|
||||
onNext(SessionControlAction.Collection(collectionAction))
|
||||
}
|
||||
|
||||
sealed class SessionControlChange : Change {
|
||||
data class TabsChange(val tabs: List<Tab>) : SessionControlChange()
|
||||
data class ModeChange(val mode: Mode) : SessionControlChange()
|
||||
data class CollectionsChange(val collections: List<TabCollection>) : SessionControlChange()
|
||||
}
|
||||
|
|
|
@ -17,11 +17,12 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
|||
import org.mozilla.fenix.BuildConfig
|
||||
|
||||
// Convert HomeState into a data structure HomeAdapter understands
|
||||
@SuppressWarnings("ComplexMethod")
|
||||
@SuppressWarnings("ComplexMethod", "NestedBlockDepth")
|
||||
private fun SessionControlState.toAdapterList(): List<AdapterItem> {
|
||||
val items = mutableListOf<AdapterItem>()
|
||||
items.add(AdapterItem.TabHeader)
|
||||
|
||||
// Populate tabs
|
||||
if (tabs.isNotEmpty()) {
|
||||
tabs.reversed().map(AdapterItem::TabItem).forEach { items.add(it) }
|
||||
if (mode == Mode.Private) {
|
||||
|
@ -36,9 +37,39 @@ private fun SessionControlState.toAdapterList(): List<AdapterItem> {
|
|||
items.add(item)
|
||||
}
|
||||
|
||||
// Populate collections
|
||||
if (mode == Mode.Normal) {
|
||||
items.add(AdapterItem.CollectionHeader)
|
||||
if (collections.isNotEmpty()) {
|
||||
|
||||
// If the collection is expanded, we want to add all of its tabs beneath it in the adapter
|
||||
collections.reversed().map(AdapterItem::CollectionItem).forEach {
|
||||
if (it.collection.expanded) {
|
||||
items.add(it)
|
||||
addCollectionTabItems(it.collection, it.collection.tabs, items)
|
||||
} else {
|
||||
items.add(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items.add(AdapterItem.NoCollectionMessage)
|
||||
}
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
private fun addCollectionTabItems(
|
||||
collection: TabCollection,
|
||||
tabs: MutableList<Tab>,
|
||||
itemList: MutableList<AdapterItem>
|
||||
) {
|
||||
for (tabIndex in 0 until tabs.size) {
|
||||
itemList.add(AdapterItem.TabInCollectionItem
|
||||
(collection, collection.tabs[tabIndex], tabIndex == collection.tabs.size - 1))
|
||||
}
|
||||
}
|
||||
|
||||
class SessionControlUIView(
|
||||
container: ViewGroup,
|
||||
actionEmitter: Observer<SessionControlAction>,
|
||||
|
|
|
@ -12,6 +12,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observer
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabViewHolder
|
||||
|
||||
class SwipeToDeleteCallback(
|
||||
|
@ -27,8 +28,11 @@ class SwipeToDeleteCallback(
|
|||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
if (viewHolder is TabViewHolder) {
|
||||
actionEmitter.onNext(TabAction.Close(viewHolder.tab?.sessionId!!))
|
||||
when (viewHolder) {
|
||||
is TabViewHolder -> actionEmitter.onNext(TabAction.Close(viewHolder.tab?.sessionId!!))
|
||||
is TabInCollectionViewHolder -> {
|
||||
actionEmitter.onNext(CollectionAction.RemoveTab(viewHolder.collection, viewHolder.tab))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,11 +47,18 @@ class SwipeToDeleteCallback(
|
|||
) {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
||||
val icon = ContextCompat.getDrawable(recyclerView.context, R.drawable.ic_delete)
|
||||
val background = ContextCompat.getDrawable(
|
||||
recyclerView.context,
|
||||
R.drawable.session_background
|
||||
)
|
||||
|
||||
val backgroundDrawable = when {
|
||||
viewHolder is TabInCollectionViewHolder && viewHolder.isLastTab -> {
|
||||
R.drawable.tab_in_collection_last_swipe_background
|
||||
}
|
||||
viewHolder is TabInCollectionViewHolder -> {
|
||||
R.drawable.tab_in_collection_swipe_background
|
||||
}
|
||||
else -> R.drawable.session_background
|
||||
}
|
||||
|
||||
val background = ContextCompat.getDrawable(recyclerView.context, backgroundDrawable)
|
||||
background?.let {
|
||||
icon?.let {
|
||||
val itemView = viewHolder.itemView
|
||||
|
@ -95,7 +106,7 @@ class SwipeToDeleteCallback(
|
|||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int {
|
||||
return if (viewHolder is TabViewHolder) {
|
||||
return if (viewHolder is TabViewHolder || viewHolder is TabInCollectionViewHolder) {
|
||||
super.getSwipeDirs(recyclerView, viewHolder)
|
||||
} else 0
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/* 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.home.sessioncontrol.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class CollectionHeaderViewHolder(
|
||||
view: View
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.collection_header
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
/* 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.home.sessioncontrol.viewholders
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observer
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.collection_home_list_row.*
|
||||
import kotlinx.android.synthetic.main.collection_home_list_row.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import mozilla.components.browser.menu.BrowserMenu
|
||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||
import org.mozilla.fenix.DefaultThemeManager
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.increaseTapArea
|
||||
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import org.mozilla.fenix.home.sessioncontrol.onNext
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class CollectionViewHolder(
|
||||
val view: View,
|
||||
val actionEmitter: Observer<SessionControlAction>,
|
||||
val job: Job,
|
||||
override val containerView: View? = view
|
||||
) :
|
||||
RecyclerView.ViewHolder(view), LayoutContainer, CoroutineScope {
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.IO + job
|
||||
|
||||
private lateinit var collection: TabCollection
|
||||
private var state = CollectionState.Collapsed
|
||||
private var collectionMenu: CollectionItemMenu
|
||||
|
||||
init {
|
||||
collectionMenu = CollectionItemMenu(view.context) {
|
||||
when (it) {
|
||||
is CollectionItemMenu.Item.DeleteCollection -> actionEmitter.onNext(CollectionAction.Delete(collection))
|
||||
is CollectionItemMenu.Item.AddTab -> actionEmitter.onNext(CollectionAction.AddTab(collection))
|
||||
is CollectionItemMenu.Item.RenameCollection -> actionEmitter.onNext(CollectionAction.Rename(collection))
|
||||
is CollectionItemMenu.Item.OpenTabs -> actionEmitter.onNext(CollectionAction.OpenTabs(collection))
|
||||
}
|
||||
}
|
||||
|
||||
collection_overflow_button.run {
|
||||
increaseTapArea(buttonIncreaseDps)
|
||||
setOnClickListener {
|
||||
collectionMenu.menuBuilder
|
||||
.build(view.context)
|
||||
.show(anchor = it, orientation = BrowserMenu.Orientation.DOWN)
|
||||
}
|
||||
}
|
||||
|
||||
collection_share_button.run {
|
||||
increaseTapArea(buttonIncreaseDps)
|
||||
setOnClickListener {
|
||||
actionEmitter.onNext(CollectionAction.ShareTabs(collection))
|
||||
}
|
||||
}
|
||||
|
||||
view.setOnClickListener {
|
||||
updateState()
|
||||
}
|
||||
|
||||
view.collection_icon.setColorFilter(ContextCompat.getColor(
|
||||
view.context,
|
||||
getNextIconColor()),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
|
||||
fun bindSession(collection: TabCollection) {
|
||||
this.collection = collection
|
||||
updateCollectionUI()
|
||||
}
|
||||
|
||||
private fun updateCollectionUI() {
|
||||
view.collection_title.text = collection.title
|
||||
|
||||
var hostNameList = listOf<String>()
|
||||
|
||||
collection.tabs.forEach {
|
||||
hostNameList += it.hostname.capitalize()
|
||||
}
|
||||
|
||||
var tabsDisplayed = 0
|
||||
val titleList = hostNameList.joinToString(", ") {
|
||||
if (it.length > maxTitleLength) {
|
||||
it.substring(0,
|
||||
maxTitleLength
|
||||
) + "..."
|
||||
} else {
|
||||
tabsDisplayed += 1
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
view.collection_description.text = titleList
|
||||
|
||||
if (collection.expanded) {
|
||||
(view.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = 0
|
||||
collection_title.setPadding(0, 0, 0, EXPANDED_PADDING)
|
||||
view.background = ContextCompat.getDrawable(view.context, R.drawable.rounded_top_corners)
|
||||
view.collection_description.visibility = View.GONE
|
||||
view.expand_button.setImageDrawable(ContextCompat.getDrawable(view.context, R.drawable.ic_chevron_up))
|
||||
} else {
|
||||
(view.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = COLLAPSED_MARGIN
|
||||
view.background = ContextCompat.getDrawable(view.context, R.drawable.rounded_all_corners)
|
||||
view.collection_description.visibility = View.VISIBLE
|
||||
view.expand_button.setImageDrawable(ContextCompat.getDrawable(view.context, R.drawable.ic_chevron_down))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateState() {
|
||||
state = when (state) {
|
||||
CollectionState.Expanded -> {
|
||||
actionEmitter.onNext(CollectionAction.Collapse(collection))
|
||||
CollectionState.Collapsed
|
||||
}
|
||||
CollectionState.Collapsed -> {
|
||||
actionEmitter.onNext(CollectionAction.Expand(collection))
|
||||
CollectionState.Expanded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod", "MagicNumber")
|
||||
private fun getNextIconColor(): Int {
|
||||
with(view.context) {
|
||||
var sessionColorIndex = Settings.getInstance(this).preferences
|
||||
.getInt(getString(R.string.pref_key_collection_color), 0)
|
||||
|
||||
val iconResource = when (sessionColorIndex) {
|
||||
0 -> R.color.collection_icon_color_violet
|
||||
1 -> R.color.collection_icon_color_blue
|
||||
2 -> R.color.collection_icon_color_pink
|
||||
3 -> R.color.collection_icon_color_green
|
||||
4 -> R.color.collection_icon_color_yellow
|
||||
else -> R.color.white_color
|
||||
}
|
||||
|
||||
if (sessionColorIndex >= MAX_COLOR_INDEX) { sessionColorIndex = 0 } else { sessionColorIndex += 1 }
|
||||
|
||||
Settings.getInstance(this).preferences.edit()
|
||||
.putInt(getString(R.string.pref_key_collection_color), sessionColorIndex).apply()
|
||||
|
||||
return iconResource
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MAX_COLOR_INDEX = 4
|
||||
const val EXPANDED_PADDING = 60
|
||||
const val COLLAPSED_MARGIN = 12
|
||||
const val LAYOUT_ID = R.layout.collection_home_list_row
|
||||
const val maxTitleLength = 20
|
||||
const val buttonIncreaseDps = 24
|
||||
}
|
||||
|
||||
enum class CollectionState {
|
||||
Expanded, Collapsed
|
||||
}
|
||||
}
|
||||
|
||||
class CollectionItemMenu(
|
||||
private val context: Context,
|
||||
private val onItemTapped: (Item) -> Unit = {}
|
||||
) {
|
||||
sealed class Item {
|
||||
object DeleteCollection : Item()
|
||||
object AddTab : Item()
|
||||
object RenameCollection : Item()
|
||||
object OpenTabs : Item()
|
||||
}
|
||||
|
||||
val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
|
||||
|
||||
private val menuItems by lazy {
|
||||
listOf(
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.collection_delete),
|
||||
textColorResource = DefaultThemeManager.resolveAttribute(R.attr.destructive, context)
|
||||
) {
|
||||
onItemTapped.invoke(Item.DeleteCollection)
|
||||
},
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.add_tab)
|
||||
) {
|
||||
onItemTapped.invoke(Item.AddTab)
|
||||
},
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.collection_rename)
|
||||
) {
|
||||
onItemTapped.invoke(Item.RenameCollection)
|
||||
},
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.collection_open_tabs)
|
||||
) {
|
||||
onItemTapped.invoke(Item.OpenTabs)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/* 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.home.sessioncontrol.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class NoCollectionMessageViewHolder(
|
||||
view: View
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.no_collection_message
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/* 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.home.sessioncontrol.viewholders
|
||||
|
||||
import android.graphics.Outline
|
||||
import android.view.View
|
||||
import android.view.ViewOutlineProvider
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observer
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.tab_in_collection.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.icons.IconRequest
|
||||
import mozilla.components.support.ktx.android.content.res.pxToDp
|
||||
import org.jetbrains.anko.backgroundColor
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.getColorFromAttr
|
||||
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import org.mozilla.fenix.home.sessioncontrol.onNext
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class TabInCollectionViewHolder(
|
||||
val view: View,
|
||||
val actionEmitter: Observer<SessionControlAction>,
|
||||
val job: Job,
|
||||
override val containerView: View? = view
|
||||
) : RecyclerView.ViewHolder(view), LayoutContainer, CoroutineScope {
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.IO + job
|
||||
|
||||
lateinit var collection: TabCollection
|
||||
private set
|
||||
lateinit var tab: Tab
|
||||
private set
|
||||
var isLastTab = false
|
||||
|
||||
init {
|
||||
collection_tab_icon.clipToOutline = true
|
||||
collection_tab_icon.outlineProvider = object : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View?, outline: Outline?) {
|
||||
outline?.setRoundRect(
|
||||
0,
|
||||
0,
|
||||
view!!.width,
|
||||
view.height,
|
||||
view.context.resources.pxToDp(TabViewHolder.favIconBorderRadiusInPx).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
collection_tab_close_button.setOnClickListener {
|
||||
actionEmitter.onNext(CollectionAction.RemoveTab(collection, tab))
|
||||
}
|
||||
}
|
||||
|
||||
fun bindSession(collection: TabCollection, tab: Tab, isLastTab: Boolean) {
|
||||
this.collection = collection
|
||||
this.tab = tab
|
||||
this.isLastTab = isLastTab
|
||||
updateTabUI()
|
||||
}
|
||||
|
||||
private fun updateTabUI() {
|
||||
collection_tab_hostname.text = tab.hostname
|
||||
collection_tab_title.text = tab.title
|
||||
launch(Dispatchers.IO) {
|
||||
val bitmap = collection_tab_icon.context.components.utils.icons
|
||||
.loadIcon(IconRequest(tab.url)).await().bitmap
|
||||
launch(Dispatchers.Main) {
|
||||
collection_tab_icon.setImageBitmap(bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
// If I'm the last one...
|
||||
if (isLastTab) {
|
||||
view.background = ContextCompat.getDrawable(view.context, R.drawable.rounded_bottom_corners)
|
||||
divider_line.visibility = View.GONE
|
||||
} else {
|
||||
view.backgroundColor = R.attr.above.getColorFromAttr(view.context)
|
||||
divider_line.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.tab_in_collection
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.icons.IconRequest
|
||||
import mozilla.components.browser.menu.BrowserMenu
|
||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||
import mozilla.components.support.ktx.android.content.res.pxToDp
|
||||
|
@ -85,18 +84,18 @@ class TabViewHolder(
|
|||
setOnClickListener {
|
||||
tabMenu.menuBuilder
|
||||
.build(view.context)
|
||||
.show(anchor = it, orientation = BrowserMenu.Orientation.DOWN)
|
||||
.show(anchor = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bindSession(tab: Tab) {
|
||||
this.tab = tab
|
||||
updateText(tab)
|
||||
updateSelected(tab.selected)
|
||||
updateTabUI(tab)
|
||||
updateSelected(tab.selected ?: false)
|
||||
}
|
||||
|
||||
fun updateText(tab: Tab) {
|
||||
private fun updateTabUI(tab: Tab) {
|
||||
hostname.text = tab.hostname
|
||||
tab_title.text = tab.title
|
||||
launch(Dispatchers.IO) {
|
||||
|
|
|
@ -9,5 +9,5 @@
|
|||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M13,14.6449L14.2772,13.309C14.6588,12.9098 15.2918,12.8955 15.691,13.2772C16.0902,13.6588 16.1045,14.2918 15.7228,14.691L12.7903,17.7585C12.4026,18.1641 11.7571,18.1713 11.3604,17.7746L8.2929,14.7071C7.9024,14.3166 7.9024,13.6834 8.2929,13.2929C8.6834,12.9024 9.3166,12.9024 9.7071,13.2929L11,14.5858L11,11C11,10.4477 11.4477,10 12,10C12.5523,10 13,10.4477 13,11L13,14.6449ZM20,8L4,8L4,19C4,19.5523 4.4477,20 5,20L19,20C19.5523,20 20,19.5523 20,19L20,8ZM18.7908,6L17.333,4.3401C17.1432,4.1239 16.8694,4 16.5817,4L7.8284,4C7.5632,4 7.3089,4.1054 7.1213,4.2929L5.4142,6L18.7908,6ZM2,19L2,7.8284C2,7.0328 2.3161,6.2697 2.8787,5.7071L5.7071,2.8787C6.2697,2.3161 7.0328,2 7.8284,2L16.5817,2C17.4448,2 18.2662,2.3718 18.8358,3.0203L21.2541,5.7739C21.7349,6.3213 22,7.025 22,7.7536L22,19C22,20.6569 20.6569,22 19,22L5,22C3.3431,22 2,20.6569 2,19Z"
|
||||
android:fillColor="?foundation" />
|
||||
android:fillColor="?primaryText" />
|
||||
</vector>
|
||||
|
|
10
app/src/main/res/drawable/ic_chevron_down.xml
Normal file
10
app/src/main/res/drawable/ic_chevron_down.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<vector android:height="6dp" android:viewportHeight="6"
|
||||
android:viewportWidth="10" android:width="10dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?primaryText" android:fillType="nonZero"
|
||||
android:pathData="M5,5.6667C4.8232,5.6666 4.6537,5.5964 4.5287,5.4713L0.5287,1.4713C0.276,1.2097 0.2796,0.7939 0.5368,0.5368C0.7939,0.2796 1.2097,0.276 1.4713,0.5287L5,4.0573L8.5287,0.5287C8.7903,0.276 9.2061,0.2796 9.4632,0.5368C9.7204,0.7939 9.724,1.2097 9.4713,1.4713L5.4713,5.4713C5.3463,5.5964 5.1768,5.6666 5,5.6667Z"
|
||||
android:strokeColor="#00000000" android:strokeWidth="1"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_chevron_up.xml
Normal file
10
app/src/main/res/drawable/ic_chevron_up.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<vector android:height="6dp" android:viewportHeight="6"
|
||||
android:viewportWidth="10" android:width="10dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?primaryText" android:fillType="nonZero"
|
||||
android:pathData="M5,0.3333C4.8232,0.3334 4.6537,0.4036 4.5287,0.5287L0.5287,4.5287C0.276,4.7903 0.2796,5.2061 0.5368,5.4632C0.7939,5.7204 1.2097,5.724 1.4713,5.4713L5,1.9427L8.5287,5.4713C8.7903,5.724 9.2061,5.7204 9.4632,5.4632C9.7204,5.2061 9.724,4.7903 9.4713,4.5287L5.4713,0.5287C5.3463,0.4036 5.1768,0.3334 5,0.3333Z"
|
||||
android:strokeColor="#00000000" android:strokeWidth="1"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_hollow_share.xml
Normal file
10
app/src/main/res/drawable/ic_hollow_share.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<vector android:height="20dp" android:viewportHeight="20"
|
||||
android:viewportWidth="19" android:width="20dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?primaryText" android:fillType="nonZero"
|
||||
android:pathData="M6.92,12.7338C6.1902,13.5131 5.1519,14 4,14C1.7909,14 0,12.2091 0,10C0,7.7909 1.7909,6 4,6C5.0609,6 6.0783,6.4214 6.8284,7.1716C6.86,7.2031 6.8909,7.2351 6.9213,7.2676L11.1206,4.9785C11.0418,4.6654 11,4.3376 11,4C11,1.7909 12.7909,0 15,0C17.2091,0 19,1.7909 19,4C19,6.2091 17.2091,8 15,8C13.8481,8 12.8098,7.5131 12.08,6.7338L7.879,9.0237C7.9587,9.3403 8,9.668 8,10C8,10.3376 7.9582,10.6654 7.8794,10.9785L12.0787,13.2675C12.8087,12.4875 13.8474,12 15,12C17.2091,12 19,13.7909 19,16C19,18.2091 17.2091,20 15,20C12.7909,20 11,18.2091 11,16C11,15.6631 11.0417,15.3358 11.1201,15.0232L6.92,12.7338ZM15,2C13.8954,2 13,2.8954 13,4C13,5.1046 13.8954,6 15,6C16.1046,6 17,5.1046 17,4C17,2.8954 16.1046,2 15,2ZM4,8C2.8954,8 2,8.8954 2,10C2,11.1046 2.8954,12 4,12C5.1046,12 6,11.1046 6,10C6,8.8954 5.1046,8 4,8ZM15,14C13.8954,14 13,14.8954 13,16C13,17.1046 13.8954,18 15,18C16.1046,18 17,17.1046 17,16C17,14.8954 16.1046,14 15,14Z"
|
||||
android:strokeColor="#00000000" android:strokeWidth="1"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/rounded_all_corners.xml
Normal file
9
app/src/main/res/drawable/rounded_all_corners.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?above" />
|
||||
<corners android:radius="@dimen/tab_corner_radius"/>
|
||||
</shape>
|
9
app/src/main/res/drawable/rounded_bottom_corners.xml
Normal file
9
app/src/main/res/drawable/rounded_bottom_corners.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?above" />
|
||||
<corners android:bottomLeftRadius="@dimen/tab_corner_radius" android:bottomRightRadius="@dimen/tab_corner_radius" />
|
||||
</shape>
|
9
app/src/main/res/drawable/rounded_top_corners.xml
Normal file
9
app/src/main/res/drawable/rounded_top_corners.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?above" />
|
||||
<corners android:topLeftRadius="@dimen/tab_corner_radius" android:topRightRadius="@dimen/tab_corner_radius" />
|
||||
</shape>
|
|
@ -5,6 +5,6 @@
|
|||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="8dp" />
|
||||
<corners android:radius="@dimen/tab_corner_radius" />
|
||||
<solid android:color="@color/photonGrey30" />
|
||||
</shape>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:bottomLeftRadius="@dimen/tab_corner_radius" android:bottomRightRadius="@dimen/tab_corner_radius" />
|
||||
<solid android:color="@color/photonGrey30" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/photonGrey30" />
|
||||
</shape>
|
33
app/src/main/res/layout/collection_header.xml
Normal file
33
app/src/main/res/layout/collection_header.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/collections_header"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_line"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?neutralFaded"
|
||||
android:layout_marginStart="23dp"
|
||||
android:layout_marginEnd="23dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/collections_header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/collections_header"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
android:layout_marginTop="15dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/divider_line" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
95
app/src/main/res/layout/collection_home_list_row.xml
Normal file
95
app/src/main/res/layout/collection_home_list_row.xml
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/item_collection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="@drawable/rounded_all_corners"
|
||||
android:elevation="5dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/collection_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="18dp"
|
||||
android:tint="@null"
|
||||
android:src="@drawable/ic_archive"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/collection_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="17dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:minLines="1"
|
||||
android:textAppearance="@style/Header16TextStyle"
|
||||
app:layout_constraintStart_toEndOf="@id/collection_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/expand_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="26dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:src="@drawable/ic_chevron_down"
|
||||
app:layout_constraintStart_toEndOf="@id/collection_title"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/collection_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:minLines="2"
|
||||
android:textAppearance="@style/SubtitleTextStyle"
|
||||
app:layout_constraintStart_toStartOf="@id/collection_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/collection_share_button"
|
||||
app:layout_constraintEnd_toStartOf="@id/collection_share_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/collection_share_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/tab_menu"
|
||||
android:src="@drawable/ic_hollow_share"
|
||||
android:layout_marginEnd="29dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/collection_overflow_button"
|
||||
app:layout_constraintTop_toTopOf="@id/collection_icon"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/collection_overflow_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/tab_menu"
|
||||
android:src="@drawable/ic_menu"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/collection_icon"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
|
@ -27,7 +27,7 @@
|
|||
android:focusable="false"
|
||||
android:textStyle="bold"
|
||||
android:gravity="center"
|
||||
android:text="@string/session_delete"
|
||||
android:text="@string/collection_delete"
|
||||
android:textColor="?contrastText"
|
||||
android:textSize="16sp" />
|
||||
</FrameLayout>
|
35
app/src/main/res/layout/no_collection_message.xml
Normal file
35
app/src/main/res/layout/no_collection_message.xml
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/no_tabs_wrapper"
|
||||
android:background="@drawable/empty_session_control_background"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:padding="16dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_collection_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableEnd="@drawable/ic_archive"
|
||||
android:drawableTint="?primaryText"
|
||||
android:drawablePadding="8dp"
|
||||
android:text="@string/no_collections_header"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_collection_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/no_collections_description"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="normal" />
|
||||
</LinearLayout>
|
|
@ -33,7 +33,7 @@
|
|||
android:contentDescription="@string/current_session_image"
|
||||
android:paddingBottom="20dp"
|
||||
android:src="@drawable/ic_session_thumbnail_placeholder_greyscale"
|
||||
android:tint="@color/session_placeholder_blue"
|
||||
android:tint="@color/collection_icon_color_blue"
|
||||
android:tintMode="multiply"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
|
84
app/src/main/res/layout/tab_in_collection.xml
Normal file
84
app/src/main/res/layout/tab_in_collection.xml
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/tab_in_collection_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="?above"
|
||||
android:elevation="5dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/collection_tab_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginTop="25dp"
|
||||
android:layout_marginStart="18dp"
|
||||
android:tint="@null"
|
||||
android:src="@drawable/ic_archive"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/collection_tab_hostname"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:minLines="1"
|
||||
android:textAppearance="@style/Header12TextStyle"
|
||||
app:layout_constraintStart_toEndOf="@id/collection_tab_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/collection_tab_close_button"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/collection_tab_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:minLines="2"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
app:layout_constraintStart_toStartOf="@id/collection_tab_hostname"
|
||||
app:layout_constraintTop_toBottomOf="@id/collection_tab_hostname"
|
||||
app:layout_constraintEnd_toEndOf="@id/collection_tab_hostname"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/collection_tab_close_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/close_tab"
|
||||
android:src="@drawable/ic_close"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:alpha="0.8"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_line"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?neutralFaded"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
|
@ -7,7 +7,7 @@
|
|||
android:id="@+id/item_tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
|
@ -40,9 +40,7 @@
|
|||
android:layout_marginStart="8dp"
|
||||
android:ellipsize="none"
|
||||
android:singleLine="true"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:textAppearance="@style/Header12TextStyle"
|
||||
app:layout_constraintEnd_toStartOf="@id/close_tab_button"
|
||||
app:layout_constraintStart_toEndOf="@id/favicon_image"
|
||||
app:layout_constraintTop_toTopOf="@id/favicon_image" />
|
||||
|
@ -58,7 +56,7 @@
|
|||
android:maxLines="2"
|
||||
android:minLines="2"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="15sp"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toEndOf="@id/favicon_image"
|
||||
app:layout_constraintEnd_toEndOf="@id/hostname"
|
||||
app:layout_constraintTop_toBottomOf="@id/hostname"
|
||||
|
|
|
@ -74,12 +74,12 @@
|
|||
<!-- Bookmark buttons -->
|
||||
<color name="bookmark_favicon_background">#DFDFE3</color>
|
||||
|
||||
<!-- Session placeholder icons-->
|
||||
<color name="session_placeholder_blue">#00B3F4</color>
|
||||
<color name="session_placeholder_orange">#FF8A50</color>
|
||||
<color name="session_placeholder_green">#54FFBD</color>
|
||||
<color name="session_placeholder_purple">#AB71FF</color>
|
||||
<color name="session_placeholder_pink">#FF4AA2</color>
|
||||
<!-- Collection icons-->
|
||||
<color name="collection_icon_color_violet">#7542E5</color>
|
||||
<color name="collection_icon_color_blue">#0250BB</color>
|
||||
<color name="collection_icon_color_pink">#E31587</color>
|
||||
<color name="collection_icon_color_green">#2AC3A2</color>
|
||||
<color name="collection_icon_color_yellow">#E27F2E</color>
|
||||
|
||||
<!-- Library buttons -->
|
||||
<color name="library_sessions_icon_background">#B9F0FD</color>
|
||||
|
|
|
@ -65,4 +65,6 @@
|
|||
<string name="pref_key_tracking_protection_settings" translatable="false">pref_key_tracking_protection_settings</string>
|
||||
<string name="pref_key_tracking_protection" translatable="false">pref_key_tracking_protection</string>
|
||||
<string name="pref_key_tracking_protection_exceptions" translatable="false">pref_key_tracking_protection_exceptions</string>
|
||||
|
||||
<string name="pref_key_collection_color" translatable="false">pref_key_collection_color</string>
|
||||
</resources>
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
- 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/. -->
|
||||
<resources>
|
||||
|
||||
|
||||
<!-- Home Fragment -->
|
||||
<!-- Content description (not visible, for screen readers etc.): "Three dot" menu button. -->
|
||||
<string name="content_description_menu">More options</string>
|
||||
|
@ -269,8 +267,12 @@
|
|||
<string name="current_session_image">Current session image</string>
|
||||
<!-- Button to save the current set of tabs into a collection -->
|
||||
<string name="save_to_collection">Save to collection</string>
|
||||
<!-- Text for the button to delete a session -->
|
||||
<string name="session_delete">Delete session</string>
|
||||
<!-- Text for the menu button to delete a collection -->
|
||||
<string name="collection_delete">Delete collection</string>
|
||||
<!-- Text for the menu button to rename a collection -->
|
||||
<string name="collection_rename">Rename collection</string>
|
||||
<!-- Text for the button to open tabs of the selected collection -->
|
||||
<string name="collection_open_tabs">Open tabs</string>
|
||||
<!-- Text for the button to delete a single session -->
|
||||
<string name="session_item_delete">Delete</string>
|
||||
<!-- Text to tell the user how many more tabs this session has.
|
||||
|
@ -383,11 +385,9 @@
|
|||
<!-- Message for copying the URL via long press on the toolbar -->
|
||||
<string name="url_copied">URL copied</string>
|
||||
|
||||
|
||||
<!-- Site Permissions -->
|
||||
<!-- Button label that take the user to the Android App setting -->
|
||||
<string name="phone_feature_go_to_settings">Go to Settings</string>
|
||||
|
||||
<!-- Content description (not visible, for screen readers etc.): Quick settings sheet
|
||||
to give users access to site specific information / settings. For example:
|
||||
Secure settings status and a button to modify site permissions -->
|
||||
|
@ -427,6 +427,13 @@
|
|||
<!-- Summary of tracking protection preference if tracking protection is set to off -->
|
||||
<string name="tracking_protection_off">Off</string>
|
||||
|
||||
<!-- Collections -->
|
||||
<!-- Collections header on home fragment -->
|
||||
<string name="collections_header">Collections</string>
|
||||
<!-- No Open Tabs Message Header -->
|
||||
<string name="no_collections_header">No collections</string>
|
||||
<!-- No Open Tabs Message Description -->
|
||||
<string name="no_collections_description">Your collections will be shown here.</string>
|
||||
|
||||
<!-- Title for the "select tabs" step of the collection creator -->
|
||||
<string name="create_collection_select_tabs">Select Tabs</string>
|
||||
|
@ -442,7 +449,6 @@
|
|||
|
||||
<!-- Button to select all tabs in the "select tabs" step of the collection creator -->
|
||||
<string name="create_collection_select_all">Select All</string>
|
||||
|
||||
<!-- Text to prompt users to select the tabs to save in the "select tabs" stepof the collection creator -->
|
||||
<string name="create_collection_save_to_collection_empty">Select tabs to save</string>
|
||||
|
||||
|
|
|
@ -166,12 +166,34 @@
|
|||
<item name="android:letterSpacing">0.03</item>
|
||||
</style>
|
||||
|
||||
<style name="Header16TextStyle" parent="TextAppearance.MaterialComponents.Body1">
|
||||
<item name="android:textColor">?primaryText</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="Header14TextStyle" parent="TextAppearance.MaterialComponents.Body2">
|
||||
<item name="android:textColor">?primaryText</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="Header12TextStyle" parent="TextAppearance.MaterialComponents.Body2">
|
||||
<item name="android:textColor">?secondaryText</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="Body14TextStyle" parent="TextAppearance.MaterialComponents.Body2">
|
||||
<item name="android:textColor">?primaryText</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
||||
<style name="SubtitleTextStyle" parent="TextAppearance.MaterialComponents.Body1">
|
||||
<item name="android:textColor">?secondaryText</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
||||
<style name="ToolbarTitleTextStyle" parent="HeaderTextStyle">
|
||||
<item name="android:textSize">20sp</item>
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue
Block a user