Skip to content

Commit

Permalink
Merge pull request #5411 from vector-im/feature/mna/PSF-735-sharing-o…
Browse files Browse the repository at this point in the history
…ptions-view

#5395: Location sharing options view
  • Loading branch information
mnaturel authored Mar 9, 2022
2 parents cbdc28d + 2bdafde commit f12afe0
Show file tree
Hide file tree
Showing 19 changed files with 475 additions and 69 deletions.
1 change: 1 addition & 0 deletions changelog.d/5395.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a custom view to display a picker for share location options
4 changes: 4 additions & 0 deletions library/ui-styles/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,8 @@
<color name="vctr_presence_indicator_offline_light">@color/palette_gray_100</color>
<color name="vctr_presence_indicator_offline_dark">@color/palette_gray_450</color>

<!-- Location sharing colors -->
<attr name="vctr_live_location" format="color" />
<color name="vctr_live_location_light">@color/palette_prune</color>
<color name="vctr_live_location_dark">@color/palette_prune</color>
</resources>
5 changes: 4 additions & 1 deletion library/ui-styles/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,7 @@

<item name="ftue_auth_profile_picture_height" format="float" type="dimen">0.15</item>
<item name="ftue_auth_profile_picture_icon_height" format="float" type="dimen">0.05</item>
</resources>

<!-- Location sharing -->
<dimen name="location_sharing_option_default_padding">10dp</dimen>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<declare-styleable name="LocationSharingOptionView">
<attr name="locShareIcon" format="reference" />
<attr name="locShareIconBackground" format="reference" />
<attr name="locShareIconBackgroundTint" format="color" />
<attr name="locShareIconPadding" format="dimension" />
<attr name="locShareIconDescription" format="string" />
<attr name="locShareTitle" format="string" />
</declare-styleable>

</resources>
2 changes: 2 additions & 0 deletions library/ui-styles/src/main/res/values/theme_dark.xml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@

<item name="android:actionButtonStyle">@style/Widget.Vector.ActionButton</item>

<!-- Location sharing -->
<item name="vctr_live_location">@color/vctr_live_location_dark</item>
</style>

<style name="Theme.Vector.Dark" parent="Base.Theme.Vector.Dark" />
Expand Down
2 changes: 2 additions & 0 deletions library/ui-styles/src/main/res/values/theme_light.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@

<item name="android:actionButtonStyle">@style/Widget.Vector.ActionButton</item>

<!-- Location sharing -->
<item name="vctr_live_location">@color/vctr_live_location_light</item>
</style>

<style name="Theme.Vector.Light" parent="Base.Theme.Vector.Light" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
Expand Down Expand Up @@ -70,6 +71,15 @@ fun View.setAttributeTintedBackground(@DrawableRes drawableRes: Int, @AttrRes ti
background = drawable
}

fun View.tintBackground(@ColorInt tintColor: Int) {
val bkg = background?.let {
val backgroundDrawable = DrawableCompat.wrap(background)
DrawableCompat.setTint(backgroundDrawable, tintColor)
backgroundDrawable
}
background = bkg
}

fun ImageView.setAttributeTintedImageResource(@DrawableRes drawableRes: Int, @AttrRes tint: Int) {
val drawable = ContextCompat.getDrawable(context, drawableRes)!!
DrawableCompat.setTint(drawable, ThemeUtils.getColor(context, tint))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package im.vector.app.features.home.room.detail.timeline.helper
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import im.vector.app.R
Expand All @@ -37,7 +39,8 @@ class LocationPinProvider @Inject constructor(
private val context: Context,
private val activeSessionHolder: ActiveSessionHolder,
private val dimensionConverter: DimensionConverter,
private val avatarRenderer: AvatarRenderer
private val avatarRenderer: AvatarRenderer,
private val matrixItemColorProvider: MatrixItemColorProvider
) {
private val cache = mutableMapOf<String, Drawable>()

Expand All @@ -61,35 +64,42 @@ class LocationPinProvider @Inject constructor(
return
}

activeSessionHolder.getActiveSession().getUser(userId)?.toMatrixItem()?.let {
val size = dimensionConverter.dpToPx(44)
avatarRenderer.render(glideRequests, it, object : CustomTarget<Drawable>(size, size) {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
Timber.d("## Location: onResourceReady")
val pinDrawable = createPinDrawable(resource)
cache[userId] = pinDrawable
callback(pinDrawable)
}
activeSessionHolder
.getActiveSession()
.getUser(userId)
?.toMatrixItem()
?.let { userItem ->
val size = dimensionConverter.dpToPx(44)
val bgTintColor = matrixItemColorProvider.getColor(userItem)
avatarRenderer.render(glideRequests, userItem, object : CustomTarget<Drawable>(size, size) {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
Timber.d("## Location: onResourceReady")
val pinDrawable = createPinDrawable(resource, bgTintColor)
cache[userId] = pinDrawable
callback(pinDrawable)
}

override fun onLoadCleared(placeholder: Drawable?) {
// Is it possible? Put placeholder instead?
// FIXME The doc says it has to be implemented and should free resources
Timber.d("## Location: onLoadCleared")
}
override fun onLoadCleared(placeholder: Drawable?) {
// Is it possible? Put placeholder instead?
// FIXME The doc says it has to be implemented and should free resources
Timber.d("## Location: onLoadCleared")
}

override fun onLoadFailed(errorDrawable: Drawable?) {
Timber.w("## Location: onLoadFailed")
errorDrawable ?: return
val pinDrawable = createPinDrawable(errorDrawable)
cache[userId] = pinDrawable
callback(pinDrawable)
override fun onLoadFailed(errorDrawable: Drawable?) {
Timber.w("## Location: onLoadFailed")
errorDrawable ?: return
val pinDrawable = createPinDrawable(errorDrawable, bgTintColor)
cache[userId] = pinDrawable
callback(pinDrawable)
}
})
}
})
}
}

private fun createPinDrawable(drawable: Drawable): Drawable {
private fun createPinDrawable(drawable: Drawable, @ColorInt bgTintColor: Int): Drawable {
val bgUserPin = ContextCompat.getDrawable(context, R.drawable.bg_map_user_pin)!!
// use mutate on drawable to avoid sharing the color when we have multiple different user pins
DrawableCompat.setTint(bgUserPin.mutate(), bgTintColor)
val layerDrawable = LayerDrawable(arrayOf(bgUserPin, drawable))
val horizontalInset = dimensionConverter.dpToPx(4)
val topInset = dimensionConverter.dpToPx(4)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,29 @@ import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentLocationSharingBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import im.vector.app.features.location.option.LocationSharingOption
import org.matrix.android.sdk.api.util.MatrixItem
import java.lang.ref.WeakReference
import javax.inject.Inject

/**
* We should consider using SupportMapFragment for a out of the box lifecycle handling
*/
class LocationSharingFragment @Inject constructor(
private val urlMapProvider: UrlMapProvider
private val urlMapProvider: UrlMapProvider,
private val avatarRenderer: AvatarRenderer,
private val matrixItemColorProvider: MatrixItemColorProvider
) : VectorBaseFragment<FragmentLocationSharingBinding>() {

private val viewModel: LocationSharingViewModel by fragmentViewModel()

// Keep a ref to handle properly the onDestroy callback
private var mapView: WeakReference<MapView>? = null

private var hasRenderedUserAvatar = false

override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding {
return FragmentLocationSharingBinding.inflate(inflater, container, false)
}
Expand All @@ -59,9 +67,7 @@ class LocationSharingFragment @Inject constructor(
views.mapView.initialize(urlMapProvider.getMapUrl())
}

views.shareLocationContainer.debouncedClicks {
viewModel.handle(LocationSharingAction.OnShareLocation)
}
initOptionsPicker()

viewModel.observeViewEvents {
when (it) {
Expand Down Expand Up @@ -107,6 +113,12 @@ class LocationSharingFragment @Inject constructor(
super.onDestroy()
}

override fun invalidate() = withState(viewModel) { state ->
views.mapView.render(state.toMapState())
views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null
updateUserAvatar(state.userItem)
}

private fun handleLocationNotAvailableError() {
MaterialAlertDialogBuilder(requireActivity())
.setTitle(R.string.location_not_available_dialog_title)
Expand All @@ -118,8 +130,28 @@ class LocationSharingFragment @Inject constructor(
.show()
}

override fun invalidate() = withState(viewModel) { state ->
views.mapView.render(state.toMapState())
views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null
private fun initOptionsPicker() {
// TODO
// change the options dynamically depending on the current chosen location
views.shareLocationOptionsPicker.render(LocationSharingOption.USER_CURRENT)
views.shareLocationOptionsPicker.optionPinned.debouncedClicks {
// TODO
}
views.shareLocationOptionsPicker.optionUserCurrent.debouncedClicks {
viewModel.handle(LocationSharingAction.OnShareLocation)
}
views.shareLocationOptionsPicker.optionUserLive.debouncedClicks {
// TODO
}
}

private fun updateUserAvatar(userItem: MatrixItem.UserItem?) {
userItem?.takeUnless { hasRenderedUserAvatar }
?.let {
hasRenderedUserAvatar = true
avatarRenderer.render(it, views.shareLocationOptionsPicker.optionUserCurrent.iconView)
val tintColor = matrixItemColorProvider.getColor(it)
views.shareLocationOptionsPicker.optionUserCurrent.setIconBackgroundTint(tintColor)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.toMatrixItem

class LocationSharingViewModel @AssistedInject constructor(
@Assisted private val initialState: LocationSharingViewState,
Expand All @@ -45,9 +46,14 @@ class LocationSharingViewModel @AssistedInject constructor(

init {
locationTracker.start(this)
setUserItem()
createPin()
}

private fun setUserItem() {
setState { copy(userItem = session.getUser(session.myUserId)?.toMatrixItem()) }
}

private fun createPin() {
locationPinProvider.create(session.myUserId) {
setState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.graphics.drawable.Drawable
import androidx.annotation.StringRes
import com.airbnb.mvrx.MavericksState
import im.vector.app.R
import org.matrix.android.sdk.api.util.MatrixItem

enum class LocationSharingMode(@StringRes val titleRes: Int) {
STATIC_SHARING(R.string.location_activity_title_static_sharing),
Expand All @@ -29,6 +30,7 @@ enum class LocationSharingMode(@StringRes val titleRes: Int) {
data class LocationSharingViewState(
val roomId: String,
val mode: LocationSharingMode,
val userItem: MatrixItem.UserItem? = null,
val lastKnownLocation: LocationData? = null,
val pinDrawable: Drawable? = null
) : MavericksState {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.app.features.location.option

enum class LocationSharingOption {
/**
* Current user's location.
*/
USER_CURRENT,

/**
* User's location during a certain amount of time.
*/
USER_LIVE,

/**
* Static location pinned by the user.
*/
PINNED
}
Loading

0 comments on commit f12afe0

Please sign in to comment.