Merge pull request #5411 from vector-im/feature/mna/PSF-735-sharing-options-view

#5395: Location sharing options view
This commit is contained in:
Maxime NATUREL 2022-03-09 16:10:46 +01:00 committed by GitHub
commit f12afe0ef0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 475 additions and 69 deletions

1
changelog.d/5395.feature Normal file
View file

@ -0,0 +1 @@
Add a custom view to display a picker for share location options

View file

@ -139,4 +139,8 @@
<color name="vctr_presence_indicator_offline_light">@color/palette_gray_100</color> <color name="vctr_presence_indicator_offline_light">@color/palette_gray_100</color>
<color name="vctr_presence_indicator_offline_dark">@color/palette_gray_450</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> </resources>

View file

@ -70,4 +70,7 @@
<item name="ftue_auth_profile_picture_height" format="float" type="dimen">0.15</item> <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> <item name="ftue_auth_profile_picture_icon_height" format="float" type="dimen">0.05</item>
<!-- Location sharing -->
<dimen name="location_sharing_option_default_padding">10dp</dimen>
</resources> </resources>

View file

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

View file

@ -145,6 +145,8 @@
<item name="android:actionButtonStyle">@style/Widget.Vector.ActionButton</item> <item name="android:actionButtonStyle">@style/Widget.Vector.ActionButton</item>
<!-- Location sharing -->
<item name="vctr_live_location">@color/vctr_live_location_dark</item>
</style> </style>
<style name="Theme.Vector.Dark" parent="Base.Theme.Vector.Dark" /> <style name="Theme.Vector.Dark" parent="Base.Theme.Vector.Dark" />

View file

@ -146,6 +146,8 @@
<item name="android:actionButtonStyle">@style/Widget.Vector.ActionButton</item> <item name="android:actionButtonStyle">@style/Widget.Vector.ActionButton</item>
<!-- Location sharing -->
<item name="vctr_live_location">@color/vctr_live_location_light</item>
</style> </style>
<style name="Theme.Vector.Light" parent="Base.Theme.Vector.Light" /> <style name="Theme.Vector.Light" parent="Base.Theme.Vector.Light" />

View file

@ -23,6 +23,7 @@ import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -70,6 +71,15 @@ fun View.setAttributeTintedBackground(@DrawableRes drawableRes: Int, @AttrRes ti
background = drawable 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) { fun ImageView.setAttributeTintedImageResource(@DrawableRes drawableRes: Int, @AttrRes tint: Int) {
val drawable = ContextCompat.getDrawable(context, drawableRes)!! val drawable = ContextCompat.getDrawable(context, drawableRes)!!
DrawableCompat.setTint(drawable, ThemeUtils.getColor(context, tint)) DrawableCompat.setTint(drawable, ThemeUtils.getColor(context, tint))

View file

@ -19,7 +19,9 @@ package im.vector.app.features.home.room.detail.timeline.helper
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable import android.graphics.drawable.LayerDrawable
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import im.vector.app.R import im.vector.app.R
@ -37,7 +39,8 @@ class LocationPinProvider @Inject constructor(
private val context: Context, private val context: Context,
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val dimensionConverter: DimensionConverter, private val dimensionConverter: DimensionConverter,
private val avatarRenderer: AvatarRenderer private val avatarRenderer: AvatarRenderer,
private val matrixItemColorProvider: MatrixItemColorProvider
) { ) {
private val cache = mutableMapOf<String, Drawable>() private val cache = mutableMapOf<String, Drawable>()
@ -61,35 +64,42 @@ class LocationPinProvider @Inject constructor(
return return
} }
activeSessionHolder.getActiveSession().getUser(userId)?.toMatrixItem()?.let { activeSessionHolder
val size = dimensionConverter.dpToPx(44) .getActiveSession()
avatarRenderer.render(glideRequests, it, object : CustomTarget<Drawable>(size, size) { .getUser(userId)
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) { ?.toMatrixItem()
Timber.d("## Location: onResourceReady") ?.let { userItem ->
val pinDrawable = createPinDrawable(resource) val size = dimensionConverter.dpToPx(44)
cache[userId] = pinDrawable val bgTintColor = matrixItemColorProvider.getColor(userItem)
callback(pinDrawable) 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?) { override fun onLoadCleared(placeholder: Drawable?) {
// Is it possible? Put placeholder instead? // Is it possible? Put placeholder instead?
// FIXME The doc says it has to be implemented and should free resources // FIXME The doc says it has to be implemented and should free resources
Timber.d("## Location: onLoadCleared") Timber.d("## Location: onLoadCleared")
} }
override fun onLoadFailed(errorDrawable: Drawable?) { override fun onLoadFailed(errorDrawable: Drawable?) {
Timber.w("## Location: onLoadFailed") Timber.w("## Location: onLoadFailed")
errorDrawable ?: return errorDrawable ?: return
val pinDrawable = createPinDrawable(errorDrawable) val pinDrawable = createPinDrawable(errorDrawable, bgTintColor)
cache[userId] = pinDrawable cache[userId] = pinDrawable
callback(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)!! 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 layerDrawable = LayerDrawable(arrayOf(bgUserPin, drawable))
val horizontalInset = dimensionConverter.dpToPx(4) val horizontalInset = dimensionConverter.dpToPx(4)
val topInset = dimensionConverter.dpToPx(4) val topInset = dimensionConverter.dpToPx(4)

View file

@ -30,6 +30,10 @@ import im.vector.app.R
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentLocationSharingBinding 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 java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
@ -37,7 +41,9 @@ import javax.inject.Inject
* We should consider using SupportMapFragment for a out of the box lifecycle handling * We should consider using SupportMapFragment for a out of the box lifecycle handling
*/ */
class LocationSharingFragment @Inject constructor( class LocationSharingFragment @Inject constructor(
private val urlMapProvider: UrlMapProvider private val urlMapProvider: UrlMapProvider,
private val avatarRenderer: AvatarRenderer,
private val matrixItemColorProvider: MatrixItemColorProvider
) : VectorBaseFragment<FragmentLocationSharingBinding>() { ) : VectorBaseFragment<FragmentLocationSharingBinding>() {
private val viewModel: LocationSharingViewModel by fragmentViewModel() private val viewModel: LocationSharingViewModel by fragmentViewModel()
@ -45,6 +51,8 @@ class LocationSharingFragment @Inject constructor(
// Keep a ref to handle properly the onDestroy callback // Keep a ref to handle properly the onDestroy callback
private var mapView: WeakReference<MapView>? = null private var mapView: WeakReference<MapView>? = null
private var hasRenderedUserAvatar = false
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding {
return FragmentLocationSharingBinding.inflate(inflater, container, false) return FragmentLocationSharingBinding.inflate(inflater, container, false)
} }
@ -59,9 +67,7 @@ class LocationSharingFragment @Inject constructor(
views.mapView.initialize(urlMapProvider.getMapUrl()) views.mapView.initialize(urlMapProvider.getMapUrl())
} }
views.shareLocationContainer.debouncedClicks { initOptionsPicker()
viewModel.handle(LocationSharingAction.OnShareLocation)
}
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {
@ -107,6 +113,12 @@ class LocationSharingFragment @Inject constructor(
super.onDestroy() 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() { private fun handleLocationNotAvailableError() {
MaterialAlertDialogBuilder(requireActivity()) MaterialAlertDialogBuilder(requireActivity())
.setTitle(R.string.location_not_available_dialog_title) .setTitle(R.string.location_not_available_dialog_title)
@ -118,8 +130,28 @@ class LocationSharingFragment @Inject constructor(
.show() .show()
} }
override fun invalidate() = withState(viewModel) { state -> private fun initOptionsPicker() {
views.mapView.render(state.toMapState()) // TODO
views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null // 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)
}
} }
} }

View file

@ -26,6 +26,7 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider 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.session.Session
import org.matrix.android.sdk.api.util.toMatrixItem
class LocationSharingViewModel @AssistedInject constructor( class LocationSharingViewModel @AssistedInject constructor(
@Assisted private val initialState: LocationSharingViewState, @Assisted private val initialState: LocationSharingViewState,
@ -45,9 +46,14 @@ class LocationSharingViewModel @AssistedInject constructor(
init { init {
locationTracker.start(this) locationTracker.start(this)
setUserItem()
createPin() createPin()
} }
private fun setUserItem() {
setState { copy(userItem = session.getUser(session.myUserId)?.toMatrixItem()) }
}
private fun createPin() { private fun createPin() {
locationPinProvider.create(session.myUserId) { locationPinProvider.create(session.myUserId) {
setState { setState {

View file

@ -20,6 +20,7 @@ import android.graphics.drawable.Drawable
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksState
import im.vector.app.R import im.vector.app.R
import org.matrix.android.sdk.api.util.MatrixItem
enum class LocationSharingMode(@StringRes val titleRes: Int) { enum class LocationSharingMode(@StringRes val titleRes: Int) {
STATIC_SHARING(R.string.location_activity_title_static_sharing), STATIC_SHARING(R.string.location_activity_title_static_sharing),
@ -29,6 +30,7 @@ enum class LocationSharingMode(@StringRes val titleRes: Int) {
data class LocationSharingViewState( data class LocationSharingViewState(
val roomId: String, val roomId: String,
val mode: LocationSharingMode, val mode: LocationSharingMode,
val userItem: MatrixItem.UserItem? = null,
val lastKnownLocation: LocationData? = null, val lastKnownLocation: LocationData? = null,
val pinDrawable: Drawable? = null val pinDrawable: Drawable? = null
) : MavericksState { ) : MavericksState {

View file

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

View file

@ -0,0 +1,86 @@
/*
* 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
import android.content.Context
import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.databinding.ViewLocationSharingOptionPickerBinding
/**
* Custom view to display the location sharing option picker.
*/
class LocationSharingOptionPickerView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
val optionPinned: LocationSharingOptionView
get() = binding.locationSharingOptionPinned
val optionUserCurrent: LocationSharingOptionView
get() = binding.locationSharingOptionUserCurrent
val optionUserLive: LocationSharingOptionView
get() = binding.locationSharingOptionUserLive
private val divider1: View
get() = binding.locationSharingOptionsDivider1
private val divider2: View
get() = binding.locationSharingOptionsDivider2
private val binding = ViewLocationSharingOptionPickerBinding.inflate(
LayoutInflater.from(context),
this
)
init {
applyBackground()
}
fun render(vararg options: LocationSharingOption) {
val optionsNumber = options.toSet().size
val isPinnedVisible = options.contains(LocationSharingOption.PINNED)
val isUserCurrentVisible = options.contains(LocationSharingOption.USER_CURRENT)
val isUserLiveVisible = options.contains(LocationSharingOption.USER_LIVE)
optionPinned.isVisible = isPinnedVisible
divider1.isVisible = isPinnedVisible && optionsNumber > 1
optionUserCurrent.isVisible = isUserCurrentVisible
divider2.isVisible = isUserCurrentVisible && isUserLiveVisible
optionUserLive.isVisible = isUserLiveVisible
}
private fun applyBackground() {
val outValue = TypedValue()
context.theme.resolveAttribute(
R.attr.colorSurface,
outValue,
true
)
binding.root.background = ContextCompat.getDrawable(
context,
outValue.resourceId
)
}
}

View file

@ -0,0 +1,91 @@
/*
* 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
import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.setPadding
import im.vector.app.R
import im.vector.app.core.extensions.tintBackground
import im.vector.app.databinding.ViewLocationSharingOptionBinding
/**
* Custom view to display a location sharing option.
*/
class LocationSharingOptionView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
val iconView: ImageView
get() = binding.shareLocationOptionIcon
private val binding = ViewLocationSharingOptionBinding.inflate(
LayoutInflater.from(context),
this
)
init {
context.theme.obtainStyledAttributes(
attrs,
R.styleable.LocationSharingOptionView,
0,
0
).run {
try {
setIcon(this)
setTitle(this)
} finally {
recycle()
}
}
}
fun setIconBackgroundTint(@ColorInt color: Int) {
binding.shareLocationOptionIcon.tintBackground(color)
}
private fun setIcon(typedArray: TypedArray) {
val icon = typedArray.getDrawable(R.styleable.LocationSharingOptionView_locShareIcon)
val background = typedArray.getDrawable(R.styleable.LocationSharingOptionView_locShareIconBackground)
val backgroundTint = typedArray.getColor(
R.styleable.LocationSharingOptionView_locShareIconBackgroundTint,
ContextCompat.getColor(context, android.R.color.transparent)
)
val padding = typedArray.getDimensionPixelOffset(
R.styleable.LocationSharingOptionView_locShareIconPadding,
context.resources.getDimensionPixelOffset(R.dimen.location_sharing_option_default_padding)
)
val description = typedArray.getString(R.styleable.LocationSharingOptionView_locShareIconDescription)
iconView.setImageDrawable(icon)
iconView.background = background
iconView.tintBackground(backgroundTint)
iconView.setPadding(padding)
iconView.contentDescription = description
}
private fun setTitle(typedArray: TypedArray) {
val title = typedArray.getString(R.styleable.LocationSharingOptionView_locShareTitle)
binding.shareLocationOptionTitle.text = title
}
}

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="16dp"
android:viewportWidth="30"
android:viewportHeight="16">
<path
android:pathData="M4.6144,14.8367C4.9699,14.4812 5.0048,13.9165 4.6841,13.533C2.007,10.2772 2.007,5.5504 4.6771,2.2877C4.9978,1.8973 4.9699,1.3256 4.6144,0.97C4.2031,0.5587 3.5198,0.5866 3.1503,1.0398C-0.1054,5.0206 -0.1054,10.7792 3.1503,14.767C3.5129,15.2202 4.1961,15.255 4.6144,14.8367ZM7.424,12.0271C7.7656,11.6855 7.8004,11.1487 7.5146,10.7513C6.3085,9.0502 6.3085,6.7635 7.5146,5.0624C7.7935,4.665 7.7656,4.1282 7.424,3.7866L7.417,3.7796C6.9987,3.3613 6.2876,3.3892 5.946,3.8703C4.21,6.2685 4.21,9.5382 5.946,11.9435C6.2945,12.4245 6.9987,12.4524 7.424,12.0271ZM15.3488,1.3771C12.6508,1.3771 10.4686,3.6186 10.4686,6.3901C10.4686,9.3764 13.5501,13.4943 14.819,15.0626C15.0978,15.4064 15.6068,15.4064 15.8856,15.0626C17.1475,13.4943 20.229,9.3764 20.229,6.3901C20.229,3.6186 18.0468,1.3771 15.3488,1.3771ZM15.3488,8.1805C14.3867,8.1805 13.6059,7.3784 13.6059,6.3901C13.6059,5.4018 14.3867,4.5997 15.3488,4.5997C16.3109,4.5997 17.0917,5.4018 17.0917,6.3901C17.0917,7.3784 16.3109,8.1805 15.3488,8.1805ZM25.3431,13.533C25.0224,13.9165 25.0573,14.4812 25.4128,14.8367C25.8311,15.255 26.5144,15.2202 26.8769,14.767C30.1327,10.7792 30.1327,5.0206 26.8769,1.0398C26.5074,0.5866 25.8242,0.5587 25.4128,0.97C25.0573,1.3256 25.0294,1.8973 25.3501,2.2877C28.0203,5.5504 28.0203,10.2772 25.3431,13.533ZM22.5126,10.7513C22.2268,11.1487 22.2616,11.6855 22.6033,12.0271C23.0285,12.4524 23.7327,12.4245 24.0813,11.9435C25.8172,9.5382 25.8172,6.2685 24.0813,3.8703C23.7396,3.3892 23.0285,3.3613 22.6102,3.7796L22.6033,3.7866C22.2616,4.1282 22.2338,4.665 22.5126,5.0624C23.7187,6.7635 23.7187,9.0502 22.5126,10.7513Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -9,52 +9,26 @@
android:id="@+id/mapView" android:id="@+id/mapView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/shareLocationContainer" app:layout_constraintBottom_toTopOf="@id/shareLocationOptionsPicker"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:mapbox_renderTextureMode="true" app:mapbox_renderTextureMode="true"
tools:background="#4F00" /> tools:background="#4F00" />
<androidx.constraintlayout.helper.widget.Flow <im.vector.app.features.location.option.LocationSharingOptionPickerView
android:id="@+id/shareLocationContainer" android:id="@+id/shareLocationOptionsPicker"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="72dp" android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingEnd="@dimen/layout_horizontal_margin"
app:constraint_referenced_ids="shareLocationImageView,shareLocationText"
app:flow_horizontalBias="0"
app:flow_horizontalGap="8dp"
app:flow_horizontalStyle="packed"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/shareLocationImageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/circle"
android:backgroundTint="?colorPrimary"
android:contentDescription="@string/a11y_location_share_icon"
android:padding="10dp"
android:src="@drawable/ic_attachment_location_white" />
<TextView
android:id="@+id/shareLocationText"
style="@style/TextAppearance.Vector.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/location_share"
android:textColor="?colorPrimary"
android:textStyle="bold" />
<ProgressBar <ProgressBar
android:id="@+id/shareLocationGpsLoading" android:id="@+id/shareLocationGpsLoading"
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="32dp" android:layout_height="32dp"
android:layout_marginEnd="@dimen/layout_horizontal_margin" android:layout_marginEnd="@dimen/layout_horizontal_margin"
app:layout_constraintBottom_toBottomOf="@id/shareLocationContainer" android:layout_marginBottom="@dimen/layout_vertical_margin"
app:layout_constraintEnd_toEndOf="@id/shareLocationContainer" app:layout_constraintBottom_toBottomOf="@id/shareLocationOptionsPicker"
app:layout_constraintTop_toTopOf="@id/shareLocationContainer" /> app:layout_constraintEnd_toEndOf="@id/shareLocationOptionsPicker" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/shareLocationOptionContainer"
android:layout_width="0dp"
android:layout_height="72dp"
android:background="?selectableItemBackground"
android:duplicateParentState="true"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingEnd="@dimen/layout_horizontal_margin"
app:constraint_referenced_ids="shareLocationOptionIcon,shareLocationOptionTitle"
app:flow_horizontalBias="0"
app:flow_horizontalGap="8dp"
app:flow_horizontalStyle="packed"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/shareLocationOptionIcon"
android:layout_width="40dp"
android:layout_height="40dp"
android:contentDescription="@string/a11y_location_share_option_pinned_icon"
tools:background="@drawable/circle"
tools:backgroundTint="?colorPrimary"
tools:padding="11dp"
tools:src="@drawable/ic_attachment_location_white" />
<TextView
android:id="@+id/shareLocationOptionTitle"
style="@style/TextAppearance.Vector.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?vctr_content_primary"
tools:text="@string/location_share_option_pinned" />
</merge>

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<im.vector.app.features.location.option.LocationSharingOptionView
android:id="@+id/locationSharingOptionPinned"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/locationSharingOptionUserCurrent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:locShareIcon="@drawable/ic_attachment_location_white"
app:locShareIconBackground="@drawable/circle"
app:locShareIconBackgroundTint="?colorPrimary"
app:locShareIconDescription="@string/a11y_location_share_option_pinned_icon"
app:locShareIconPadding="11dp"
app:locShareTitle="@string/location_share_option_pinned" />
<View
android:id="@+id/locationSharingOptionsDivider1"
android:layout_width="0dp"
android:layout_height="1dp"
android:alpha="0.15"
android:background="?vctr_content_secondary"
app:layout_constraintBottom_toTopOf="@id/locationSharingOptionUserCurrent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationSharingOptionPinned" />
<im.vector.app.features.location.option.LocationSharingOptionView
android:id="@+id/locationSharingOptionUserCurrent"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/locationSharingOptionUserLive"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationSharingOptionPinned"
app:locShareIconBackground="@drawable/circle"
app:locShareIconBackgroundTint="?colorPrimary"
app:locShareIconDescription="@string/a11y_location_share_option_user_current_icon"
app:locShareIconPadding="3dp"
app:locShareTitle="@string/location_share_option_user_current" />
<View
android:id="@+id/locationSharingOptionsDivider2"
android:layout_width="0dp"
android:layout_height="1dp"
android:alpha="0.15"
android:background="?vctr_content_secondary"
app:layout_constraintBottom_toTopOf="@id/locationSharingOptionUserLive"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationSharingOptionUserCurrent" />
<im.vector.app.features.location.option.LocationSharingOptionView
android:id="@+id/locationSharingOptionUserLive"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationSharingOptionUserCurrent"
app:locShareIcon="@drawable/ic_attachment_location_live_white"
app:locShareIconBackground="@drawable/circle"
app:locShareIconBackgroundTint="?vctr_live_location"
app:locShareIconDescription="@string/a11y_location_share_option_user_live_icon"
app:locShareIconPadding="3dp"
app:locShareTitle="@string/location_share_option_user_live" />
</merge>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="notice_room_invite_no_invitee">%s\'s invitation</string> <string name="notice_room_invite_no_invitee">%s\'s invitation</string>
<string name="notice_room_invite_no_invitee_by_you">Your invitation</string> <string name="notice_room_invite_no_invitee_by_you">Your invitation</string>
<string name="notice_room_created">%1$s created the room</string> <string name="notice_room_created">%1$s created the room</string>
@ -2924,9 +2924,17 @@
<!-- Location --> <!-- Location -->
<string name="location_activity_title_static_sharing">Share location</string> <string name="location_activity_title_static_sharing">Share location</string>
<string name="location_activity_title_preview">Location</string> <string name="location_activity_title_preview">Location</string>
<string name="a11y_location_share_icon">Share location</string> <!-- TODO delete -->
<string name="a11y_location_share_icon" tools:ignore="UnusedResources">Share location</string>
<string name="a11y_static_map_image">Map</string> <string name="a11y_static_map_image">Map</string>
<string name="location_share">Share location</string> <!-- TODO delete -->
<string name="location_share" tools:ignore="UnusedResources">Share location</string>
<string name="location_share_option_user_current">Share my current location</string>
<string name="a11y_location_share_option_user_current_icon">Share my current location</string>
<string name="location_share_option_user_live">Share live location</string>
<string name="a11y_location_share_option_user_live_icon">Share live location</string>
<string name="location_share_option_pinned">Share this location</string>
<string name="a11y_location_share_option_pinned_icon">Share this location</string>
<string name="location_not_available_dialog_title">${app_name} could not access your location</string> <string name="location_not_available_dialog_title">${app_name} could not access your location</string>
<string name="location_not_available_dialog_content">${app_name} could not access your location. Please try again later.</string> <string name="location_not_available_dialog_content">${app_name} could not access your location. Please try again later.</string>
<string name="location_share_external">Open with</string> <string name="location_share_external">Open with</string>