|
|
|
@ -4,21 +4,30 @@ import android.app.Activity
|
|
|
|
|
import android.content.Intent
|
|
|
|
|
import android.net.Uri
|
|
|
|
|
import android.os.Parcelable
|
|
|
|
|
import android.text.SpannableString
|
|
|
|
|
import android.text.Spanned
|
|
|
|
|
import android.text.style.ClickableSpan
|
|
|
|
|
import android.view.View
|
|
|
|
|
import android.view.ViewGroup
|
|
|
|
|
import android.widget.ImageView
|
|
|
|
|
import android.widget.TextView
|
|
|
|
|
import androidx.compose.foundation.layout.Column
|
|
|
|
|
import androidx.compose.foundation.layout.Row
|
|
|
|
|
import androidx.compose.foundation.layout.padding
|
|
|
|
|
import androidx.compose.material.Icon
|
|
|
|
|
import androidx.compose.material.IconButton
|
|
|
|
|
import androidx.compose.material.Text
|
|
|
|
|
import androidx.compose.material.icons.Icons
|
|
|
|
|
import androidx.compose.material.icons.outlined.Notifications
|
|
|
|
|
import androidx.compose.material.icons.outlined.NotificationsOff
|
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
|
import androidx.core.util.Pair
|
|
|
|
|
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
|
|
|
|
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
|
|
|
|
import dagger.hilt.android.AndroidEntryPoint
|
|
|
|
|
import org.tasks.R
|
|
|
|
|
import org.tasks.Strings.isNullOrEmpty
|
|
|
|
|
import org.tasks.compose.DisabledText
|
|
|
|
|
import org.tasks.compose.collectAsStateLifecycleAware
|
|
|
|
|
import org.tasks.data.Geofence
|
|
|
|
|
import org.tasks.data.Location
|
|
|
|
|
import org.tasks.data.Place
|
|
|
|
|
import org.tasks.databinding.LocationRowBinding
|
|
|
|
|
import org.tasks.dialogs.DialogBuilder
|
|
|
|
|
import org.tasks.dialogs.GeofenceDialog
|
|
|
|
|
import org.tasks.extensions.Context.openUri
|
|
|
|
@ -27,78 +36,37 @@ import org.tasks.location.LocationPickerActivity
|
|
|
|
|
import org.tasks.preferences.Device
|
|
|
|
|
import org.tasks.preferences.FragmentPermissionRequestor
|
|
|
|
|
import org.tasks.preferences.PermissionChecker
|
|
|
|
|
import org.tasks.preferences.PermissionChecker.backgroundPermissions
|
|
|
|
|
import org.tasks.preferences.Preferences
|
|
|
|
|
import javax.inject.Inject
|
|
|
|
|
|
|
|
|
|
@AndroidEntryPoint
|
|
|
|
|
class LocationControlSet : TaskEditControlFragment() {
|
|
|
|
|
class LocationControlSet : TaskEditControlComposeFragment() {
|
|
|
|
|
@Inject lateinit var preferences: Preferences
|
|
|
|
|
@Inject lateinit var dialogBuilder: DialogBuilder
|
|
|
|
|
@Inject lateinit var device: Device
|
|
|
|
|
@Inject lateinit var permissionRequestor: FragmentPermissionRequestor
|
|
|
|
|
@Inject lateinit var permissionChecker: PermissionChecker
|
|
|
|
|
|
|
|
|
|
private lateinit var locationName: TextView
|
|
|
|
|
private lateinit var locationAddress: TextView
|
|
|
|
|
private lateinit var geofenceOptions: ImageView
|
|
|
|
|
|
|
|
|
|
override fun onResume() {
|
|
|
|
|
super.onResume()
|
|
|
|
|
|
|
|
|
|
updateUi()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun setLocation(location: Location?) {
|
|
|
|
|
viewModel.selectedLocation = location
|
|
|
|
|
updateUi()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun updateUi() {
|
|
|
|
|
val location = viewModel.selectedLocation
|
|
|
|
|
if (location == null) {
|
|
|
|
|
locationName.text = ""
|
|
|
|
|
geofenceOptions.visibility = View.GONE
|
|
|
|
|
locationAddress.visibility = View.GONE
|
|
|
|
|
} else {
|
|
|
|
|
geofenceOptions.visibility = View.VISIBLE
|
|
|
|
|
geofenceOptions.setImageResource(
|
|
|
|
|
if (permissionChecker.canAccessBackgroundLocation()
|
|
|
|
|
&& (location.isArrival || location.isDeparture)) R.drawable.ic_outline_notifications_24px else R.drawable.ic_outline_notifications_off_24px)
|
|
|
|
|
val name = location.displayName
|
|
|
|
|
val address = location.displayAddress
|
|
|
|
|
if (!isNullOrEmpty(address) && address != name) {
|
|
|
|
|
locationAddress.text = address
|
|
|
|
|
locationAddress.visibility = View.VISIBLE
|
|
|
|
|
} else {
|
|
|
|
|
locationAddress.visibility = View.GONE
|
|
|
|
|
}
|
|
|
|
|
val spannableString = SpannableString(name)
|
|
|
|
|
spannableString.setSpan(
|
|
|
|
|
object : ClickableSpan() {
|
|
|
|
|
override fun onClick(view: View) {}
|
|
|
|
|
},
|
|
|
|
|
0,
|
|
|
|
|
name.length,
|
|
|
|
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
|
locationName.text = spannableString
|
|
|
|
|
}
|
|
|
|
|
viewModel.selectedLocation.value = location
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onRowClick() {
|
|
|
|
|
val location = viewModel.selectedLocation
|
|
|
|
|
val location = viewModel.selectedLocation.value
|
|
|
|
|
if (location == null) {
|
|
|
|
|
chooseLocation()
|
|
|
|
|
} else {
|
|
|
|
|
val options: MutableList<Pair<Int, () -> Unit>> = ArrayList()
|
|
|
|
|
options.add(Pair.create(R.string.open_map, { location.open(activity) }))
|
|
|
|
|
options.add(Pair.create(R.string.open_map) { location.open(activity) })
|
|
|
|
|
if (!isNullOrEmpty(location.phone)) {
|
|
|
|
|
options.add(Pair.create(R.string.action_call, { call() }))
|
|
|
|
|
options.add(Pair.create(R.string.action_call) { call() })
|
|
|
|
|
}
|
|
|
|
|
if (!isNullOrEmpty(location.url)) {
|
|
|
|
|
options.add(Pair.create(R.string.visit_website, { openWebsite() }))
|
|
|
|
|
options.add(Pair.create(R.string.visit_website) { openWebsite() })
|
|
|
|
|
}
|
|
|
|
|
options.add(Pair.create(R.string.choose_new_location, { chooseLocation() }))
|
|
|
|
|
options.add(Pair.create(R.string.delete, { setLocation(null) }))
|
|
|
|
|
options.add(Pair.create(R.string.choose_new_location) { chooseLocation() })
|
|
|
|
|
options.add(Pair.create(R.string.delete) { setLocation(null) })
|
|
|
|
|
val items = options.map { requireContext().getString(it.first!!) }
|
|
|
|
|
dialogBuilder
|
|
|
|
|
.newDialog(location.displayName)
|
|
|
|
@ -111,35 +79,45 @@ class LocationControlSet : TaskEditControlFragment() {
|
|
|
|
|
|
|
|
|
|
private fun chooseLocation() {
|
|
|
|
|
val intent = Intent(activity, LocationPickerActivity::class.java)
|
|
|
|
|
viewModel.selectedLocation?.let {
|
|
|
|
|
viewModel.selectedLocation.value?.let {
|
|
|
|
|
intent.putExtra(LocationPickerActivity.EXTRA_PLACE, it.place as Parcelable)
|
|
|
|
|
}
|
|
|
|
|
startActivityForResult(intent, REQUEST_LOCATION_REMINDER)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun geofenceOptions() {
|
|
|
|
|
if (permissionChecker.canAccessBackgroundLocation()) {
|
|
|
|
|
showGeofenceOptions()
|
|
|
|
|
} else {
|
|
|
|
|
newLocationPermissionDialog(this, REQUEST_LOCATION_PERMISSIONS)
|
|
|
|
|
.show(parentFragmentManager, FRAG_TAG_REQUEST_LOCATION)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun showGeofenceOptions() {
|
|
|
|
|
val dialog = GeofenceDialog.newGeofenceDialog(viewModel.selectedLocation)
|
|
|
|
|
val dialog = GeofenceDialog.newGeofenceDialog(viewModel.selectedLocation.value)
|
|
|
|
|
dialog.setTargetFragment(this, REQUEST_GEOFENCE_DETAILS)
|
|
|
|
|
dialog.show(parentFragmentManager, FRAG_TAG_LOCATION_DIALOG)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun bind(parent: ViewGroup?) =
|
|
|
|
|
LocationRowBinding.inflate(layoutInflater, parent, true).let {
|
|
|
|
|
locationName = it.locationName
|
|
|
|
|
locationAddress = it.locationAddress
|
|
|
|
|
geofenceOptions = it.geofenceOptions.apply {
|
|
|
|
|
setOnClickListener { geofenceOptions() }
|
|
|
|
|
@OptIn(ExperimentalPermissionsApi::class)
|
|
|
|
|
@Composable
|
|
|
|
|
override fun Body() {
|
|
|
|
|
val location = viewModel.selectedLocation.collectAsStateLifecycleAware().value
|
|
|
|
|
val hasPermissions =
|
|
|
|
|
rememberMultiplePermissionsState(permissions = backgroundPermissions())
|
|
|
|
|
.allPermissionsGranted
|
|
|
|
|
if (location == null) {
|
|
|
|
|
DisabledText(
|
|
|
|
|
text = stringResource(id = R.string.add_location),
|
|
|
|
|
modifier = Modifier.padding(vertical = 20.dp)
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
LocationRow(
|
|
|
|
|
name = location.displayName,
|
|
|
|
|
address = location.displayAddress,
|
|
|
|
|
onClick = {
|
|
|
|
|
if (hasPermissions) {
|
|
|
|
|
showGeofenceOptions()
|
|
|
|
|
} else {
|
|
|
|
|
newLocationPermissionDialog(this, REQUEST_LOCATION_PERMISSIONS)
|
|
|
|
|
.show(parentFragmentManager, FRAG_TAG_REQUEST_LOCATION)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
geofenceOn = hasPermissions && (location.isArrival || location.isDeparture)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
it.root
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override val icon = R.drawable.ic_outline_place_24px
|
|
|
|
@ -149,11 +127,11 @@ class LocationControlSet : TaskEditControlFragment() {
|
|
|
|
|
override val isClickable = true
|
|
|
|
|
|
|
|
|
|
private fun openWebsite() {
|
|
|
|
|
viewModel.selectedLocation?.let { context?.openUri(it.url) }
|
|
|
|
|
viewModel.selectedLocation.value?.let { context?.openUri(it.url) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun call() {
|
|
|
|
|
viewModel.selectedLocation?.let {
|
|
|
|
|
viewModel.selectedLocation.value?.let {
|
|
|
|
|
startActivity(Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + it.phone)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -166,7 +144,7 @@ class LocationControlSet : TaskEditControlFragment() {
|
|
|
|
|
} else if (requestCode == REQUEST_LOCATION_REMINDER) {
|
|
|
|
|
if (resultCode == Activity.RESULT_OK) {
|
|
|
|
|
val place: Place = data!!.getParcelableExtra(LocationPickerActivity.EXTRA_PLACE)!!
|
|
|
|
|
val location = viewModel.selectedLocation
|
|
|
|
|
val location = viewModel.selectedLocation.value
|
|
|
|
|
val geofence = if (location == null) {
|
|
|
|
|
Geofence(place.uid, preferences)
|
|
|
|
|
} else {
|
|
|
|
@ -183,7 +161,7 @@ class LocationControlSet : TaskEditControlFragment() {
|
|
|
|
|
if (resultCode == Activity.RESULT_OK) {
|
|
|
|
|
setLocation(Location(
|
|
|
|
|
data?.getParcelableExtra(GeofenceDialog.EXTRA_GEOFENCE) ?: return,
|
|
|
|
|
viewModel.selectedLocation?.place ?: return
|
|
|
|
|
viewModel.selectedLocation.value?.place ?: return
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -200,3 +178,38 @@ class LocationControlSet : TaskEditControlFragment() {
|
|
|
|
|
private const val FRAG_TAG_REQUEST_LOCATION = "request_location"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
fun LocationRow(
|
|
|
|
|
name: String,
|
|
|
|
|
address: String?,
|
|
|
|
|
geofenceOn: Boolean,
|
|
|
|
|
onClick: () -> Unit,
|
|
|
|
|
) {
|
|
|
|
|
Row {
|
|
|
|
|
Column(
|
|
|
|
|
modifier = Modifier
|
|
|
|
|
.weight(1f)
|
|
|
|
|
.padding(vertical = 20.dp)
|
|
|
|
|
) {
|
|
|
|
|
Text(text = name)
|
|
|
|
|
address?.takeIf { it.isNotBlank() && it != name }?.let {
|
|
|
|
|
Text(text = address)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
IconButton(
|
|
|
|
|
onClick = onClick,
|
|
|
|
|
modifier = Modifier.padding(top = 8.dp /* + 12dp from icon */)
|
|
|
|
|
) {
|
|
|
|
|
Icon(
|
|
|
|
|
imageVector = if (geofenceOn) {
|
|
|
|
|
Icons.Outlined.Notifications
|
|
|
|
|
} else {
|
|
|
|
|
Icons.Outlined.NotificationsOff
|
|
|
|
|
},
|
|
|
|
|
contentDescription = null
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|