parent
6b9a0d027f
commit
333ff8c941
|
@ -13,6 +13,9 @@ import kotlinx.android.synthetic.main.fragment_add_new_device.*
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.settings.SupportUtils
|
import org.mozilla.fenix.settings.SupportUtils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fragment to add a new device. Tabs can be shared to devices after they are added.
|
||||||
|
*/
|
||||||
class AddNewDeviceFragment : Fragment(R.layout.fragment_add_new_device) {
|
class AddNewDeviceFragment : Fragment(R.layout.fragment_add_new_device) {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|
|
@ -23,6 +23,7 @@ class ShareCloseView(
|
||||||
override val containerView: ViewGroup,
|
override val containerView: ViewGroup,
|
||||||
private val interactor: ShareCloseInteractor
|
private val interactor: ShareCloseInteractor
|
||||||
) : LayoutContainer {
|
) : LayoutContainer {
|
||||||
|
|
||||||
val adapter = ShareTabsAdapter()
|
val adapter = ShareTabsAdapter()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -36,6 +37,6 @@ class ShareCloseView(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTabs(tabs: List<ShareTab>) {
|
fun setTabs(tabs: List<ShareTab>) {
|
||||||
adapter.setTabs(tabs)
|
adapter.submitList(tabs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import org.mozilla.fenix.components.metrics.Event
|
||||||
import org.mozilla.fenix.ext.getRootView
|
import org.mozilla.fenix.ext.getRootView
|
||||||
import org.mozilla.fenix.ext.metrics
|
import org.mozilla.fenix.ext.metrics
|
||||||
import org.mozilla.fenix.ext.nav
|
import org.mozilla.fenix.ext.nav
|
||||||
import org.mozilla.fenix.share.listadapters.AppShareOption
|
import org.mozilla.fenix.share.listadapters.AndroidShareOption
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [ShareFragment] controller.
|
* [ShareFragment] controller.
|
||||||
|
@ -36,7 +36,7 @@ import org.mozilla.fenix.share.listadapters.AppShareOption
|
||||||
interface ShareController {
|
interface ShareController {
|
||||||
fun handleReauth()
|
fun handleReauth()
|
||||||
fun handleShareClosed()
|
fun handleShareClosed()
|
||||||
fun handleShareToApp(app: AppShareOption)
|
fun handleShareToApp(app: AndroidShareOption.App)
|
||||||
fun handleAddNewDevice()
|
fun handleAddNewDevice()
|
||||||
fun handleShareToDevice(device: Device)
|
fun handleShareToDevice(device: Device)
|
||||||
fun handleShareToAllDevices(devices: List<Device>)
|
fun handleShareToAllDevices(devices: List<Device>)
|
||||||
|
@ -72,7 +72,7 @@ class DefaultShareController(
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleShareToApp(app: AppShareOption) {
|
override fun handleShareToApp(app: AndroidShareOption.App) {
|
||||||
val intent = Intent(ACTION_SEND).apply {
|
val intent = Intent(ACTION_SEND).apply {
|
||||||
putExtra(EXTRA_TEXT, getShareText())
|
putExtra(EXTRA_TEXT, getShareText())
|
||||||
type = "text/plain"
|
type = "text/plain"
|
||||||
|
@ -116,9 +116,10 @@ class DefaultShareController(
|
||||||
private fun shareToDevicesWithRetry(shareOperation: () -> Deferred<Boolean>) {
|
private fun shareToDevicesWithRetry(shareOperation: () -> Deferred<Boolean>) {
|
||||||
// Use GlobalScope to allow the continuation of this method even if the share fragment is closed.
|
// Use GlobalScope to allow the continuation of this method even if the share fragment is closed.
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
when (shareOperation.invoke().await()) {
|
if (shareOperation.invoke().await()) {
|
||||||
true -> showSuccess()
|
showSuccess()
|
||||||
false -> showFailureWithRetryOption { shareToDevicesWithRetry(shareOperation) }
|
} else {
|
||||||
|
showFailureWithRetryOption { shareToDevicesWithRetry(shareOperation) }
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,19 @@ import android.os.Parcelable
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.appcompat.app.AppCompatDialogFragment
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
|
import androidx.core.content.getSystemService
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import androidx.navigation.fragment.navArgs
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.fragment_share.view.*
|
import kotlinx.android.synthetic.main.fragment_share.view.*
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import mozilla.components.concept.sync.DeviceCapability
|
import mozilla.components.concept.sync.DeviceCapability
|
||||||
import mozilla.components.concept.sync.DeviceType
|
|
||||||
import mozilla.components.feature.sendtab.SendTabUseCases
|
import mozilla.components.feature.sendtab.SendTabUseCases
|
||||||
import mozilla.components.service.fxa.manager.FxaAccountManager
|
import mozilla.components.service.fxa.manager.FxaAccountManager
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
@ -35,7 +37,7 @@ import org.mozilla.fenix.components.FenixSnackbarPresenter
|
||||||
import org.mozilla.fenix.ext.components
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.getRootView
|
import org.mozilla.fenix.ext.getRootView
|
||||||
import org.mozilla.fenix.ext.requireComponents
|
import org.mozilla.fenix.ext.requireComponents
|
||||||
import org.mozilla.fenix.share.listadapters.AppShareOption
|
import org.mozilla.fenix.share.listadapters.AndroidShareOption
|
||||||
import org.mozilla.fenix.share.listadapters.SyncShareOption
|
import org.mozilla.fenix.share.listadapters.SyncShareOption
|
||||||
|
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
|
@ -44,20 +46,39 @@ class ShareFragment : AppCompatDialogFragment() {
|
||||||
private lateinit var shareCloseView: ShareCloseView
|
private lateinit var shareCloseView: ShareCloseView
|
||||||
private lateinit var shareToAccountDevicesView: ShareToAccountDevicesView
|
private lateinit var shareToAccountDevicesView: ShareToAccountDevicesView
|
||||||
private lateinit var shareToAppsView: ShareToAppsView
|
private lateinit var shareToAppsView: ShareToAppsView
|
||||||
private lateinit var appsListDeferred: Deferred<List<AppShareOption>>
|
private lateinit var appsListDeferred: Deferred<List<AndroidShareOption>>
|
||||||
private lateinit var devicesListDeferred: Deferred<List<SyncShareOption>>
|
private lateinit var devicesListDeferred: Deferred<List<SyncShareOption>>
|
||||||
private var connectivityManager: ConnectivityManager? = null
|
private var connectivityManager: ConnectivityManager? = null
|
||||||
|
|
||||||
|
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||||
|
override fun onLost(network: Network?) = reloadDevices()
|
||||||
|
override fun onAvailable(network: Network?) = reloadDevices()
|
||||||
|
|
||||||
|
private fun reloadDevices() {
|
||||||
|
context?.let { context ->
|
||||||
|
val fxaAccountManager = context.components.backgroundServices.accountManager
|
||||||
|
lifecycleScope.launch {
|
||||||
|
fxaAccountManager.authenticatedAccount()
|
||||||
|
?.deviceConstellation()
|
||||||
|
?.refreshDevicesAsync()
|
||||||
|
?.await()
|
||||||
|
|
||||||
|
val devicesShareOptions = buildDeviceList(fxaAccountManager)
|
||||||
|
shareToAccountDevicesView.setShareTargets(devicesShareOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
super.onAttach(context)
|
super.onAttach(context)
|
||||||
|
|
||||||
connectivityManager =
|
connectivityManager = context.getSystemService()
|
||||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
|
|
||||||
val networkRequest = NetworkRequest.Builder().build()
|
val networkRequest = NetworkRequest.Builder().build()
|
||||||
connectivityManager?.registerNetworkCallback(networkRequest, networkCallback)
|
connectivityManager?.registerNetworkCallback(networkRequest, networkCallback)
|
||||||
|
|
||||||
// Start preparing the data as soon as we have a valid Context
|
// Start preparing the data as soon as we have a valid Context
|
||||||
appsListDeferred = lifecycleScope.async(Dispatchers.IO) {
|
appsListDeferred = lifecycleScope.async(IO) {
|
||||||
val shareIntent = Intent(ACTION_SEND).apply {
|
val shareIntent = Intent(ACTION_SEND).apply {
|
||||||
type = "text/plain"
|
type = "text/plain"
|
||||||
flags = FLAG_ACTIVITY_NEW_TASK
|
flags = FLAG_ACTIVITY_NEW_TASK
|
||||||
|
@ -66,53 +87,29 @@ class ShareFragment : AppCompatDialogFragment() {
|
||||||
buildAppsList(shareAppsActivities, context)
|
buildAppsList(shareAppsActivities, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
devicesListDeferred = lifecycleScope.async(Dispatchers.IO) {
|
devicesListDeferred = lifecycleScope.async(IO) {
|
||||||
val fxaAccountManager = context.components.backgroundServices.accountManager
|
val fxaAccountManager = context.components.backgroundServices.accountManager
|
||||||
buildDeviceList(fxaAccountManager)
|
buildDeviceList(fxaAccountManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setStyle(STYLE_NO_TITLE, R.style.ShareDialogStyle)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
|
|
||||||
override fun onLost(network: Network?) {
|
|
||||||
reloadDevices()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAvailable(network: Network?) {
|
|
||||||
reloadDevices()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun reloadDevices() {
|
|
||||||
context?.let {
|
|
||||||
val fxaAccountManager = it.components.backgroundServices.accountManager
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val refreshDevicesAsync =
|
|
||||||
fxaAccountManager.authenticatedAccount()?.deviceConstellation()
|
|
||||||
?.refreshDevicesAsync()
|
|
||||||
refreshDevicesAsync?.await()
|
|
||||||
val devicesShareOptions = buildDeviceList(fxaAccountManager)
|
|
||||||
shareToAccountDevicesView.setSharetargets(devicesShareOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetach() {
|
override fun onDetach() {
|
||||||
connectivityManager?.unregisterNetworkCallback(networkCallback)
|
connectivityManager?.unregisterNetworkCallback(networkCallback)
|
||||||
super.onDetach()
|
super.onDetach()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setStyle(STYLE_NO_TITLE, R.style.ShareDialogStyle)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
val view = inflater.inflate(R.layout.fragment_share, container, false)
|
val view = inflater.inflate(R.layout.fragment_share, container, false)
|
||||||
val args = ShareFragmentArgs.fromBundle(arguments!!)
|
val args by navArgs<ShareFragmentArgs>()
|
||||||
check(!(args.url == null && args.tabs.isNullOrEmpty())) { "URL and tabs cannot both be null." }
|
check(!(args.url == null && args.tabs.isNullOrEmpty())) { "URL and tabs cannot both be null." }
|
||||||
|
|
||||||
val tabs = args.tabs?.toList() ?: listOf(ShareTab(args.url!!, args.title.orEmpty()))
|
val tabs = args.tabs?.toList() ?: listOf(ShareTab(args.url!!, args.title.orEmpty()))
|
||||||
|
@ -153,74 +150,84 @@ class ShareFragment : AppCompatDialogFragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// Start with some invisible views so the share menu height doesn't jump later
|
||||||
|
shareToAppsView.setShareTargets(
|
||||||
|
listOf(AndroidShareOption.Invisible, AndroidShareOption.Invisible)
|
||||||
|
)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val devicesShareOptions = devicesListDeferred.await()
|
val devicesShareOptions = devicesListDeferred.await()
|
||||||
shareToAccountDevicesView.setSharetargets(devicesShareOptions)
|
shareToAccountDevicesView.setShareTargets(devicesShareOptions)
|
||||||
val appsToShareTo = appsListDeferred.await()
|
val appsToShareTo = appsListDeferred.await()
|
||||||
shareToAppsView.setShareTargets(appsToShareTo)
|
shareToAppsView.setShareTargets(appsToShareTo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
private fun getIntentActivities(shareIntent: Intent, context: Context): List<ResolveInfo>? {
|
private fun getIntentActivities(shareIntent: Intent, context: Context): List<ResolveInfo>? {
|
||||||
return context.packageManager.queryIntentActivities(shareIntent, 0)
|
return context.packageManager.queryIntentActivities(shareIntent, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of apps that can be shared to.
|
||||||
|
* @param intentActivities List of activities from [getIntentActivities].
|
||||||
|
*/
|
||||||
|
@WorkerThread
|
||||||
private fun buildAppsList(
|
private fun buildAppsList(
|
||||||
intentActivities: List<ResolveInfo>?,
|
intentActivities: List<ResolveInfo>?,
|
||||||
context: Context
|
context: Context
|
||||||
): List<AppShareOption> {
|
): List<AndroidShareOption> {
|
||||||
return intentActivities?.map { resolveInfo ->
|
return intentActivities
|
||||||
AppShareOption(
|
.orEmpty()
|
||||||
resolveInfo.loadLabel(context.packageManager).toString(),
|
.filter { it.activityInfo.packageName != context.packageName }
|
||||||
resolveInfo.loadIcon(context.packageManager),
|
.map { resolveInfo ->
|
||||||
resolveInfo.activityInfo.packageName,
|
AndroidShareOption.App(
|
||||||
resolveInfo.activityInfo.name
|
resolveInfo.loadLabel(context.packageManager).toString(),
|
||||||
)
|
resolveInfo.loadIcon(context.packageManager),
|
||||||
}?.filter { it.packageName != context.packageName }.orEmpty()
|
resolveInfo.activityInfo.packageName,
|
||||||
|
resolveInfo.activityInfo.name
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ReturnCount")
|
/**
|
||||||
|
* Builds list of options to display in the top row of the share sheet.
|
||||||
|
* This will primarily include devices that tabs can be sent to, but also options
|
||||||
|
* for reconnecting the account or sending to all devices.
|
||||||
|
*/
|
||||||
private fun buildDeviceList(accountManager: FxaAccountManager): List<SyncShareOption> {
|
private fun buildDeviceList(accountManager: FxaAccountManager): List<SyncShareOption> {
|
||||||
val list = mutableListOf<SyncShareOption>()
|
|
||||||
|
|
||||||
val activeNetwork = connectivityManager?.activeNetworkInfo
|
val activeNetwork = connectivityManager?.activeNetworkInfo
|
||||||
if (activeNetwork?.isConnected != true) {
|
val account = accountManager.authenticatedAccount()
|
||||||
list.add(SyncShareOption.Offline)
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountManager.authenticatedAccount() == null) {
|
return when {
|
||||||
list.add(SyncShareOption.SignIn)
|
// No network
|
||||||
return list
|
activeNetwork?.isConnected != true -> listOf(SyncShareOption.Offline)
|
||||||
}
|
// No account signed in
|
||||||
|
account == null -> listOf(SyncShareOption.SignIn)
|
||||||
|
// Account needs to be re-authenticated
|
||||||
|
accountManager.accountNeedsReauth() -> listOf(SyncShareOption.Reconnect)
|
||||||
|
// Signed in
|
||||||
|
else -> {
|
||||||
|
val shareableDevices = account.deviceConstellation().state()
|
||||||
|
?.otherDevices
|
||||||
|
.orEmpty()
|
||||||
|
.filter { it.capabilities.contains(DeviceCapability.SEND_TAB) }
|
||||||
|
|
||||||
if (accountManager.accountNeedsReauth()) {
|
val list = mutableListOf<SyncShareOption>()
|
||||||
list.add(SyncShareOption.Reconnect)
|
if (shareableDevices.isEmpty()) {
|
||||||
return list
|
// Show add device button if there are no devices
|
||||||
}
|
list.add(SyncShareOption.AddNewDevice)
|
||||||
|
|
||||||
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) {
|
shareableDevices.mapTo(list) { SyncShareOption.SingleDevice(it) }
|
||||||
list.add(SyncShareOption.SendAll(shareableDevices))
|
|
||||||
|
if (shareableDevices.size > 1) {
|
||||||
|
// Show send all button if there are multiple devices
|
||||||
|
list.add(SyncShareOption.SendAll(shareableDevices))
|
||||||
|
}
|
||||||
|
list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
package org.mozilla.fenix.share
|
package org.mozilla.fenix.share
|
||||||
|
|
||||||
import mozilla.components.concept.sync.Device
|
import mozilla.components.concept.sync.Device
|
||||||
import org.mozilla.fenix.share.listadapters.AppShareOption
|
import org.mozilla.fenix.share.listadapters.AndroidShareOption
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interactor for the share screen.
|
* Interactor for the share screen.
|
||||||
|
@ -37,7 +37,7 @@ class ShareInteractor(
|
||||||
controller.handleShareToAllDevices(devices)
|
controller.handleShareToAllDevices(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShareToApp(appToShareTo: AppShareOption) {
|
override fun onShareToApp(appToShareTo: AndroidShareOption.App) {
|
||||||
controller.handleShareToApp(appToShareTo)
|
controller.handleShareToApp(appToShareTo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,18 +26,19 @@ interface ShareToAccountDevicesInteractor {
|
||||||
|
|
||||||
class ShareToAccountDevicesView(
|
class ShareToAccountDevicesView(
|
||||||
override val containerView: ViewGroup,
|
override val containerView: ViewGroup,
|
||||||
private val interactor: ShareToAccountDevicesInteractor
|
interactor: ShareToAccountDevicesInteractor
|
||||||
) : LayoutContainer {
|
) : LayoutContainer {
|
||||||
|
|
||||||
|
private val adapter = AccountDevicesShareAdapter(interactor)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
LayoutInflater.from(containerView.context)
|
LayoutInflater.from(containerView.context)
|
||||||
.inflate(R.layout.share_to_account_devices, containerView, true)
|
.inflate(R.layout.share_to_account_devices, containerView, true)
|
||||||
|
|
||||||
devicesList.adapter = AccountDevicesShareAdapter(interactor)
|
devicesList.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSharetargets(targets: List<SyncShareOption>) {
|
fun setShareTargets(targets: List<SyncShareOption>) {
|
||||||
with(devicesList.adapter as AccountDevicesShareAdapter) {
|
adapter.submitList(targets)
|
||||||
updateData(targets)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,14 @@ import android.view.ViewGroup
|
||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.share_to_apps.*
|
import kotlinx.android.synthetic.main.share_to_apps.*
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.share.listadapters.AndroidShareOption
|
||||||
import org.mozilla.fenix.share.listadapters.AppShareAdapter
|
import org.mozilla.fenix.share.listadapters.AppShareAdapter
|
||||||
import org.mozilla.fenix.share.listadapters.AppShareOption
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callbacks for possible user interactions on the [ShareCloseView]
|
* Callbacks for possible user interactions on the [ShareCloseView]
|
||||||
*/
|
*/
|
||||||
interface ShareToAppsInteractor {
|
interface ShareToAppsInteractor {
|
||||||
fun onShareToApp(appToShareTo: AppShareOption)
|
fun onShareToApp(appToShareTo: AndroidShareOption.App)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShareToAppsView(
|
class ShareToAppsView(
|
||||||
|
@ -34,7 +34,7 @@ class ShareToAppsView(
|
||||||
appsList.adapter = adapter
|
appsList.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setShareTargets(targets: List<AppShareOption>) {
|
fun setShareTargets(targets: List<AndroidShareOption>) {
|
||||||
progressBar.visibility = View.GONE
|
progressBar.visibility = View.GONE
|
||||||
appsList.visibility = View.VISIBLE
|
appsList.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,19 @@ package org.mozilla.fenix.share.listadapters
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import mozilla.components.concept.sync.Device
|
import mozilla.components.concept.sync.Device
|
||||||
import org.mozilla.fenix.share.ShareToAccountDevicesInteractor
|
import org.mozilla.fenix.share.ShareToAccountDevicesInteractor
|
||||||
import org.mozilla.fenix.share.viewholders.AccountDeviceViewHolder
|
import org.mozilla.fenix.share.viewholders.AccountDeviceViewHolder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for a list of devices that can be shared to.
|
||||||
|
* May also display buttons to reconnect, add a device, or send to all devices.
|
||||||
|
*/
|
||||||
class AccountDevicesShareAdapter(
|
class AccountDevicesShareAdapter(
|
||||||
private val interactor: ShareToAccountDevicesInteractor,
|
private val interactor: ShareToAccountDevicesInteractor
|
||||||
private val devices: MutableList<SyncShareOption> = mutableListOf()
|
) : ListAdapter<SyncShareOption, AccountDeviceViewHolder>(DiffCallback) {
|
||||||
) : RecyclerView.Adapter<AccountDeviceViewHolder>() {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountDeviceViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountDeviceViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
@ -23,25 +27,33 @@ class AccountDevicesShareAdapter(
|
||||||
return AccountDeviceViewHolder(view, interactor)
|
return AccountDeviceViewHolder(view, interactor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = devices.size
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: AccountDeviceViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: AccountDeviceViewHolder, position: Int) {
|
||||||
holder.bind(devices[position])
|
holder.bind(getItem(position))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateData(deviceOptions: List<SyncShareOption>) {
|
private object DiffCallback : DiffUtil.ItemCallback<SyncShareOption>() {
|
||||||
this.devices.clear()
|
override fun areItemsTheSame(oldItem: SyncShareOption, newItem: SyncShareOption) =
|
||||||
this.devices.addAll(deviceOptions)
|
when (oldItem) {
|
||||||
notifyDataSetChanged()
|
is SyncShareOption.SendAll -> newItem is SyncShareOption.SendAll
|
||||||
|
is SyncShareOption.SingleDevice ->
|
||||||
|
newItem is SyncShareOption.SingleDevice && oldItem.device.id == newItem.device.id
|
||||||
|
else -> oldItem === newItem
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DiffUtilEquals")
|
||||||
|
override fun areContentsTheSame(oldItem: SyncShareOption, newItem: SyncShareOption) =
|
||||||
|
oldItem == newItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Different options to be displayed by [AccountDevicesShareAdapter].
|
||||||
|
*/
|
||||||
sealed class SyncShareOption {
|
sealed class SyncShareOption {
|
||||||
object Reconnect : SyncShareOption()
|
object Reconnect : SyncShareOption()
|
||||||
object Offline : SyncShareOption()
|
object Offline : SyncShareOption()
|
||||||
object SignIn : SyncShareOption()
|
object SignIn : SyncShareOption()
|
||||||
object AddNewDevice : SyncShareOption()
|
object AddNewDevice : SyncShareOption()
|
||||||
data class SendAll(val devices: List<Device>) : SyncShareOption()
|
data class SendAll(val devices: List<Device>) : SyncShareOption()
|
||||||
data class Mobile(val name: String, val device: Device) : SyncShareOption()
|
data class SingleDevice(val device: Device) : SyncShareOption()
|
||||||
data class Desktop(val name: String, val device: Device) : SyncShareOption()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,12 @@ import androidx.recyclerview.widget.ListAdapter
|
||||||
import org.mozilla.fenix.share.ShareToAppsInteractor
|
import org.mozilla.fenix.share.ShareToAppsInteractor
|
||||||
import org.mozilla.fenix.share.viewholders.AppViewHolder
|
import org.mozilla.fenix.share.viewholders.AppViewHolder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for a list of apps that can be shared to.
|
||||||
|
*/
|
||||||
class AppShareAdapter(
|
class AppShareAdapter(
|
||||||
private val interactor: ShareToAppsInteractor
|
private val interactor: ShareToAppsInteractor
|
||||||
) : ListAdapter<AppShareOption, AppViewHolder>(DiffCallback) {
|
) : ListAdapter<AndroidShareOption, AppViewHolder>(DiffCallback) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
@ -26,20 +29,38 @@ class AppShareAdapter(
|
||||||
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
|
||||||
holder.bind(getItem(position))
|
holder.bind(getItem(position))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object DiffCallback : DiffUtil.ItemCallback<AndroidShareOption>() {
|
||||||
|
override fun areItemsTheSame(oldItem: AndroidShareOption, newItem: AndroidShareOption) =
|
||||||
|
when (oldItem) {
|
||||||
|
AndroidShareOption.Invisible -> oldItem === newItem
|
||||||
|
is AndroidShareOption.App ->
|
||||||
|
newItem is AndroidShareOption.App && oldItem.packageName == newItem.packageName
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DiffUtilEquals")
|
||||||
|
override fun areContentsTheSame(oldItem: AndroidShareOption, newItem: AndroidShareOption) =
|
||||||
|
oldItem == newItem
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private object DiffCallback : DiffUtil.ItemCallback<AppShareOption>() {
|
/**
|
||||||
|
* Represents an app that can be shared to.
|
||||||
override fun areItemsTheSame(oldItem: AppShareOption, newItem: AppShareOption) =
|
*/
|
||||||
oldItem.packageName == newItem.packageName
|
sealed class AndroidShareOption {
|
||||||
|
object Invisible : AndroidShareOption()
|
||||||
override fun areContentsTheSame(oldItem: AppShareOption, newItem: AppShareOption) =
|
/**
|
||||||
oldItem == newItem
|
* Represents an app that can be shared to.
|
||||||
|
*
|
||||||
|
* @property name Name of the app.
|
||||||
|
* @property icon Icon representing the share target.
|
||||||
|
* @property packageName Package of the app.
|
||||||
|
* @property activityName Activity that will be shared to.
|
||||||
|
*/
|
||||||
|
data class App(
|
||||||
|
val name: String,
|
||||||
|
val icon: Drawable,
|
||||||
|
val packageName: String,
|
||||||
|
val activityName: String
|
||||||
|
) : AndroidShareOption()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class AppShareOption(
|
|
||||||
val name: String,
|
|
||||||
val icon: Drawable,
|
|
||||||
val packageName: String,
|
|
||||||
val activityName: String
|
|
||||||
)
|
|
||||||
|
|
|
@ -16,8 +16,11 @@ import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.loadIntoView
|
import org.mozilla.fenix.ext.loadIntoView
|
||||||
import org.mozilla.fenix.share.ShareTab
|
import org.mozilla.fenix.share.ShareTab
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for a list of tabs to be shared.
|
||||||
|
*/
|
||||||
class ShareTabsAdapter :
|
class ShareTabsAdapter :
|
||||||
ListAdapter<ShareTab, ShareTabsAdapter.ShareTabViewHolder>(ShareTabDiffCallback()) {
|
ListAdapter<ShareTab, ShareTabsAdapter.ShareTabViewHolder>(ShareTabDiffCallback) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ShareTabViewHolder(
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ShareTabViewHolder(
|
||||||
LayoutInflater.from(parent.context)
|
LayoutInflater.from(parent.context)
|
||||||
|
@ -27,13 +30,7 @@ class ShareTabsAdapter :
|
||||||
override fun onBindViewHolder(holder: ShareTabViewHolder, position: Int) =
|
override fun onBindViewHolder(holder: ShareTabViewHolder, position: Int) =
|
||||||
holder.bind(getItem(position))
|
holder.bind(getItem(position))
|
||||||
|
|
||||||
fun setTabs(tabs: List<ShareTab>) {
|
class ShareTabViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
submitList(tabs.toMutableList())
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ShareTabViewHolder(
|
|
||||||
itemView: View
|
|
||||||
) : RecyclerView.ViewHolder(itemView) {
|
|
||||||
|
|
||||||
fun bind(item: ShareTab) = with(itemView) {
|
fun bind(item: ShareTab) = with(itemView) {
|
||||||
context.components.core.icons.loadIntoView(itemView.share_tab_favicon, item.url)
|
context.components.core.icons.loadIntoView(itemView.share_tab_favicon, item.url)
|
||||||
|
@ -42,19 +39,11 @@ class ShareTabsAdapter :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ShareTabDiffCallback : DiffUtil.ItemCallback<ShareTab>() {
|
private object ShareTabDiffCallback : DiffUtil.ItemCallback<ShareTab>() {
|
||||||
override fun areItemsTheSame(
|
override fun areItemsTheSame(oldItem: ShareTab, newItem: ShareTab) =
|
||||||
oldItem: ShareTab,
|
oldItem.url == newItem.url
|
||||||
newItem: ShareTab
|
|
||||||
): Boolean {
|
|
||||||
return oldItem.url == newItem.url
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun areContentsTheSame(
|
override fun areContentsTheSame(oldItem: ShareTab, newItem: ShareTab) =
|
||||||
oldItem: ShareTab,
|
oldItem == newItem
|
||||||
newItem: ShareTab
|
|
||||||
): Boolean {
|
|
||||||
return oldItem.url == newItem.url && oldItem.title == newItem.title
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.annotation.VisibleForTesting
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.android.synthetic.main.account_share_list_item.view.*
|
import kotlinx.android.synthetic.main.account_share_list_item.view.*
|
||||||
|
import mozilla.components.concept.sync.DeviceType
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.lib.Do
|
import org.mozilla.fenix.lib.Do
|
||||||
import org.mozilla.fenix.share.ShareToAccountDevicesInteractor
|
import org.mozilla.fenix.share.ShareToAccountDevicesInteractor
|
||||||
|
@ -35,8 +36,7 @@ class AccountDeviceViewHolder(
|
||||||
SyncShareOption.SignIn -> interactor.onSignIn()
|
SyncShareOption.SignIn -> interactor.onSignIn()
|
||||||
SyncShareOption.AddNewDevice -> interactor.onAddNewDevice()
|
SyncShareOption.AddNewDevice -> interactor.onAddNewDevice()
|
||||||
is SyncShareOption.SendAll -> interactor.onShareToAllDevices(option.devices)
|
is SyncShareOption.SendAll -> interactor.onShareToAllDevices(option.devices)
|
||||||
is SyncShareOption.Mobile -> interactor.onShareToDevice(option.device)
|
is SyncShareOption.SingleDevice -> interactor.onShareToDevice(option.device)
|
||||||
is SyncShareOption.Desktop -> interactor.onShareToDevice(option.device)
|
|
||||||
SyncShareOption.Reconnect -> interactor.onReauth()
|
SyncShareOption.Reconnect -> interactor.onReauth()
|
||||||
SyncShareOption.Offline -> {
|
SyncShareOption.Offline -> {
|
||||||
// nothing we are offline
|
// nothing we are offline
|
||||||
|
@ -46,7 +46,25 @@ class AccountDeviceViewHolder(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindView(option: SyncShareOption) {
|
private fun bindView(option: SyncShareOption) {
|
||||||
val (name, drawableRes, colorRes) = when (option) {
|
val (name, drawableRes, colorRes) = getNameIconBackground(context, option)
|
||||||
|
|
||||||
|
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.isClickable = option != SyncShareOption.Offline
|
||||||
|
itemView.deviceName.text = name
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val LAYOUT_ID = R.layout.account_share_list_item
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a triple with the name, icon drawable resource, and background color drawable resource
|
||||||
|
* corresponding to the given [SyncShareOption].
|
||||||
|
*/
|
||||||
|
private fun getNameIconBackground(context: Context, option: SyncShareOption) = when (option) {
|
||||||
SyncShareOption.SignIn -> Triple(
|
SyncShareOption.SignIn -> Triple(
|
||||||
context.getText(R.string.sync_sign_in),
|
context.getText(R.string.sync_sign_in),
|
||||||
R.drawable.mozac_ic_sync,
|
R.drawable.mozac_ic_sync,
|
||||||
|
@ -72,28 +90,18 @@ class AccountDeviceViewHolder(
|
||||||
R.drawable.mozac_ic_select_all,
|
R.drawable.mozac_ic_select_all,
|
||||||
R.color.default_share_background
|
R.color.default_share_background
|
||||||
)
|
)
|
||||||
is SyncShareOption.Mobile -> Triple(
|
is SyncShareOption.SingleDevice -> when (option.device.deviceType) {
|
||||||
option.name,
|
DeviceType.MOBILE -> Triple(
|
||||||
R.drawable.mozac_ic_device_mobile,
|
option.device.displayName,
|
||||||
R.color.device_type_mobile_background
|
R.drawable.mozac_ic_device_mobile,
|
||||||
)
|
R.color.device_type_mobile_background
|
||||||
is SyncShareOption.Desktop -> Triple(
|
)
|
||||||
option.name,
|
else -> Triple(
|
||||||
R.drawable.mozac_ic_device_desktop,
|
option.device.displayName,
|
||||||
R.color.device_type_desktop_background
|
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.isClickable = option != SyncShareOption.Offline
|
|
||||||
itemView.deviceName.text = name
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val LAYOUT_ID = R.layout.account_share_list_item
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,32 +6,43 @@ package org.mozilla.fenix.share.viewholders
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.core.view.isInvisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.android.synthetic.main.app_share_list_item.view.*
|
import kotlinx.android.synthetic.main.app_share_list_item.view.*
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.lib.Do
|
||||||
import org.mozilla.fenix.share.ShareToAppsInteractor
|
import org.mozilla.fenix.share.ShareToAppsInteractor
|
||||||
import org.mozilla.fenix.share.listadapters.AppShareOption
|
import org.mozilla.fenix.share.listadapters.AndroidShareOption
|
||||||
|
|
||||||
class AppViewHolder(
|
class AppViewHolder(
|
||||||
itemView: View,
|
itemView: View,
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
@VisibleForTesting val interactor: ShareToAppsInteractor
|
||||||
val interactor: ShareToAppsInteractor
|
|
||||||
) : RecyclerView.ViewHolder(itemView) {
|
) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
private var application: AppShareOption? = null
|
private var application: AndroidShareOption? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
application?.let { application ->
|
Do exhaustive when (val app = application) {
|
||||||
interactor.onShareToApp(application)
|
AndroidShareOption.Invisible, null -> { /* no-op */ }
|
||||||
|
is AndroidShareOption.App -> interactor.onShareToApp(app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(item: AppShareOption) {
|
fun bind(item: AndroidShareOption) {
|
||||||
application = item
|
application = item
|
||||||
itemView.appName.text = item.name
|
|
||||||
itemView.appIcon.setImageDrawable(item.icon)
|
when (item) {
|
||||||
|
AndroidShareOption.Invisible -> {
|
||||||
|
itemView.isInvisible = true
|
||||||
|
}
|
||||||
|
is AndroidShareOption.App -> {
|
||||||
|
itemView.isInvisible = false
|
||||||
|
itemView.appName.text = item.name
|
||||||
|
itemView.appIcon.setImageDrawable(item.icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
android:orientation="vertical"/>
|
android:orientation="vertical"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/learn_button"
|
android:id="@+id/learn_button"
|
||||||
style="@style/ThemeIndependentMaterialGreyButton"
|
style="@style/ThemeIndependentMaterialGreyButton"
|
||||||
|
|
|
@ -40,7 +40,7 @@ import org.mozilla.fenix.components.metrics.Event
|
||||||
import org.mozilla.fenix.components.metrics.MetricController
|
import org.mozilla.fenix.components.metrics.MetricController
|
||||||
import org.mozilla.fenix.ext.metrics
|
import org.mozilla.fenix.ext.metrics
|
||||||
import org.mozilla.fenix.ext.nav
|
import org.mozilla.fenix.ext.nav
|
||||||
import org.mozilla.fenix.share.listadapters.AppShareOption
|
import org.mozilla.fenix.share.listadapters.AndroidShareOption
|
||||||
import org.robolectric.RobolectricTestRunner
|
import org.robolectric.RobolectricTestRunner
|
||||||
import org.robolectric.annotation.Config
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class ShareControllerTest {
|
||||||
fun `handleShareToApp should start a new sharing activity and close this`() {
|
fun `handleShareToApp should start a new sharing activity and close this`() {
|
||||||
val appPackageName = "package"
|
val appPackageName = "package"
|
||||||
val appClassName = "activity"
|
val appClassName = "activity"
|
||||||
val appShareOption = AppShareOption("app", mockk(), appPackageName, appClassName)
|
val appShareOption = AndroidShareOption.App("app", mockk(), appPackageName, appClassName)
|
||||||
val shareIntent = slot<Intent>()
|
val shareIntent = slot<Intent>()
|
||||||
// Our share Intent uses `FLAG_ACTIVITY_NEW_TASK` but when resolving the startActivity call
|
// Our share Intent uses `FLAG_ACTIVITY_NEW_TASK` but when resolving the startActivity call
|
||||||
// needed for capturing the actual Intent used the `slot` one doesn't have this flag so we
|
// needed for capturing the actual Intent used the `slot` one doesn't have this flag so we
|
||||||
|
|
|
@ -8,7 +8,7 @@ import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import mozilla.components.concept.sync.Device
|
import mozilla.components.concept.sync.Device
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mozilla.fenix.share.listadapters.AppShareOption
|
import org.mozilla.fenix.share.listadapters.AndroidShareOption
|
||||||
|
|
||||||
class ShareInteractorTest {
|
class ShareInteractorTest {
|
||||||
private val controller = mockk<ShareController>(relaxed = true)
|
private val controller = mockk<ShareController>(relaxed = true)
|
||||||
|
@ -62,7 +62,7 @@ class ShareInteractorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onShareToApp() {
|
fun onShareToApp() {
|
||||||
val app = mockk<AppShareOption>()
|
val app = mockk<AndroidShareOption.App>()
|
||||||
|
|
||||||
interactor.onShareToApp(app)
|
interactor.onShareToApp(app)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.spyk
|
import io.mockk.spyk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.mockk.verifyOrder
|
|
||||||
import mozilla.components.support.test.robolectric.testContext
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
@ -26,26 +25,8 @@ import org.robolectric.annotation.Config
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
@Config(application = TestApplication::class)
|
@Config(application = TestApplication::class)
|
||||||
class AccountDevicesShareAdapterTest {
|
class AccountDevicesShareAdapterTest {
|
||||||
private val syncOptions = mutableListOf(SyncShareOption.AddNewDevice, SyncShareOption.SignIn)
|
|
||||||
private val syncOptionsEmpty = mutableListOf<SyncShareOption>()
|
|
||||||
private val interactor: ShareInteractor = mockk(relaxed = true)
|
private val interactor: ShareInteractor = mockk(relaxed = true)
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `updateData should replace all previous data with argument and call notifyDataSetChanged()`() {
|
|
||||||
// Used AccountDevicesShareAdapter as a spy to ease testing of notifyDataSetChanged()
|
|
||||||
// and syncOptionsEmpty to be able to record them being called
|
|
||||||
val adapter = spyk(AccountDevicesShareAdapter(mockk(), syncOptionsEmpty))
|
|
||||||
every { adapter.notifyDataSetChanged() } just Runs
|
|
||||||
|
|
||||||
adapter.updateData(syncOptions)
|
|
||||||
|
|
||||||
verifyOrder {
|
|
||||||
syncOptionsEmpty.clear()
|
|
||||||
syncOptionsEmpty.addAll(syncOptions)
|
|
||||||
adapter.notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getItemCount on a default instantiated Adapter should return 0`() {
|
fun `getItemCount on a default instantiated Adapter should return 0`() {
|
||||||
val adapter = AccountDevicesShareAdapter(mockk())
|
val adapter = AccountDevicesShareAdapter(mockk())
|
||||||
|
@ -53,13 +34,6 @@ class AccountDevicesShareAdapterTest {
|
||||||
assertThat(adapter.itemCount).isEqualTo(0)
|
assertThat(adapter.itemCount).isEqualTo(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `getItemCount after updateData() call should return the the passed in list's size`() {
|
|
||||||
val adapter = AccountDevicesShareAdapter(mockk(), syncOptions)
|
|
||||||
|
|
||||||
assertThat(adapter.itemCount).isEqualTo(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `the adapter uses the right ViewHolder`() {
|
fun `the adapter uses the right ViewHolder`() {
|
||||||
val adapter = AccountDevicesShareAdapter(interactor)
|
val adapter = AccountDevicesShareAdapter(interactor)
|
||||||
|
@ -84,7 +58,9 @@ class AccountDevicesShareAdapterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `the adapter binds the right item to a ViewHolder`() {
|
fun `the adapter binds the right item to a ViewHolder`() {
|
||||||
val adapter = AccountDevicesShareAdapter(interactor, syncOptions)
|
val syncOptions = listOf(SyncShareOption.AddNewDevice, SyncShareOption.SignIn)
|
||||||
|
val adapter = AccountDevicesShareAdapter(interactor)
|
||||||
|
adapter.submitList(syncOptions)
|
||||||
val parentView: ViewGroup = mockk(relaxed = true)
|
val parentView: ViewGroup = mockk(relaxed = true)
|
||||||
val itemView: ViewGroup = mockk(relaxed = true)
|
val itemView: ViewGroup = mockk(relaxed = true)
|
||||||
every { parentView.context } returns testContext
|
every { parentView.context } returns testContext
|
||||||
|
|
|
@ -14,6 +14,7 @@ import io.mockk.mockk
|
||||||
import io.mockk.spyk
|
import io.mockk.spyk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.mockk.verifyOrder
|
import io.mockk.verifyOrder
|
||||||
|
import kotlinx.android.synthetic.main.app_share_list_item.view.*
|
||||||
import mozilla.components.support.test.robolectric.testContext
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
@ -27,11 +28,11 @@ import org.robolectric.annotation.Config
|
||||||
@Config(application = TestApplication::class)
|
@Config(application = TestApplication::class)
|
||||||
class AppShareAdapterTest {
|
class AppShareAdapterTest {
|
||||||
|
|
||||||
private val appOptions = mutableListOf(
|
private val appOptions = mutableListOf<AndroidShareOption>(
|
||||||
AppShareOption("App 0", mockk(), "package 0", "activity 0"),
|
AndroidShareOption.App("App 0", mockk(), "package 0", "activity 0"),
|
||||||
AppShareOption("App 1", mockk(), "package 1", "activity 1")
|
AndroidShareOption.App("App 1", mockk(), "package 1", "activity 1")
|
||||||
)
|
)
|
||||||
private val appOptionsEmpty = emptyList<AppShareOption>()
|
private val appOptionsEmpty = emptyList<AndroidShareOption>()
|
||||||
private val interactor: ShareInteractor = mockk(relaxed = true)
|
private val interactor: ShareInteractor = mockk(relaxed = true)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue