2
0
mirror of https://github.com/fork-maintainers/iceraven-browser synced 2024-11-17 15:26:23 +00:00

[fenix] For https://github.com/mozilla-mobile/fenix/issues/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.
This commit is contained in:
Mugurell 2019-08-26 18:18:33 +03:00 committed by Jeff Boek
parent a8cbf8b1d8
commit 051f09a15a
4 changed files with 133 additions and 8 deletions

View File

@ -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()
shareCloseView = ShareCloseView(view.closeSharingLayout, shareInteractor)
if (isSharingToDevicesAvailable(requireContext().applicationContext)) {
shareToAccountDevicesView = ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor)
} else {
view.devicesShareGroup.visibility = View.GONE
}
shareCloseView = ShareCloseView(view.closeSharingLayout, 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

View File

@ -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")
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}