diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationBottomSheetController.kt new file mode 100644 index 0000000000..44caba19ca --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationBottomSheetController.kt @@ -0,0 +1,77 @@ +/* + * 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.live.map + +import android.content.Context +import com.airbnb.epoxy.EpoxyController +import im.vector.app.R +import im.vector.app.core.date.DateFormatKind +import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.resources.DateProvider +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.resources.toTimestamp +import im.vector.app.core.time.Clock +import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.location.live.map.bottomsheet.liveLocationUserItem +import javax.inject.Inject + +class LiveLocationBottomSheetController @Inject constructor( + private val avatarRenderer: AvatarRenderer, + private val vectorDateFormatter: VectorDateFormatter, + private val stringProvider: StringProvider, + private val clock: Clock, + private val context: Context, +) : EpoxyController() { + + interface Callback { + fun onUserSelected(userId: String) + fun onStopLocationClicked() + } + + private var userLocations: List? = null + var callback: Callback? = null + + fun setData(userLocations: List) { + this.userLocations = userLocations + requestModelBuild() + } + + override fun buildModels() { + val currentUserLocations = userLocations ?: return + val host = this + + currentUserLocations.forEach { liveLocationViewState -> + val remainingTime = getFormattedLocalTimeEndOfLive(liveLocationViewState.endOfLiveTimestampMillis) + liveLocationUserItem { + id(liveLocationViewState.matrixItem.id) + matrixItem(liveLocationViewState.matrixItem) + stringProvider(host.stringProvider) + clock(host.clock) + avatarRenderer(host.avatarRenderer) + remainingTime(remainingTime) + locationUpdateTimeMillis(liveLocationViewState.locationTimestampMillis) + showStopSharingButton(false) + } + } + } + + private fun getFormattedLocalTimeEndOfLive(endOfLiveDateTimestampMillis: Long?): String { + val endOfLiveDateTime = DateProvider.toLocalDateTime(endOfLiveDateTimestampMillis) + val formattedDateTime = endOfLiveDateTime.toTimestamp().let { vectorDateFormatter.format(it, DateFormatKind.MESSAGE_SIMPLE) } + return stringProvider.getString(R.string.location_share_live_until, formattedDateTime) + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationUserItem.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationUserItem.kt new file mode 100644 index 0000000000..e32157ff10 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationUserItem.kt @@ -0,0 +1,107 @@ +/* + * 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.live.map.bottomsheet + +import android.widget.Button +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.epoxy.onClick +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.time.Clock +import im.vector.app.core.utils.TextUtils +import im.vector.app.features.home.AvatarRenderer +import im.vector.lib.core.utils.timer.CountUpTimer +import org.matrix.android.sdk.api.util.MatrixItem +import org.threeten.bp.Duration + +@EpoxyModelClass(layout = R.layout.item_live_location_users_bottom_sheet) +abstract class LiveLocationUserItem : VectorEpoxyModel() { + + interface Callback { + fun onStopSharingClicked() + } + + @EpoxyAttribute + var callback: Callback? = null + + @EpoxyAttribute + lateinit var matrixItem: MatrixItem + + @EpoxyAttribute + lateinit var avatarRenderer: AvatarRenderer + + @EpoxyAttribute + lateinit var stringProvider: StringProvider + + @EpoxyAttribute + lateinit var clock: Clock + + @EpoxyAttribute + var remainingTime: String? = null + + @EpoxyAttribute + var locationUpdateTimeMillis: Long? = null + + @EpoxyAttribute + var showStopSharingButton: Boolean = false + + private var timer: CountUpTimer? = null + + override fun bind(holder: Holder) { + super.bind(holder) + avatarRenderer.render(matrixItem, holder.itemUserAvatarImageView) + holder.itemUserDisplayNameTextView.text = matrixItem.displayName + holder.itemRemainingTimeTextView.text = remainingTime + + holder.itemStopSharingButton.isVisible = showStopSharingButton + if (showStopSharingButton) { + holder.itemStopSharingButton.onClick { + callback?.onStopSharingClicked() + } + } + + timer = CountUpTimer(1000).apply { + tickListener = object : CountUpTimer.TickListener { + override fun onTick(milliseconds: Long) { + holder.itemLastUpdatedAtTextView.text = getFormattedLastUpdatedAt(locationUpdateTimeMillis) + } + } + resume() + } + } + + private fun getFormattedLastUpdatedAt(locationUpdateTimeMillis: Long?): String { + if (locationUpdateTimeMillis == null) return "" + val elapsedTime = clock.epochMillis() - locationUpdateTimeMillis + val duration = Duration.ofMillis(elapsedTime.coerceAtLeast(0L)) + return stringProvider.getString(R.string.live_location_bottom_sheet_last_updated_at, TextUtils.formatDurationWithUnits(stringProvider, duration)) + } + + class Holder : VectorEpoxyHolder() { + val itemUserAvatarImageView by bind(R.id.itemUserAvatarImageView) + val itemUserDisplayNameTextView by bind(R.id.itemUserDisplayNameTextView) + val itemRemainingTimeTextView by bind(R.id.itemRemainingTimeTextView) + val itemLastUpdatedAtTextView by bind(R.id.itemLastUpdatedAtTextView) + val itemStopSharingButton by bind