feat: updating call layout

This commit is contained in:
jubb
2021-11-12 17:07:39 +11:00
parent 3d0e5541d0
commit b6c53b4964
12 changed files with 173 additions and 45 deletions

View File

@@ -54,7 +54,6 @@
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" tools:node="remove"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" tools:node="remove"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<queries> <queries>
<intent> <intent>

View File

@@ -5,11 +5,14 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.graphics.drawable.ColorDrawable
import android.media.AudioManager import android.media.AudioManager
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
@@ -65,6 +68,14 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent?.action == ACTION_ANSWER) {
val answerIntent = WebRtcCallService.acceptCallIntent(this)
ContextCompat.startForegroundService(this,answerIntent)
}
}
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
super.onCreate(savedInstanceState, ready) super.onCreate(savedInstanceState, ready)
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
@@ -82,9 +93,18 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
.execute() .execute()
if (intent.action == ACTION_ANSWER) { if (intent.action == ACTION_ANSWER) {
// answer via ViewModel
val answerIntent = WebRtcCallService.acceptCallIntent(this) val answerIntent = WebRtcCallService.acceptCallIntent(this)
startService(answerIntent) ContextCompat.startForegroundService(this,answerIntent)
}
acceptCallButton.setOnClickListener {
val answerIntent = WebRtcCallService.acceptCallIntent(this)
ContextCompat.startForegroundService(this,answerIntent)
}
declineCallButton.setOnClickListener {
val declineIntent = WebRtcCallService.hangupIntent(this)
startService(declineIntent)
} }
registerReceiver(object: BroadcastReceiver() { registerReceiver(object: BroadcastReceiver() {
@@ -128,7 +148,20 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
launch { launch {
viewModel.callState.collect { state -> viewModel.callState.collect { state ->
remote_loading_view.isVisible = state != CALL_CONNECTED when (state) {
CALL_RINGING -> {
}
CALL_OUTGOING -> {
}
CALL_CONNECTED -> {
}
}
controlGroup.isVisible = state in listOf(CALL_CONNECTED, CALL_OUTGOING, CALL_INCOMING)
remote_loading_view.isVisible = state !in listOf(CALL_CONNECTED, CALL_RINGING)
incomingControlGroup.isVisible = state == CALL_RINGING
} }
} }
@@ -165,9 +198,10 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
} }
local_renderer.isVisible = isEnabled local_renderer.isVisible = isEnabled
enableCameraButton.setImageResource( enableCameraButton.setImageResource(
if (isEnabled) R.drawable.ic_baseline_videocam_off_24 if (isEnabled) R.drawable.ic_outline_videocam_off_24
else R.drawable.ic_baseline_videocam_24 else R.drawable.ic_outline_videocam_24
) )
enableCameraButton.styleEnabled(isEnabled)
} }
} }
@@ -184,7 +218,15 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
} }
} }
fun getUserDisplayName(publicKey: String): String { fun View.styleEnabled(isEnabled: Boolean) {
if (isEnabled) {
setBackgroundResource(R.drawable.call_controls_selected)
} else {
setBackgroundResource(R.drawable.call_controls_unselected)
}
}
private fun getUserDisplayName(publicKey: String): String {
val contact = DatabaseComponent.get(this).sessionContactDatabase().getContactWithSessionID(publicKey) val contact = DatabaseComponent.get(this).sessionContactDatabase().getContactWithSessionID(publicKey)
return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
} }

View File

@@ -337,23 +337,23 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
val recipient = callManager.recipient ?: return val recipient = callManager.recipient ?: return
val timestamp = callManager.pendingOfferTime val timestamp = callManager.pendingOfferTime
setCallInProgressNotification(TYPE_INCOMING_CONNECTING, recipient)
intent.putExtra(EXTRA_CALL_ID, callId) intent.putExtra(EXTRA_CALL_ID, callId)
intent.putExtra(EXTRA_RECIPIENT_ADDRESS, recipient.address) intent.putExtra(EXTRA_RECIPIENT_ADDRESS, recipient.address)
intent.putExtra(EXTRA_REMOTE_DESCRIPTION, pending) intent.putExtra(EXTRA_REMOTE_DESCRIPTION, pending)
intent.putExtra(EXTRA_TIMESTAMP, timestamp) intent.putExtra(EXTRA_TIMESTAMP, timestamp)
callManager.postConnectionEvent(STATE_ANSWERING) callManager.postConnectionEvent(STATE_ANSWERING)
callManager.postViewModelState(CallViewModel.State.CALL_INCOMING)
if (isIncomingMessageExpired(intent)) { if (isIncomingMessageExpired(intent)) {
insertMissedCall(recipient, true) insertMissedCall(recipient, true)
terminate() terminate()
return return
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setCallInProgressNotification(TYPE_INCOMING_CONNECTING, recipient)
}
timeoutExecutor.schedule(TimeoutRunnable(callId, this), 5, TimeUnit.MINUTES) timeoutExecutor.schedule(TimeoutRunnable(callId, this), 2, TimeUnit.MINUTES)
callManager.initializeAudioForCall() callManager.initializeAudioForCall()
callManager.initializeVideo(this) callManager.initializeVideo(this)
@@ -370,7 +370,6 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
terminate() terminate()
} }
} }
callManager.postViewModelState(CallViewModel.State.CALL_INCOMING)
lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING) lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG,e) Log.e(TAG,e)

View File

@@ -46,6 +46,7 @@ class CallNotificationBuilder {
} }
TYPE_INCOMING_RINGING -> { TYPE_INCOMING_RINGING -> {
builder.setContentText(context.getString(R.string.NotificationBarManager__incoming_signal_call)) builder.setContentText(context.getString(R.string.NotificationBarManager__incoming_signal_call))
.setCategory(NotificationCompat.CATEGORY_CALL)
builder.addAction(getServiceNotificationAction( builder.addAction(getServiceNotificationAction(
context, context,
WebRtcCallService.ACTION_DENY_CALL, WebRtcCallService.ACTION_DENY_CALL,

View File

@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.webrtc.audio package org.thoughtcrime.securesms.webrtc.audio
import android.Manifest
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothHeadset import android.bluetooth.BluetoothHeadset
@@ -32,8 +33,6 @@ class SignalBluetoothManager(
} }
private set private set
private fun hasPermission() = false
private var bluetoothAdapter: BluetoothAdapter? = null private var bluetoothAdapter: BluetoothAdapter? = null
private var bluetoothDevice: BluetoothDevice? = null private var bluetoothDevice: BluetoothDevice? = null
private var bluetoothHeadset: BluetoothHeadset? = null private var bluetoothHeadset: BluetoothHeadset? = null
@@ -92,7 +91,7 @@ class SignalBluetoothManager(
Log.d(TAG, "stop(): state: $state") Log.d(TAG, "stop(): state: $state")
if (bluetoothAdapter == null || !hasPermission()) { if (bluetoothAdapter == null) {
return return
} }
@@ -125,7 +124,6 @@ class SignalBluetoothManager(
fun startScoAudio(): Boolean { fun startScoAudio(): Boolean {
handler.assertHandlerThread() handler.assertHandlerThread()
if (!hasPermission()) return false
Log.i(TAG, "startScoAudio(): $state attempts: $scoConnectionAttempts") Log.i(TAG, "startScoAudio(): $state attempts: $scoConnectionAttempts")
@@ -150,7 +148,6 @@ class SignalBluetoothManager(
fun stopScoAudio() { fun stopScoAudio() {
handler.assertHandlerThread() handler.assertHandlerThread()
if (!hasPermission()) return
Log.i(TAG, "stopScoAudio(): $state") Log.i(TAG, "stopScoAudio(): $state")
@@ -166,7 +163,6 @@ class SignalBluetoothManager(
fun updateDevice() { fun updateDevice() {
handler.assertHandlerThread() handler.assertHandlerThread()
if (!hasPermission()) return
Log.d(TAG, "updateDevice(): state: $state") Log.d(TAG, "updateDevice(): state: $state")
@@ -200,7 +196,6 @@ class SignalBluetoothManager(
private fun onBluetoothTimeout() { private fun onBluetoothTimeout() {
Log.i(TAG, "onBluetoothTimeout: state: $state bluetoothHeadset: $bluetoothHeadset") Log.i(TAG, "onBluetoothTimeout: state: $state bluetoothHeadset: $bluetoothHeadset")
if (!hasPermission()) return
if (state == State.UNINITIALIZED || bluetoothHeadset == null || state != State.CONNECTING) { if (state == State.UNINITIALIZED || bluetoothHeadset == null || state != State.CONNECTING) {
return return

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval"
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/unimportant"/>
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval"
xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:color="@color/unimportant" android:width="@dimen/small_spacing"/>
</shape>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M15,8v8H5V8h10m1,-2H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4V7c0,-0.55 -0.45,-1 -1,-1z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M9.56,8l-2,-2 -4.15,-4.14L2,3.27 4.73,6L4,6c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.21,0 0.39,-0.08 0.55,-0.18L19.73,21l1.41,-1.41 -8.86,-8.86L9.56,8zM5,16L5,8h1.73l8,8L5,16zM15,8v2.61l6,6L21,6.5l-4,4L17,7c0,-0.55 -0.45,-1 -1,-1h-5.61l2,2L15,8z"/>
</vector>

View File

@@ -7,29 +7,39 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<FrameLayout <FrameLayout
android:id="@+id/remote_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<FrameLayout <FrameLayout
android:id="@+id/remote_renderer" android:id="@+id/remote_renderer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/> app:layout_constraintTop_toTopOf="parent" />
<com.github.ybq.android.spinkit.SpinKitView </FrameLayout>
android:id="@+id/remote_loading_view"
style="@style/SpinKitView.Large.ThreeBounce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:SpinKit_Color="@color/text"
android:layout_gravity="center"
tools:visibility="visible"
android:visibility="gone" />
<com.makeramen.roundedimageview.RoundedImageView <com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/remote_recipient" android:id="@+id/remote_recipient"
android:layout_gravity="center" app:layout_constraintStart_toStartOf="@id/remote_parent"
app:layout_constraintEnd_toEndOf="@id/remote_parent"
app:layout_constraintTop_toTopOf="@id/remote_parent"
app:layout_constraintBottom_toBottomOf="@id/remote_parent"
android:layout_width="@dimen/extra_large_profile_picture_size" android:layout_width="@dimen/extra_large_profile_picture_size"
android:layout_height="@dimen/extra_large_profile_picture_size"/> android:layout_height="@dimen/extra_large_profile_picture_size"/>
</FrameLayout>
<com.github.ybq.android.spinkit.SpinKitView
android:id="@+id/remote_loading_view"
style="@style/SpinKitView.ThreeBounce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:foregroundGravity="center"
android:visibility="gone"
app:SpinKit_Color="@color/text"
app:layout_constraintEnd_toEndOf="@+id/remote_recipient"
app:layout_constraintStart_toStartOf="@+id/remote_recipient"
app:layout_constraintTop_toBottomOf="@id/remote_recipient"
tools:visibility="visible" />
<FrameLayout <FrameLayout
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@@ -59,7 +69,7 @@
android:id="@+id/endCallButton" android:id="@+id/endCallButton"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
android:src="@drawable/ic_baseline_call_end_24" android:src="@drawable/ic_baseline_call_end_24"
android:padding="@dimen/small_spacing" android:padding="@dimen/medium_spacing"
app:tint="@color/core_white" app:tint="@color/core_white"
android:backgroundTint="@color/destructive" android:backgroundTint="@color/destructive"
android:layout_width="@dimen/large_button_height" android:layout_width="@dimen/large_button_height"
@@ -74,12 +84,12 @@
android:id="@+id/switchCameraButton" android:id="@+id/switchCameraButton"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
android:src="@drawable/ic_baseline_flip_camera_android_24" android:src="@drawable/ic_baseline_flip_camera_android_24"
android:padding="@dimen/small_spacing" android:padding="@dimen/medium_spacing"
app:tint="@color/unimportant" app:tint="@color/unimportant"
android:backgroundTint="@color/unimportant_button_background" android:backgroundTint="@color/unimportant_button_background"
android:layout_width="@dimen/large_button_height" android:layout_width="@dimen/large_button_height"
android:layout_height="@dimen/large_button_height" android:layout_height="@dimen/large_button_height"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toTopOf="@+id/endCallButton"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="@dimen/large_spacing" android:layout_marginBottom="@dimen/large_spacing"
@@ -90,44 +100,87 @@
android:id="@+id/enableCameraButton" android:id="@+id/enableCameraButton"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
android:src="@drawable/ic_baseline_videocam_24" android:src="@drawable/ic_baseline_videocam_24"
android:padding="@dimen/small_spacing" android:padding="@dimen/medium_spacing"
app:tint="@color/unimportant" app:tint="@color/unimportant"
android:backgroundTint="@color/unimportant_button_background" android:backgroundTint="@color/unimportant_button_background"
android:layout_width="@dimen/large_button_height" android:layout_width="@dimen/large_button_height"
android:layout_height="@dimen/large_button_height" android:layout_height="@dimen/large_button_height"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toTopOf="@+id/endCallButton"
app:layout_constraintStart_toEndOf="@id/switchCameraButton" app:layout_constraintStart_toEndOf="@id/switchCameraButton"
app:layout_constraintEnd_toStartOf="@id/endCallButton" app:layout_constraintEnd_toStartOf="@id/microphoneButton"
android:layout_marginBottom="@dimen/large_spacing" android:layout_marginBottom="@dimen/large_spacing"
/> />
<ImageView <ImageView
android:id="@+id/microphoneButton"
android:layout_width="@dimen/large_button_height" android:layout_width="@dimen/large_button_height"
android:layout_height="@dimen/large_button_height" android:layout_height="@dimen/large_button_height"
android:padding="@dimen/small_spacing" android:padding="@dimen/medium_spacing"
android:src="@drawable/ic_microphone" android:src="@drawable/ic_microphone"
app:tint="@color/unimportant" app:tint="@color/unimportant"
android:layout_marginBottom="@dimen/large_spacing" android:layout_marginBottom="@dimen/large_spacing"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toTopOf="@+id/endCallButton"
android:backgroundTint="@color/unimportant_button_background" android:backgroundTint="@color/unimportant_button_background"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
app:layout_constraintEnd_toStartOf="@id/speakerPhoneButton" app:layout_constraintEnd_toStartOf="@id/speakerPhoneButton"
app:layout_constraintStart_toEndOf="@id/endCallButton"/> app:layout_constraintStart_toEndOf="@id/enableCameraButton"/>
<ImageView <ImageView
android:id="@+id/speakerPhoneButton" android:id="@+id/speakerPhoneButton"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
android:src="@drawable/ic_audio_light" android:src="@drawable/ic_audio_light"
android:padding="@dimen/small_spacing" android:padding="@dimen/medium_spacing"
app:tint="@color/unimportant" app:tint="@color/unimportant"
android:backgroundTint="@color/unimportant_button_background" android:backgroundTint="@color/unimportant_button_background"
android:layout_width="@dimen/large_button_height" android:layout_width="@dimen/large_button_height"
android:layout_height="@dimen/large_button_height" android:layout_height="@dimen/large_button_height"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toTopOf="@+id/endCallButton"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="@dimen/large_spacing" android:layout_marginBottom="@dimen/large_spacing"
app:layout_constraintHorizontal_bias="0.9" app:layout_constraintHorizontal_bias="0.9"
/> />
<TextView
android:textAllCaps="true"
style="@style/Widget.Session.Button.Common.ProminentOutline"
android:text="@string/accept_call"
android:layout_marginBottom="@dimen/large_spacing"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.85"
android:padding="@dimen/medium_spacing"
android:gravity="center"
android:id="@+id/acceptCallButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:textAllCaps="true"
style="@style/Widget.Session.Button.Common.UnimportantDestructive"
android:text="@string/decline_call"
android:layout_marginBottom="@dimen/large_spacing"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.15"
android:padding="@dimen/medium_spacing"
android:id="@+id/declineCallButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.constraintlayout.widget.Group
android:id="@+id/controlGroup"
android:layout_width="0dp"
android:layout_height="0dp"
app:constraint_referenced_ids="enableCameraButton,endCallButton,switchCameraButton,speakerPhoneButton,microphoneButton"
/>
<androidx.constraintlayout.widget.Group
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/incomingControlGroup"
app:constraint_referenced_ids="acceptCallButton,declineCallButton"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -911,5 +911,7 @@
<string name="NotificationBarManager__cancel_call">Cancel call</string> <string name="NotificationBarManager__cancel_call">Cancel call</string>
<string name="NotificationBarManager__establishing_signal_call">Establishing call</string> <string name="NotificationBarManager__establishing_signal_call">Establishing call</string>
<string name="NotificationBarManager__end_call">End call</string> <string name="NotificationBarManager__end_call">End call</string>
<string name="accept_call">Accept Call</string>
<string name="decline_call">Decline call</string>
</resources> </resources>

View File

@@ -95,6 +95,13 @@
<item name="android:drawableTint" tools:ignore="NewApi">?android:textColorPrimary</item> <item name="android:drawableTint" tools:ignore="NewApi">?android:textColorPrimary</item>
</style> </style>
<style name="Widget.Session.Button.Common.UnimportantDestructive">
<item name="android:background">@drawable/unimportant_outline_button_medium_background</item>
<item name="android:textColor">@color/destructive</item>
<item name="android:backgroundTint" tools:ignore="NewApi">@color/destructive</item>
<item name="android:drawableTint" tools:ignore="NewApi">@color/destructive</item>
</style>
<style name="Widget.Session.Button.Dialog" parent=""> <style name="Widget.Session.Button.Dialog" parent="">
<item name="android:textAllCaps">false</item> <item name="android:textAllCaps">false</item>
<item name="android:textSize">@dimen/small_font_size</item> <item name="android:textSize">@dimen/small_font_size</item>