Commit 16eba61b authored by Mugurell's avatar Mugurell Committed by Jeff Boek
Browse files

For #4007 - ShareFragment will set the contained Views' state

ShareFragment which acts as a container would contain all business logic needed
for populating it's Views.
Data initialization should be done only once since the app state has no reason
to change after the ShareFragment is created and is done as soon as possible,
in onAttach().
Because of the expected short lifespan of this fragment, given the fact that
the state has no reason to change and we handle orientation changes ourselves
to keep things simple I didn't use a ViewModel to persist the state.
parent 09587014
......@@ -4,16 +4,33 @@
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.content.pm.ResolveInfo
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.lifecycle.lifecycleScope
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_share.view.*
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import mozilla.components.concept.sync.DeviceCapability
import mozilla.components.concept.sync.DeviceType
import mozilla.components.service.fxa.manager.FxaAccountManager
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.share.listadapters.AppShareOption
import org.mozilla.fenix.share.listadapters.SyncShareOption
@Suppress("TooManyFunctions")
class ShareFragment : AppCompatDialogFragment() {
interface TabsSharedCallback {
fun onTabsShared(tabsSize: Int)
......@@ -23,7 +40,27 @@ class ShareFragment : AppCompatDialogFragment() {
private lateinit var shareCloseView: ShareCloseView
private lateinit var shareToAccountDevicesView: ShareToAccountDevicesView
private lateinit var shareToAppsView: ShareToAppsView
private var tabs: Array<ShareTab> = emptyArray()
private lateinit var appsListDeferred: Deferred<List<AppShareOption>>
private lateinit var devicesListDeferred: Deferred<List<SyncShareOption>>
override fun onAttach(context: Context) {
super.onAttach(context)
// Start preparing the data as soon as we have a valid Context
appsListDeferred = lifecycleScope.async(Dispatchers.IO) {
val shareIntent = Intent(ACTION_SEND).apply {
type = "text/plain"
flags = FLAG_ACTIVITY_NEW_TASK
}
val shareAppsActivities = getIntentActivities(shareIntent, context)
buildAppsList(shareAppsActivities, context)
}
devicesListDeferred = lifecycleScope.async(Dispatchers.IO) {
val fxaAccountManager = context.components.backgroundServices.accountManager
buildDeviceList(fxaAccountManager)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -41,16 +78,79 @@ class ShareFragment : AppCompatDialogFragment() {
throw IllegalStateException("URL and tabs cannot both be null.")
}
tabs = args.tabs ?: arrayOf(ShareTab(args.url!!, args.title ?: ""))
val tabs = args.tabs?.toList() ?: listOf(ShareTab(args.url!!, args.title ?: ""))
shareInteractor = ShareInteractor()
if (isSharingToDevicesAvailable(requireContext().applicationContext)) {
shareToAccountDevicesView = ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor)
} else {
view.devicesShareGroup.visibility = View.GONE
}
shareCloseView = ShareCloseView(view.closeSharingLayout, shareInteractor)
shareToAccountDevicesView = ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor)
shareToAppsView = ShareToAppsView(view.appsShareLayout, shareInteractor)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
val devicesShareOptions = devicesListDeferred.await()
shareToAccountDevicesView.setSharetargets(devicesShareOptions)
val appsToShareTo = appsListDeferred.await()
shareToAppsView.setSharetargets(appsToShareTo)
}
}
private fun isSharingToDevicesAvailable(context: Context) =
!context.components.backgroundServices.accountManager.accountNeedsReauth()
private fun getIntentActivities(shareIntent: Intent, context: Context): List<ResolveInfo>? {
return context.packageManager.queryIntentActivities(shareIntent, 0)
}
private fun buildAppsList(intentActivities: List<ResolveInfo>?, context: Context): List<AppShareOption> {
return intentActivities?.map { resolveInfo ->
AppShareOption(
resolveInfo.loadLabel(context.packageManager).toString(),
resolveInfo.loadIcon(context.packageManager),
resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name
)
} ?: emptyList()
}
private fun buildDeviceList(accountManager: FxaAccountManager): List<SyncShareOption> {
val list = mutableListOf<SyncShareOption>()
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
}
}
@Parcelize
......
......@@ -5,7 +5,7 @@
package org.mozilla.fenix.share
import mozilla.components.concept.sync.Device
import org.mozilla.fenix.share.listadapters.Application
import org.mozilla.fenix.share.listadapters.AppShareOption
/**
* Interactor for the share screen.
......@@ -31,7 +31,7 @@ class ShareInteractor : ShareCloseInteractor, ShareToAccountDevicesInteractor, S
TODO("not yet!? implemented")
}
override fun onShareToApp(appToShareTo: Application) {
override fun onShareToApp(appToShareTo: AppShareOption) {
TODO("not yet!? implemented")
}
}
......@@ -7,8 +7,11 @@ package org.mozilla.fenix.share
import android.view.LayoutInflater
import android.view.ViewGroup
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.share_to_account_devices.*
import mozilla.components.concept.sync.Device
import org.mozilla.fenix.R
import org.mozilla.fenix.share.listadapters.AccountDevicesShareAdapter
import org.mozilla.fenix.share.listadapters.SyncShareOption
/**
* Callbacks for possible user interactions on the [ShareToAccountDevicesView]
......@@ -27,5 +30,13 @@ class ShareToAccountDevicesView(
init {
LayoutInflater.from(containerView.context)
.inflate(R.layout.share_to_account_devices, containerView, true)
devicesList.adapter = AccountDevicesShareAdapter(interactor)
}
fun setSharetargets(targets: List<SyncShareOption>) {
with(devicesList.adapter as AccountDevicesShareAdapter) {
updateData(targets)
}
}
}
......@@ -5,24 +5,38 @@
package org.mozilla.fenix.share
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.extensions.LayoutContainer
import org.mozilla.fenix.R
import org.mozilla.fenix.share.listadapters.Application
import org.mozilla.fenix.share.listadapters.AppShareOption
import kotlinx.android.synthetic.main.share_to_apps.*
import org.mozilla.fenix.share.listadapters.AppShareAdapter
/**
* Callbacks for possible user interactions on the [ShareCloseView]
*/
interface ShareToAppsInteractor {
fun onShareToApp(appToShareTo: Application)
fun onShareToApp(appToShareTo: AppShareOption)
}
class ShareToAppsView(
override val containerView: ViewGroup,
private val interactor: ShareToAppsInteractor
interactor: ShareToAppsInteractor
) : LayoutContainer {
init {
LayoutInflater.from(containerView.context)
.inflate(R.layout.share_to_apps, containerView, true)
appsList.adapter = AppShareAdapter(interactor)
}
fun setSharetargets(targets: List<AppShareOption>) {
progressBar.visibility = View.GONE
appsList.visibility = View.VISIBLE
with(appsList.adapter as AppShareAdapter) {
updateData(targets)
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment