[fenix] For https://github.com/mozilla-mobile/fenix/issues/4007 - Refactor AppShareView in standalone Share Views
In an effort to respect the initial MVI architecture I've broken the complex `AppShareView` in 3 separate Views - `ShareCloseView` - `ShareToAccountDevicesView` - `ShareToAppsView` They are standalone Views (extending LayoutContainer) which know nothing about each other or their parent and so offer their container the possibility to order or display them in any form later. According to the lib-state contract they are only responsible to - inflate themselves in their injected containerView - render a certain state (to be added in later commits) - delegate all user interaction to an associated Interactorpull/600/head
parent
4168f278f2
commit
a8cbf8b1d8
@ -1,150 +0,0 @@
|
||||
/* 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.share
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff.Mode.SRC_IN
|
||||
import android.view.LayoutInflater
|
||||
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.synthetic.main.account_share_list_item.view.*
|
||||
import mozilla.components.concept.sync.Device
|
||||
import mozilla.components.concept.sync.DeviceCapability
|
||||
import mozilla.components.concept.sync.DeviceType
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
class AccountDevicesShareAdapter(
|
||||
private val context: Context,
|
||||
val actionEmitter: Observer<ShareAction>
|
||||
) : RecyclerView.Adapter<AccountDeviceViewHolder>() {
|
||||
|
||||
private val devices = buildDeviceList()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountDeviceViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(AccountDeviceViewHolder.LAYOUT_ID, parent, false)
|
||||
return AccountDeviceViewHolder(view, actionEmitter)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = devices.size
|
||||
|
||||
override fun onBindViewHolder(holder: AccountDeviceViewHolder, position: Int) {
|
||||
holder.bind(devices[position])
|
||||
}
|
||||
|
||||
private fun buildDeviceList(): List<SyncShareOption> {
|
||||
val list = mutableListOf<SyncShareOption>()
|
||||
val accountManager = context.components.backgroundServices.accountManager
|
||||
|
||||
if (accountManager.authenticatedAccount() == null) {
|
||||
list.add(SyncShareOption.SignIn)
|
||||
return list
|
||||
}
|
||||
|
||||
accountManager.authenticatedAccount()?.deviceConstellation()?.state()?.otherDevices?.let { devices ->
|
||||
val shareableDevices = devices.filter { it.capabilities.contains(DeviceCapability.SEND_TAB) }
|
||||
|
||||
if (shareableDevices.isEmpty()) {
|
||||
list.add(SyncShareOption.AddNewDevice)
|
||||
}
|
||||
|
||||
val shareOptions = shareableDevices.map {
|
||||
when (it.deviceType) {
|
||||
DeviceType.MOBILE -> SyncShareOption.Mobile(it.displayName, it)
|
||||
else -> SyncShareOption.Desktop(it.displayName, it)
|
||||
}
|
||||
}
|
||||
list.addAll(shareOptions)
|
||||
|
||||
if (shareableDevices.size > 1) {
|
||||
list.add(SyncShareOption.SendAll(shareableDevices))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
class AccountDeviceViewHolder(
|
||||
itemView: View,
|
||||
actionEmitter: Observer<ShareAction>
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private val context: Context = itemView.context
|
||||
private var action: ShareAction? = null
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener {
|
||||
action?.let { actionEmitter.onNext(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(option: SyncShareOption) {
|
||||
val (name, drawableRes, colorRes) = when (option) {
|
||||
SyncShareOption.SignIn -> {
|
||||
action = ShareAction.SignInClicked
|
||||
Triple(
|
||||
context.getText(R.string.sync_sign_in),
|
||||
R.drawable.mozac_ic_sync,
|
||||
R.color.default_share_background
|
||||
)
|
||||
}
|
||||
SyncShareOption.AddNewDevice -> {
|
||||
action = ShareAction.AddNewDeviceClicked
|
||||
Triple(
|
||||
context.getText(R.string.sync_connect_device),
|
||||
R.drawable.mozac_ic_new,
|
||||
R.color.default_share_background
|
||||
)
|
||||
}
|
||||
is SyncShareOption.SendAll -> {
|
||||
action = ShareAction.SendAllClicked(option.devices)
|
||||
Triple(
|
||||
context.getText(R.string.sync_send_to_all),
|
||||
R.drawable.mozac_ic_select_all,
|
||||
R.color.default_share_background
|
||||
)
|
||||
}
|
||||
is SyncShareOption.Mobile -> {
|
||||
action = ShareAction.ShareDeviceClicked(option.device)
|
||||
Triple(
|
||||
option.name,
|
||||
R.drawable.mozac_ic_device_mobile,
|
||||
R.color.device_type_mobile_background
|
||||
)
|
||||
}
|
||||
is SyncShareOption.Desktop -> {
|
||||
action = ShareAction.ShareDeviceClicked(option.device)
|
||||
Triple(
|
||||
option.name,
|
||||
R.drawable.mozac_ic_device_desktop,
|
||||
R.color.device_type_desktop_background
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
itemView.deviceIcon.apply {
|
||||
setImageResource(drawableRes)
|
||||
background.setColorFilter(ContextCompat.getColor(context, colorRes), SRC_IN)
|
||||
drawable.setTint(ContextCompat.getColor(context, R.color.device_foreground))
|
||||
}
|
||||
itemView.deviceName.text = name
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.account_share_list_item
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SyncShareOption {
|
||||
object SignIn : SyncShareOption()
|
||||
object AddNewDevice : SyncShareOption()
|
||||
data class SendAll(val devices: List<Device>) : SyncShareOption()
|
||||
data class Mobile(val name: String, val device: Device) : SyncShareOption()
|
||||
data class Desktop(val name: String, val device: Device) : SyncShareOption()
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/* 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.share
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.ACTION_SEND
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observer
|
||||
import kotlinx.android.synthetic.main.app_share_list_item.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class AppShareAdapter(
|
||||
private val context: Context,
|
||||
val actionEmitter: Observer<ShareAction>,
|
||||
private val intentType: String = "text/plain"
|
||||
) : RecyclerView.Adapter<AppShareItemViewHolder>() {
|
||||
|
||||
private var scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
private var size: Int = 0
|
||||
private val shareItems: MutableList<ShareItem> = mutableListOf()
|
||||
|
||||
init {
|
||||
val testIntent = Intent(ACTION_SEND).apply {
|
||||
type = intentType
|
||||
flags = FLAG_ACTIVITY_NEW_TASK
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val activities = context.packageManager.queryIntentActivities(testIntent, 0)
|
||||
|
||||
val items = activities.map { resolveInfo ->
|
||||
ShareItem(
|
||||
resolveInfo.loadLabel(context.packageManager).toString(),
|
||||
resolveInfo.loadIcon(context.packageManager),
|
||||
resolveInfo.activityInfo.packageName,
|
||||
resolveInfo.activityInfo.name
|
||||
)
|
||||
}
|
||||
|
||||
size = activities.size
|
||||
shareItems.addAll(items)
|
||||
|
||||
// Notify adapter on the UI thread when the dataset is populated.
|
||||
withContext(Dispatchers.Main) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppShareItemViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(AppShareItemViewHolder.LAYOUT_ID, parent, false)
|
||||
return AppShareItemViewHolder(view, actionEmitter)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = size
|
||||
|
||||
override fun onBindViewHolder(holder: AppShareItemViewHolder, position: Int) {
|
||||
holder.bind(shareItems[position])
|
||||
}
|
||||
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onDetachedFromRecyclerView(recyclerView)
|
||||
scope.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
class AppShareItemViewHolder(
|
||||
itemView: View,
|
||||
actionEmitter: Observer<ShareAction>
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private var shareItem: ShareItem? = null
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener {
|
||||
shareItem?.let {
|
||||
actionEmitter.onNext(ShareAction.ShareAppClicked(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun bind(item: ShareItem) {
|
||||
shareItem = item
|
||||
itemView.appName.text = item.name
|
||||
itemView.appIcon.setImageDrawable(item.icon)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.app_share_list_item
|
||||
}
|
||||
}
|
||||
|
||||
data class ShareItem(val name: String, val icon: Drawable, val packageName: String, val activityName: String)
|
@ -0,0 +1,30 @@
|
||||
/* 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.share
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.share_close.*
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
/**
|
||||
* Callbacks for possible user interactions on the [ShareCloseView]
|
||||
*/
|
||||
interface ShareCloseInteractor {
|
||||
fun onShareClosed()
|
||||
}
|
||||
|
||||
class ShareCloseView(
|
||||
override val containerView: ViewGroup,
|
||||
private val interactor: ShareCloseInteractor
|
||||
) : LayoutContainer {
|
||||
init {
|
||||
LayoutInflater.from(containerView.context)
|
||||
.inflate(R.layout.share_close, containerView, true)
|
||||
|
||||
closeButton.setOnClickListener { interactor.onShareClosed() }
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/* 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.share
|
||||
|
||||
import android.view.ViewGroup
|
||||
import mozilla.components.concept.sync.Device
|
||||
import org.mozilla.fenix.mvi.Action
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
import org.mozilla.fenix.mvi.Reducer
|
||||
import org.mozilla.fenix.mvi.UIComponent
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
|
||||
object ShareState : ViewState
|
||||
|
||||
sealed class ShareChange : Change
|
||||
|
||||
sealed class ShareAction : Action {
|
||||
object Close : ShareAction()
|
||||
object SignInClicked : ShareAction()
|
||||
object AddNewDeviceClicked : ShareAction()
|
||||
data class ShareDeviceClicked(val device: Device) : ShareAction()
|
||||
data class SendAllClicked(val devices: List<Device>) : ShareAction()
|
||||
data class ShareAppClicked(val item: ShareItem) : ShareAction()
|
||||
}
|
||||
|
||||
class ShareComponent(
|
||||
private val container: ViewGroup,
|
||||
bus: ActionBusFactory,
|
||||
viewModelProvider: UIComponentViewModelProvider<ShareState, ShareChange>
|
||||
) : UIComponent<ShareState, ShareAction, ShareChange>(
|
||||
bus.getManagedEmitter(ShareAction::class.java),
|
||||
bus.getSafeManagedObservable(ShareChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
override fun initView() = ShareUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
init {
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
class ShareUIViewModel(
|
||||
initialState: ShareState
|
||||
) : UIComponentViewModelBase<ShareState, ShareChange>(
|
||||
initialState,
|
||||
reducer
|
||||
) {
|
||||
companion object {
|
||||
val reducer: Reducer<ShareState, ShareChange> = { _, _ -> ShareState }
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/* 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.share
|
||||
|
||||
import mozilla.components.concept.sync.Device
|
||||
import org.mozilla.fenix.share.listadapters.Application
|
||||
|
||||
/**
|
||||
* Interactor for the share screen.
|
||||
*/
|
||||
class ShareInteractor : ShareCloseInteractor, ShareToAccountDevicesInteractor, ShareToAppsInteractor {
|
||||
override fun onShareClosed() {
|
||||
TODO("not yet!? implemented")
|
||||
}
|
||||
|
||||
override fun onSignIn() {
|
||||
TODO("not yet!? implemented")
|
||||
}
|
||||
|
||||
override fun onAddNewDevice() {
|
||||
TODO("not yet!? implemented")
|
||||
}
|
||||
|
||||
override fun onShareToDevice(device: Device) {
|
||||
TODO("not yet!? implemented")
|
||||
}
|
||||
|
||||
override fun onShareToAllDevices(devices: List<Device>) {
|
||||
TODO("not yet!? implemented")
|
||||
}
|
||||
|
||||
override fun onShareToApp(appToShareTo: Application) {
|
||||
TODO("not yet!? implemented")
|
||||
}
|
||||
}
|
@ -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.share
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import mozilla.components.concept.sync.Device
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
/**
|
||||
* Callbacks for possible user interactions on the [ShareToAccountDevicesView]
|
||||
*/
|
||||
interface ShareToAccountDevicesInteractor {
|
||||
fun onSignIn()
|
||||
fun onAddNewDevice()
|
||||
fun onShareToDevice(device: Device)
|
||||
fun onShareToAllDevices(devices: List<Device>)
|
||||
}
|
||||
|
||||
class ShareToAccountDevicesView(
|
||||
override val containerView: ViewGroup,
|
||||
private val interactor: ShareToAccountDevicesInteractor
|
||||
) : LayoutContainer {
|
||||
init {
|
||||
LayoutInflater.from(containerView.context)
|
||||
.inflate(R.layout.share_to_account_devices, containerView, true)
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/* 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.share
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.share.listadapters.Application
|
||||
|
||||
/**
|
||||
* Callbacks for possible user interactions on the [ShareCloseView]
|
||||
*/
|
||||
interface ShareToAppsInteractor {
|
||||
fun onShareToApp(appToShareTo: Application)
|
||||
}
|
||||
|
||||
class ShareToAppsView(
|
||||
override val containerView: ViewGroup,
|
||||
private val interactor: ShareToAppsInteractor
|
||||
) : LayoutContainer {
|
||||
init {
|
||||
LayoutInflater.from(containerView.context)
|
||||
.inflate(R.layout.share_to_apps, containerView, true)
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/* 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.share
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.functions.Consumer
|
||||
import kotlinx.android.synthetic.main.component_share.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
|
||||
class ShareUIView(
|
||||
container: ViewGroup,
|
||||
actionEmitter: Observer<ShareAction>,
|
||||
changesObservable: Observable<ShareChange>
|
||||
) : UIView<ShareState, ShareAction, ShareChange>(
|
||||
container,
|
||||
actionEmitter,
|
||||
changesObservable
|
||||
) {
|
||||
override val view: View = LayoutInflater.from(container.context)
|
||||
.inflate(R.layout.component_share, container, true)
|
||||
|
||||
init {
|
||||
val adapter = AppShareAdapter(view.context, actionEmitter).also {
|
||||
it.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
progressBar.visibility = View.GONE
|
||||
appsList.visibility = View.VISIBLE
|
||||
}
|
||||
})
|
||||
}
|
||||
appsList.adapter = adapter
|
||||
|
||||
if (view.context.components.backgroundServices.accountManager.accountNeedsReauth()) {
|
||||
sendTabGroup.visibility = View.GONE
|
||||
accountHeaderText.visibility = View.GONE
|
||||
} else {
|
||||
devicesList.adapter = AccountDevicesShareAdapter(view.context, actionEmitter)
|
||||
}
|
||||
|
||||
container.setOnClickListener { actionEmitter.onNext(ShareAction.Close) }
|
||||
closeButton.setOnClickListener { actionEmitter.onNext(ShareAction.Close) }
|
||||
}
|
||||
|
||||
override fun updateView() = Consumer<ShareState> {
|
||||
ShareState
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/* 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.share.listadapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.concept.sync.Device
|
||||
import org.mozilla.fenix.share.ShareToAccountDevicesInteractor
|
||||
import org.mozilla.fenix.share.viewholders.AccountDeviceViewHolder
|
||||
|
||||
class AccountDevicesShareAdapter(
|
||||
private val interactor: ShareToAccountDevicesInteractor,
|
||||
private val devices: MutableList<SyncShareOption> = mutableListOf()
|
||||
) : RecyclerView.Adapter<AccountDeviceViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountDeviceViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(AccountDeviceViewHolder.LAYOUT_ID, parent, false)
|
||||
|
||||
return AccountDeviceViewHolder(view, interactor)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = devices.size
|
||||
|
||||
override fun onBindViewHolder(holder: AccountDeviceViewHolder, position: Int) {
|
||||
holder.bind(devices[position])
|
||||
}
|
||||
|
||||
fun updateData(deviceOptions: List<SyncShareOption>) {
|
||||
this.devices.clear()
|
||||
this.devices.addAll(deviceOptions)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SyncShareOption {
|
||||
object SignIn : SyncShareOption()
|
||||
object AddNewDevice : SyncShareOption()
|
||||
data class SendAll(val devices: List<Device>) : SyncShareOption()
|
||||
data class Mobile(val name: String, val device: Device) : SyncShareOption()
|
||||
data class Desktop(val name: String, val device: Device) : SyncShareOption()
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/* 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.share.listadapters
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.share.ShareToAppsInteractor
|
||||
import org.mozilla.fenix.share.viewholders.AppViewHolder
|
||||
|
||||
class AppShareAdapter(
|
||||
private val interactor: ShareToAppsInteractor,
|
||||
private val applications: MutableList<AppShareOption> = mutableListOf()
|
||||
) : RecyclerView.Adapter<AppViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(AppViewHolder.LAYOUT_ID, parent, false)
|
||||
|
||||
return AppViewHolder(view, interactor)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = applications.size
|
||||
|
||||
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
|
||||
holder.bind(applications[position])
|
||||
}
|
||||
|
||||
fun updateData(applications: List<AppShareOption>) {
|
||||
this.applications.clear()
|
||||
this.applications.addAll(applications)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
data class AppShareOption(
|
||||
val name: String,
|
||||
val icon: Drawable,
|
||||
val packageName: String,
|
||||
val activityName: String
|
||||
)
|
@ -0,0 +1,82 @@
|
||||
/* 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.share.viewholders
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.account_share_list_item.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.lib.Do
|
||||
import org.mozilla.fenix.share.ShareToAccountDevicesInteractor
|
||||
import org.mozilla.fenix.share.listadapters.SyncShareOption
|
||||
|
||||
class AccountDeviceViewHolder(
|
||||
itemView: View,
|
||||
private val interactor: ShareToAccountDevicesInteractor
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private val context: Context = itemView.context
|
||||
|
||||
fun bind(option: SyncShareOption) {
|
||||
bindClickListeners(option)
|
||||
bindView(option)
|
||||
}
|
||||
|
||||
private fun bindClickListeners(option: SyncShareOption) {
|
||||
itemView.setOnClickListener {
|
||||
Do exhaustive when (option) {
|
||||
SyncShareOption.SignIn -> interactor.onSignIn()
|
||||
SyncShareOption.AddNewDevice -> interactor.onAddNewDevice()
|
||||
is SyncShareOption.SendAll -> interactor.onShareToAllDevices(option.devices)
|
||||
is SyncShareOption.Mobile -> interactor.onShareToDevice(option.device)
|
||||
is SyncShareOption.Desktop -> interactor.onShareToDevice(option.device)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindView(option: SyncShareOption) {
|
||||
val (name, drawableRes, colorRes) = when (option) {
|
||||
SyncShareOption.SignIn -> Triple(
|
||||
context.getText(R.string.sync_sign_in),
|
||||
R.drawable.mozac_ic_sync,
|
||||
R.color.default_share_background
|
||||
)
|
||||
SyncShareOption.AddNewDevice -> Triple(
|
||||
context.getText(R.string.sync_connect_device),
|
||||
R.drawable.mozac_ic_new,
|
||||
R.color.default_share_background
|
||||
)
|
||||
is SyncShareOption.SendAll -> Triple(
|
||||
context.getText(R.string.sync_send_to_all),
|
||||
R.drawable.mozac_ic_select_all,
|
||||
R.color.default_share_background
|
||||
)
|
||||
is SyncShareOption.Mobile -> Triple(
|
||||
option.name,
|
||||
R.drawable.mozac_ic_device_mobile,
|
||||
R.color.device_type_mobile_background
|
||||
)
|
||||
is SyncShareOption.Desktop -> Triple(
|
||||
option.name,
|
||||
R.drawable.mozac_ic_device_desktop,
|
||||
R.color.device_type_desktop_background
|
||||
)
|
||||
}
|
||||
|
||||
itemView.deviceIcon.apply {
|
||||
setImageResource(drawableRes)
|
||||
background.setColorFilter(ContextCompat.getColor(context, colorRes), PorterDuff.Mode.SRC_IN)
|
||||
drawable.setTint(ContextCompat.getColor(context, R.color.device_foreground))
|
||||
}
|
||||
itemView.deviceName.text = name
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.account_share_list_item
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/* 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.share.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.app_share_list_item.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.share.ShareToAppsInteractor
|
||||
import org.mozilla.fenix.share.listadapters.AppShareOption
|
||||
|
||||
class AppViewHolder(
|
||||
itemView: View,
|
||||
interactor: ShareToAppsInteractor
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private var application: AppShareOption? = null
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener {
|
||||
application?.let { application ->
|
||||
interactor.onShareToApp(application)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(item: AppShareOption) {
|
||||
application = item
|
||||
itemView.appName.text = item.name
|
||||
itemView.appIcon.setImageDrawable(item.icon)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.app_share_list_item
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
<?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/collection_constraint_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bottom_sheet_dialog_fragment_background"
|
||||
android:backgroundTint="?above"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="76dp"
|
||||
android:layout_height="37dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/link_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/link_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/share_link_subheader"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/divider_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accountHeaderText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/share_device_subheader"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_line"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?neutralFaded"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/devicesList" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/sendTabGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="accountHeaderText,devicesList,divider_line" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/devicesList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/accountHeaderText" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/appsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/link_header"
|
||||
app:spanCount="2" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/closeButton"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/share_header"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
android:textColor="@color/neutral_text"
|
||||
android:textSize="20sp"
|
||||
app:icon="@drawable/mozac_ic_close"
|
||||
app:iconPadding="8dp"
|
||||
app:iconTint="@color/neutral_text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,24 @@
|
||||
<?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/. -->
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/closeButton"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/share_header"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
android:textColor="@color/neutral_text"
|
||||
android:textSize="20sp"
|
||||
app:icon="@drawable/mozac_ic_close"
|
||||
app:iconPadding="8dp"
|
||||
app:iconTint="@color/neutral_text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:backgroundTint="#000" />
|
@ -0,0 +1,41 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accountHeaderText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/share_device_subheader"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/devicesList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/accountHeaderText" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,54 @@
|
||||
<?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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="76dp"
|
||||
android:layout_height="37dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/link_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/link_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/share_link_subheader"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/appsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/link_header"
|
||||
app:spanCount="2" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue