mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Create RoomWidget feature
This commit is contained in:
parent
91301197ea
commit
9778999a7f
12 changed files with 532 additions and 5 deletions
|
@ -95,6 +95,7 @@ import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragm
|
|||
import im.vector.riotx.features.settings.push.PushGatewaysFragment
|
||||
import im.vector.riotx.features.share.IncomingShareFragment
|
||||
import im.vector.riotx.features.signout.soft.SoftLogoutFragment
|
||||
import im.vector.riotx.features.widgets.WidgetFragment
|
||||
|
||||
@Module
|
||||
interface FragmentModule {
|
||||
|
@ -468,4 +469,9 @@ interface FragmentModule {
|
|||
@IntoMap
|
||||
@FragmentKey(SharedSecuredStorageKeyFragment::class)
|
||||
fun bindSharedSecuredStorageKeyFragment(fragment: SharedSecuredStorageKeyFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(WidgetFragment::class)
|
||||
fun bindWidgetFragment(fragment: WidgetFragment): Fragment
|
||||
}
|
||||
|
|
|
@ -23,21 +23,27 @@ interface WebViewEventListener {
|
|||
*
|
||||
* @param url The url about to be rendered.
|
||||
*/
|
||||
fun pageWillStart(url: String)
|
||||
fun pageWillStart(url: String){
|
||||
//NO-OP
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a loading webview page has started.
|
||||
*
|
||||
* @param url The rendering url.
|
||||
*/
|
||||
fun onPageStarted(url: String)
|
||||
fun onPageStarted(url: String){
|
||||
//NO-OP
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a loading webview page has finished loading but has not been rendered yet.
|
||||
*
|
||||
* @param url The finished url.
|
||||
*/
|
||||
fun onPageFinished(url: String)
|
||||
fun onPageFinished(url: String){
|
||||
//NO-OP
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when an error occurred while loading a page.
|
||||
|
@ -46,7 +52,9 @@ interface WebViewEventListener {
|
|||
* @param errorCode The error code.
|
||||
* @param description The error description.
|
||||
*/
|
||||
fun onPageError(url: String, errorCode: Int, description: String)
|
||||
fun onPageError(url: String, errorCode: Int, description: String){
|
||||
//NO-OP
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a webview load an url
|
||||
|
@ -54,5 +62,7 @@ interface WebViewEventListener {
|
|||
* @param url The url about to be rendered.
|
||||
* @return true if the method needs to manage some custom handling
|
||||
*/
|
||||
fun shouldOverrideUrlLoading(url: String): Boolean
|
||||
fun shouldOverrideUrlLoading(url: String): Boolean{
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.riotx.features.widgets
|
||||
|
||||
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class RoomWidgetAction : VectorViewModelAction
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.riotx.features.widgets
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.PermissionRequest
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebSettings
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.fragments.roomwidgets.WebviewPermissionUtils
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import im.vector.riotx.features.webview.VectorWebViewClient
|
||||
import im.vector.riotx.features.webview.WebViewEventListener
|
||||
import im.vector.riotx.features.widgets.webview.clearAfterWidget
|
||||
import im.vector.riotx.features.widgets.webview.setupForWidget
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_widget.*
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@Parcelize
|
||||
data class WidgetArgs(
|
||||
val widgetId: String
|
||||
) : Parcelable
|
||||
|
||||
class WidgetFragment @Inject constructor(
|
||||
private val viewModelFactory: RoomWidgetViewModel.Factory
|
||||
) : VectorBaseFragment(), RoomWidgetViewModel.Factory by viewModelFactory, WebViewEventListener {
|
||||
|
||||
private val fragmentArgs: WidgetArgs by args()
|
||||
private val viewModel: RoomWidgetViewModel by fragmentViewModel()
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_widget
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
widgetWebView.setupForWidget(this)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
widgetWebView.clearAfterWidget()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
widgetWebView?.let {
|
||||
it.resumeTimers()
|
||||
it.onResume()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
widgetWebView?.let {
|
||||
it.pauseTimers()
|
||||
it.onPause()
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
Timber.v("Invalidate with state: $state")
|
||||
}
|
||||
|
||||
override fun onPageStarted(url: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPageFinished(url: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPageError(url: String, errorCode: Int, description: String) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.riotx.features.widgets
|
||||
|
||||
import im.vector.riotx.core.platform.VectorViewEvents
|
||||
|
||||
sealed class RoomWidgetViewEvents : VectorViewEvents
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.riotx.features.widgets
|
||||
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
|
||||
class RoomWidgetViewModel @AssistedInject constructor(@Assisted initialState: WidgetViewState,
|
||||
private val session: Session)
|
||||
: VectorViewModel<WidgetViewState, RoomWidgetAction, RoomWidgetViewEvents>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: WidgetViewState): RoomWidgetViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomWidgetViewModel, WidgetViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: WidgetViewState): RoomWidgetViewModel? {
|
||||
val factory = when (viewModelContext) {
|
||||
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
|
||||
is ActivityViewModelContext -> viewModelContext.activity as? Factory
|
||||
}
|
||||
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: RoomWidgetAction) {
|
||||
//TODO
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.riotx.features.widgets
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
|
||||
enum class WidgetStatus {
|
||||
UNKNOWN,
|
||||
WIDGET_NOT_ALLOWED,
|
||||
WIDGET_ALLOWED
|
||||
}
|
||||
|
||||
data class WidgetViewState(
|
||||
val widgetId: String,
|
||||
val status: WidgetStatus = WidgetStatus.UNKNOWN,
|
||||
val formattedURL: Async<String> = Uninitialized,
|
||||
val webviewLoadedUrl: Async<String> = Uninitialized,
|
||||
val widgetName: String = "",
|
||||
val canManageWidgets: Boolean = false,
|
||||
val createdByMe: Boolean = false
|
||||
) : MvRxState {
|
||||
|
||||
constructor(widgetArgs: WidgetArgs) : this(widgetId = widgetArgs.widgetId)
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.riotx.features.widgets
|
||||
|
||||
import im.vector.matrix.android.api.util.JsonDict
|
||||
|
||||
// Example of data:
|
||||
// {
|
||||
// "event.data": {
|
||||
// "action": "get_widgets",
|
||||
// "room_id": "!byqyNXFYAGirEulaEm:matrix.org",
|
||||
// "_id": "1526370173321-0.55myregve98-1"
|
||||
// }
|
||||
// }
|
||||
typealias WidgetEventData = Map<String, JsonDict>
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.fragments.roomwidgets
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.webkit.PermissionRequest
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import im.vector.riotx.R
|
||||
|
||||
object WebviewPermissionUtils {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun promptForPermissions(@StringRes title: Int, request: PermissionRequest, context: Context) {
|
||||
val allowedPermissions = request.resources.map {
|
||||
it to false
|
||||
}.toMutableList()
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(title)
|
||||
.setMultiChoiceItems(
|
||||
request.resources.map { webPermissionToHumanReadable(it, context) }.toTypedArray()
|
||||
, null
|
||||
) { _, which, isChecked ->
|
||||
allowedPermissions[which] = allowedPermissions[which].first to isChecked
|
||||
}
|
||||
.setPositiveButton(R.string.room_widget_resource_grant_permission) { _, _->
|
||||
request.grant(allowedPermissions.mapNotNull { perm ->
|
||||
perm.first.takeIf { perm.second }
|
||||
}.toTypedArray())
|
||||
}
|
||||
.setNegativeButton(R.string.room_widget_resource_decline_permission) { _, _ ->
|
||||
request.deny()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun webPermissionToHumanReadable(permission: String, context: Context): String {
|
||||
return when (permission) {
|
||||
PermissionRequest.RESOURCE_AUDIO_CAPTURE -> context.getString(R.string.room_widget_webview_access_microphone)
|
||||
PermissionRequest.RESOURCE_VIDEO_CAPTURE -> context.getString(R.string.room_widget_webview_access_camera)
|
||||
PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID -> context.getString(R.string.room_widget_webview_read_protected_media)
|
||||
else -> permission
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.riotx.features.widgets.webview
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.PermissionRequest
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import im.vector.fragments.roomwidgets.WebviewPermissionUtils
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import im.vector.riotx.features.webview.VectorWebViewClient
|
||||
import im.vector.riotx.features.webview.WebViewEventListener
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun WebView.setupForWidget(webViewEventListener: WebViewEventListener) {
|
||||
// xml value seems ignored
|
||||
this.setBackgroundColor(ThemeUtils.getColor(context, R.attr.vctr_bottom_nav_background_color))
|
||||
|
||||
// clear caches
|
||||
this.clearHistory()
|
||||
this.clearFormData()
|
||||
this.clearCache(true)
|
||||
|
||||
this.settings.let { settings ->
|
||||
// does not cache the data
|
||||
settings.cacheMode = WebSettings.LOAD_NO_CACHE
|
||||
|
||||
// Enable Javascript
|
||||
settings.javaScriptEnabled = true
|
||||
|
||||
// Use WideViewport and Zoom out if there is no viewport defined
|
||||
settings.useWideViewPort = true
|
||||
settings.loadWithOverviewMode = true
|
||||
|
||||
// Enable pinch to zoom without the zoom buttons
|
||||
settings.builtInZoomControls = true
|
||||
|
||||
// Allow use of Local Storage
|
||||
settings.domStorageEnabled = true
|
||||
|
||||
settings.allowFileAccessFromFileURLs = true
|
||||
settings.allowUniversalAccessFromFileURLs = true
|
||||
|
||||
settings.displayZoomControls = false
|
||||
}
|
||||
|
||||
// Permission requests
|
||||
this.webChromeClient = object : WebChromeClient() {
|
||||
override fun onPermissionRequest(request: PermissionRequest) {
|
||||
WebviewPermissionUtils.promptForPermissions(R.string.room_widget_resource_permission_title, request, context)
|
||||
}
|
||||
}
|
||||
this.webViewClient = VectorWebViewClient(webViewEventListener)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val cookieManager = CookieManager.getInstance()
|
||||
cookieManager.setAcceptThirdPartyCookies(this, false)
|
||||
}
|
||||
}
|
||||
|
||||
fun WebView.clearAfterWidget() {
|
||||
// Make sure you remove the WebView from its parent view before doing anything.
|
||||
(this.parent as? ViewGroup)?.removeAllViews()
|
||||
this.webChromeClient = null
|
||||
this.webViewClient = null
|
||||
this.clearHistory()
|
||||
|
||||
// NOTE: clears RAM cache, if you pass true, it will also clear the disk cache.
|
||||
this.clearCache(true)
|
||||
|
||||
// Loading a blank page is optional, but will ensure that the WebView isn't doing anything when you destroy it.
|
||||
this.loadUrl("about:blank")
|
||||
|
||||
this.onPause()
|
||||
this.removeAllViews()
|
||||
|
||||
// NOTE: This pauses JavaScript execution for ALL WebViews,
|
||||
// do not use if you have other WebViews still alive.
|
||||
// If you create another WebView after calling this,
|
||||
// make sure to call mWebView.resumeTimers().
|
||||
this.pauseTimers()
|
||||
|
||||
// NOTE: This can occasionally cause a segfault below API 17 (4.2)
|
||||
this.destroy()
|
||||
}
|
53
vector/src/main/res/layout/fragment_room_widget.xml
Normal file
53
vector/src/main/res/layout/fragment_room_widget.xml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<WebView
|
||||
android:id="@+id/widgetWebView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/widgetProgressBar"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="6dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?colorPrimary"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/widgetErrorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?vctr_bottom_nav_background_color"
|
||||
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:src="@drawable/error" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widgetErrorText"
|
||||
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="?android:textColorPrimary"
|
||||
android:textStyle="bold"
|
||||
tools:text="Fail to load widget " />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
30
vector/src/main/res/menu/menu_room_widget.xml
Normal file
30
vector/src/main/res/menu/menu_room_widget.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:icon="@drawable/ic_refresh_cw"
|
||||
android:iconTint="?attr/vctr_icon_tint_on_dark_action_bar_color"
|
||||
android:title="@string/room_widget_reload"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_widget_open_ext"
|
||||
android:iconTint="?attr/vctr_icon_tint_on_dark_action_bar_color"
|
||||
android:title="@string/room_widget_open_in_browser"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_close"
|
||||
android:icon="@drawable/ic_close_round"
|
||||
android:iconTint="@color/vector_error_color"
|
||||
android:title="@string/delete"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_revoke"
|
||||
android:title="@string/room_widget_revoke_access"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
Loading…
Reference in a new issue