Implement bluetooth device list bottom sheet.
This commit is contained in:
parent
706f513baf
commit
b3b5a5bfe6
@ -3247,6 +3247,11 @@
|
||||
<!-- Element Call Widget - Push to Talk -->
|
||||
<string name="push_to_talk_notification_title">${app_name} Push to Talk</string>
|
||||
<string name="push_to_talk_notification_description">A service is running to communicate with BLE device</string>
|
||||
<string name="push_to_talk_activity_title">Walkie-Talkie Call</string>
|
||||
<string name="action_push_to_talk_configure_device">Configure push to talk device</string>
|
||||
<string name="push_to_talk_bottom_sheet_title">Bluetooth</string>
|
||||
<string name="push_to_talk_device_connected">Connected</string>
|
||||
<string name="push_to_talk_device_disconnected">Disconnected</string>
|
||||
|
||||
<plurals name="room_removed_messages">
|
||||
<item quantity="one">%d message removed</item>
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.app.features.widgets
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class WidgetAction : VectorViewModelAction {
|
||||
@ -27,7 +26,7 @@ sealed class WidgetAction : VectorViewModelAction {
|
||||
object DeleteWidget : WidgetAction()
|
||||
object RevokeWidget : WidgetAction()
|
||||
object OnTermsReviewed : WidgetAction()
|
||||
data class ConnectToBluetoothDevice(val device: BluetoothDevice) : WidgetAction()
|
||||
data class ConnectToBluetoothDevice(val deviceAddress: String) : WidgetAction()
|
||||
object HangupElementCall : WidgetAction()
|
||||
object CloseWidget : WidgetAction()
|
||||
}
|
||||
|
||||
@ -35,7 +35,6 @@ import android.webkit.PermissionRequest
|
||||
import android.webkit.WebMessage
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.view.isInvisible
|
||||
@ -47,30 +46,34 @@ import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||
import im.vector.app.core.platform.OnBackPressed
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.platform.VectorMenuProvider
|
||||
import im.vector.app.core.utils.CheckWebViewPermissionsUseCase
|
||||
import im.vector.app.core.utils.PERMISSIONS_FOR_BLUETOOTH
|
||||
import im.vector.app.core.utils.checkPermissions
|
||||
import im.vector.app.core.utils.onPermissionDeniedDialog
|
||||
import im.vector.app.core.platform.VectorMenuProvider
|
||||
import im.vector.app.core.utils.CheckWebViewPermissionsUseCase
|
||||
import im.vector.app.core.utils.openUrlInExternalBrowser
|
||||
import im.vector.app.core.utils.registerForPermissionsResult
|
||||
import im.vector.app.databinding.FragmentRoomWidgetBinding
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.features.webview.WebEventListener
|
||||
import im.vector.app.features.widgets.ptt.BluetoothLowEnergyDevice
|
||||
import im.vector.app.features.widgets.ptt.BluetoothLowEnergyDeviceScanner
|
||||
import im.vector.app.features.widgets.ptt.BluetoothLowEnergyDevicesBottomSheetController
|
||||
import im.vector.app.features.widgets.ptt.BluetoothLowEnergyService
|
||||
import im.vector.app.features.widgets.webview.WebviewPermissionUtils
|
||||
import im.vector.app.features.widgets.webview.clearAfterWidget
|
||||
import im.vector.app.features.widgets.webview.setupForWidget
|
||||
import im.vector.lib.core.utils.compat.resolveActivityCompat
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.terms.TermsService
|
||||
import timber.log.Timber
|
||||
import java.net.URISyntaxException
|
||||
@ -96,6 +99,7 @@ class WidgetFragment :
|
||||
@Inject lateinit var checkWebViewPermissionsUseCase: CheckWebViewPermissionsUseCase
|
||||
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||
@Inject lateinit var bluetoothLowEnergyDeviceScanner: BluetoothLowEnergyDeviceScanner
|
||||
@Inject lateinit var bluetoothLowEnergyDevicesBottomSheetController: BluetoothLowEnergyDevicesBottomSheetController
|
||||
|
||||
private val fragmentArgs: WidgetArgs by args()
|
||||
private val viewModel: WidgetViewModel by activityViewModel()
|
||||
@ -127,6 +131,12 @@ class WidgetFragment :
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
configureAudioDevice()
|
||||
}
|
||||
views.widgetBluetoothListRecyclerView.configureWith(bluetoothLowEnergyDevicesBottomSheetController, hasFixedSize = false)
|
||||
bluetoothLowEnergyDevicesBottomSheetController.callback = object : BluetoothLowEnergyDevicesBottomSheetController.Callback {
|
||||
override fun onItemSelected(deviceAddress: String) {
|
||||
onBluetoothDeviceSelected(deviceAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.observeViewEvents {
|
||||
@ -175,6 +185,7 @@ class WidgetFragment :
|
||||
viewModel.getPostAPIMediator().clearWebView()
|
||||
}
|
||||
views.widgetWebView.clearAfterWidget()
|
||||
views.widgetBluetoothListRecyclerView.cleanup()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
@ -201,7 +212,8 @@ class WidgetFragment :
|
||||
override fun handlePrepareMenu(menu: Menu) {
|
||||
withState(viewModel) { state ->
|
||||
val widget = state.asyncWidget()
|
||||
menu.findItem(R.id.action_edit)?.isVisible = state.widgetKind != WidgetKind.INTEGRATION_MANAGER
|
||||
menu.findItem(R.id.action_edit)?.isVisible = state.widgetKind !in listOf(WidgetKind.INTEGRATION_MANAGER, WidgetKind.ELEMENT_CALL)
|
||||
menu.findItem(R.id.action_push_to_talk)?.isVisible = state.widgetKind == WidgetKind.ELEMENT_CALL
|
||||
if (widget == null) {
|
||||
menu.findItem(R.id.action_refresh)?.isVisible = false
|
||||
menu.findItem(R.id.action_widget_open_ext)?.isVisible = false
|
||||
@ -251,6 +263,10 @@ class WidgetFragment :
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.action_push_to_talk -> {
|
||||
showBluetoothLowEnergyDevicesBottomSheet()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@ -413,15 +429,12 @@ class WidgetFragment :
|
||||
viewModel.handle(WidgetAction.RevokeWidget)
|
||||
}
|
||||
|
||||
private var deviceListDialog: AlertDialog? = null
|
||||
|
||||
private fun startBluetoothScanning() {
|
||||
val deviceListDialogBuilder = MaterialAlertDialogBuilder(requireContext())
|
||||
val bluetoothDevices = mutableListOf<BluetoothDevice>()
|
||||
|
||||
bluetoothLowEnergyDeviceScanner.callback = object : BluetoothLowEnergyDeviceScanner.Callback {
|
||||
override fun onPairedDeviceFound(device: BluetoothDevice) {
|
||||
onBluetoothDeviceSelected(device)
|
||||
onBluetoothDeviceSelected(device.address)
|
||||
}
|
||||
|
||||
override fun onScanResult(device: BluetoothDevice) {
|
||||
@ -432,24 +445,28 @@ class WidgetFragment :
|
||||
|
||||
bluetoothDevices.add(device)
|
||||
|
||||
deviceListDialogBuilder.setItems(
|
||||
bluetoothDevices.map { it.name + " " + it.address }.toTypedArray()
|
||||
) { _, which ->
|
||||
Timber.d("### WidgetFragment. $which selected")
|
||||
onBluetoothDeviceSelected(bluetoothDevices[which])
|
||||
}
|
||||
|
||||
if (deviceListDialog?.isShowing.orFalse()) {
|
||||
deviceListDialog?.dismiss()
|
||||
}
|
||||
deviceListDialog = deviceListDialogBuilder.show()
|
||||
bluetoothLowEnergyDevicesBottomSheetController.setData(
|
||||
bluetoothDevices.map {
|
||||
BluetoothLowEnergyDevice(
|
||||
name = it.name,
|
||||
macAddress = it.address,
|
||||
isConnected = it.bondState == BluetoothDevice.BOND_BONDED
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
bluetoothLowEnergyDeviceScanner.startScanning()
|
||||
}
|
||||
|
||||
private fun onBluetoothDeviceSelected(device: BluetoothDevice) {
|
||||
viewModel.handle(WidgetAction.ConnectToBluetoothDevice(device))
|
||||
private fun showBluetoothLowEnergyDevicesBottomSheet() {
|
||||
bluetoothLowEnergyDeviceScanner.startScanning()
|
||||
views.bottomSheet.isVisible = true
|
||||
BottomSheetBehavior.from(views.bottomSheet).state = BottomSheetBehavior.STATE_HALF_EXPANDED
|
||||
}
|
||||
|
||||
private fun onBluetoothDeviceSelected(deviceAddress: String) {
|
||||
viewModel.handle(WidgetAction.ConnectToBluetoothDevice(deviceAddress))
|
||||
|
||||
Intent(requireContext(), BluetoothLowEnergyService::class.java).also {
|
||||
ContextCompat.startForegroundService(requireContext(), it)
|
||||
|
||||
@ -161,7 +161,7 @@ class WidgetViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private fun handleConnectToBluetoothDevice(action: WidgetAction.ConnectToBluetoothDevice) {
|
||||
bluetoothLowEnergyServiceConnection.bind(action.device, this)
|
||||
bluetoothLowEnergyServiceConnection.bind(action.deviceAddress, this)
|
||||
}
|
||||
|
||||
private fun handleCloseWidget() {
|
||||
|
||||
@ -34,7 +34,7 @@ enum class WidgetKind(@StringRes val nameRes: Int, val screenId: String?) {
|
||||
ROOM(R.string.room_widget_activity_title, null),
|
||||
STICKER_PICKER(R.string.title_activity_choose_sticker, WidgetType.StickerPicker.preferred),
|
||||
INTEGRATION_MANAGER(0, null),
|
||||
ELEMENT_CALL(0, null);
|
||||
ELEMENT_CALL(R.string.push_to_talk_activity_title, null);
|
||||
|
||||
fun isAdmin(): Boolean {
|
||||
return this == STICKER_PICKER || this == INTEGRATION_MANAGER
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.widgets.ptt
|
||||
|
||||
data class BluetoothLowEnergyDevice(
|
||||
val name: String,
|
||||
val macAddress: String?,
|
||||
val isConnected: Boolean,
|
||||
)
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.widgets.ptt
|
||||
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
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.extensions.setTextOrHide
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class BluetoothLowEnergyDeviceItem : VectorEpoxyModel<BluetoothLowEnergyDeviceItem.Holder>(R.layout.item_bluetooth_device) {
|
||||
|
||||
interface Callback {
|
||||
fun onItemSelected(deviceAddress: String)
|
||||
}
|
||||
|
||||
@EpoxyAttribute
|
||||
var deviceName: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var deviceMacAddress: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var deviceConnectionStatusText: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
@ColorInt
|
||||
var deviceConnectionStatusTextColor: Int? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var callback: Callback? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.bluetoothDeviceNameTextView.setTextOrHide(deviceName)
|
||||
holder.bluetoothDeviceMacAddressTextView.setTextOrHide(deviceMacAddress)
|
||||
holder.bluetoothDeviceConnectionStatusTextView.setTextOrHide(deviceConnectionStatusText)
|
||||
|
||||
deviceConnectionStatusTextColor?.let {
|
||||
holder.bluetoothDeviceConnectionStatusTextView.setTextColor(it)
|
||||
} ?: run {
|
||||
holder.bluetoothDeviceConnectionStatusTextView.setTextColor(ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_primary))
|
||||
}
|
||||
|
||||
holder.view.setOnClickListener {
|
||||
deviceMacAddress?.let {
|
||||
callback?.onItemSelected(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val bluetoothDeviceNameTextView by bind<TextView>(R.id.bluetoothDeviceNameTextView)
|
||||
val bluetoothDeviceMacAddressTextView by bind<TextView>(R.id.bluetoothDeviceMacAddressTextView)
|
||||
val bluetoothDeviceConnectionStatusTextView by bind<TextView>(R.id.bluetoothDeviceConnectionStatusTextView)
|
||||
}
|
||||
}
|
||||
@ -48,6 +48,7 @@ class BluetoothLowEnergyDeviceScanner @Inject constructor(
|
||||
}
|
||||
|
||||
fun startScanning() {
|
||||
stopScanning()
|
||||
bluetoothManager
|
||||
?.adapter
|
||||
?.bondedDevices
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.widgets.ptt
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class BluetoothLowEnergyDevicesBottomSheetController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider,
|
||||
) : EpoxyController() {
|
||||
|
||||
interface Callback {
|
||||
fun onItemSelected(deviceAddress: String)
|
||||
}
|
||||
|
||||
private var deviceList: List<BluetoothLowEnergyDevice>? = null
|
||||
var callback: Callback? = null
|
||||
|
||||
fun setData(deviceList: List<BluetoothLowEnergyDevice>) {
|
||||
this.deviceList = deviceList
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val currentDeviceList = deviceList ?: return
|
||||
val host = this
|
||||
|
||||
currentDeviceList.forEach { device ->
|
||||
val deviceConnectionStatus = host.stringProvider.getString(
|
||||
if (device.isConnected) R.string.push_to_talk_device_connected else R.string.push_to_talk_device_disconnected
|
||||
)
|
||||
val deviceConnectionStatusColor = host.colorProvider.getColorFromAttribute(
|
||||
if (device.isConnected) R.attr.colorPrimary else R.attr.colorError
|
||||
)
|
||||
|
||||
val deviceItemCallback = object : BluetoothLowEnergyDeviceItem.Callback {
|
||||
override fun onItemSelected(deviceAddress: String) {
|
||||
host.callback?.onItemSelected(deviceAddress)
|
||||
}
|
||||
}
|
||||
|
||||
bluetoothLowEnergyDeviceItem {
|
||||
id(device.hashCode())
|
||||
deviceName(device.name)
|
||||
deviceMacAddress(device.macAddress)
|
||||
deviceConnectionStatusText(deviceConnectionStatus)
|
||||
deviceConnectionStatusTextColor(deviceConnectionStatusColor)
|
||||
callback(deviceItemCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.app.features.widgets.ptt
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@ -34,12 +33,12 @@ class BluetoothLowEnergyServiceConnection @Inject constructor(
|
||||
|
||||
private var isBound = false
|
||||
private var bluetoothLowEnergyService: BluetoothLowEnergyService? = null
|
||||
private var bluetoothDevice: BluetoothDevice? = null
|
||||
private var deviceAddress: String? = null
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
fun bind(device: BluetoothDevice, callback: Callback) {
|
||||
this.bluetoothDevice = device
|
||||
fun bind(deviceAddress: String, callback: Callback) {
|
||||
this.deviceAddress = deviceAddress
|
||||
this.callback = callback
|
||||
|
||||
if (!isBound) {
|
||||
@ -54,7 +53,7 @@ class BluetoothLowEnergyServiceConnection @Inject constructor(
|
||||
it.callback = this
|
||||
}
|
||||
|
||||
bluetoothDevice?.address?.let {
|
||||
deviceAddress?.let {
|
||||
bluetoothLowEnergyService?.connect(it)
|
||||
}
|
||||
isBound = true
|
||||
|
||||
@ -1,53 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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="match_parent">
|
||||
|
||||
<WebView
|
||||
android:id="@+id/widgetWebView"
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:background="@android:color/transparent" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/widgetProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
<WebView
|
||||
android:id="@+id/widgetWebView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/widgetErrorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?colorSurface"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/error" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widgetErrorText"
|
||||
style="@style/Widget.Vector.TextView.Subtitle"
|
||||
<ProgressBar
|
||||
android:id="@+id/widgetProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
tools:text="Fail to load widget " />
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/widgetErrorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?colorSurface"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/error" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widgetErrorText"
|
||||
style="@style/Widget.Vector.TextView.Subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
tools:text="Fail to load widget " />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottomSheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?vctr_system"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:behavior_hideable="true"
|
||||
app:behavior_peekHeight="200dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.Vector.Headline.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="30dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:text="@string/push_to_talk_bottom_sheet_title" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?vctr_list_separator" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/widgetBluetoothListRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:listitem="@layout/item_bluetooth_device" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
40
vector/src/main/res/layout/item_bluetooth_device.xml
Normal file
40
vector/src/main/res/layout/item_bluetooth_device.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bluetoothDeviceNameTextView"
|
||||
style="@style/TextAppearance.Vector.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="30dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Device 1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bluetoothDeviceMacAddressTextView"
|
||||
style="@style/TextAppearance.Vector.Caption"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="@id/bluetoothDeviceNameTextView"
|
||||
app:layout_constraintTop_toBottomOf="@id/bluetoothDeviceNameTextView"
|
||||
tools:text="00:1B:44:11:3A:B7" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bluetoothDeviceConnectionStatusTextView"
|
||||
style="@style/TextAppearance.Vector.Body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/bluetoothDeviceMacAddressTextView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/bluetoothDeviceNameTextView"
|
||||
tools:text="Disconnected"
|
||||
tools:textColor="?colorError" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -27,4 +27,10 @@
|
||||
android:title="@string/room_widget_revoke_access"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_push_to_talk"
|
||||
android:title="@string/action_push_to_talk_configure_device"
|
||||
android:icon="@drawable/quantum_ic_bluetooth_audio_grey600_24"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
||||
Loading…
x
Reference in New Issue
Block a user