summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorAleksandr Granin <aleksandr@mullvad.net>2021-03-01 14:55:08 +0100
committerAleksandr Granin <aleksandr@mullvad.net>2021-03-12 14:57:41 +0100
commitfa8cb1e8461be82416d0f4d9c48dcbb22938fd8b (patch)
treeef1ca691b54291a7e13252874911131bafba62d9 /android
parent1db49ed3e79d82697f7e3490249ef2cce463c2be (diff)
downloadmullvadvpn-fa8cb1e8461be82416d0f4d9c48dcbb22938fd8b.tar.xz
mullvadvpn-fa8cb1e8461be82416d0f4d9c48dcbb22938fd8b.zip
Create list item view and layouts. Defie text and list item styles
Diffstat (limited to 'android')
-rw-r--r--android/build.gradle1
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ListItemListener.kt7
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ActionListItemView.kt105
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ListItemView.kt42
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/PlainListItemView.kt30
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ProgressListItemView.kt15
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/TwoActionListItemView.kt12
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/WidgetViewController.kt39
-rw-r--r--android/src/main/res/layout/list_item_action.xml28
-rw-r--r--android/src/main/res/layout/list_item_base.xml41
-rw-r--r--android/src/main/res/layout/list_item_group_divider.xml6
-rw-r--r--android/src/main/res/layout/list_item_plain_text.xml29
-rw-r--r--android/src/main/res/layout/list_item_progress.xml17
-rw-r--r--android/src/main/res/layout/list_item_two_action.xml39
-rw-r--r--android/src/main/res/layout/list_item_widget_edit_text.xml21
-rw-r--r--android/src/main/res/layout/list_item_widget_image.xml10
-rw-r--r--android/src/main/res/layout/list_item_widget_switch.xml11
-rw-r--r--android/src/main/res/values/attrs.xml4
-rw-r--r--android/src/main/res/values/dimensions.xml2
-rw-r--r--android/src/main/res/values/styles.xml53
20 files changed, 512 insertions, 0 deletions
diff --git a/android/build.gradle b/android/build.gradle
index 407a680258..f6d91922c5 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -100,6 +100,7 @@ repositories {
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.fragment:fragment-ktx:1.3.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0"
implementation 'androidx.recyclerview:recyclerview:1.1.0'
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ListItemListener.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ListItemListener.kt
new file mode 100644
index 0000000000..72cc32196c
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ListItemListener.kt
@@ -0,0 +1,7 @@
+package net.mullvad.mullvadvpn.ui
+
+import net.mullvad.mullvadvpn.model.ListItemData
+
+interface ListItemListener {
+ fun onItemAction(item: ListItemData)
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ActionListItemView.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ActionListItemView.kt
new file mode 100644
index 0000000000..865d50db24
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ActionListItemView.kt
@@ -0,0 +1,105 @@
+package net.mullvad.mullvadvpn.ui.listitemview
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.core.view.isInvisible
+import androidx.core.view.isVisible
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.model.WidgetState
+
+open class ActionListItemView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = R.attr.actionListItemViewStyle,
+ defStyleRes: Int = 0
+) : ListItemView(context, attrs, defStyleAttr, defStyleRes) {
+ override val layoutRes: Int
+ get() = R.layout.list_item_action
+
+ override val heightRes: Int
+ get() = R.dimen.cell_height
+ protected var widgetController: WidgetViewController<*>? = null
+ protected val clickListener = OnClickListener {
+ itemData.action?.let { _ ->
+ listItemListener?.onItemAction(itemData)
+ }
+ }
+ protected val itemIcon: ImageView = findViewById(R.id.itemIcon)
+ protected val itemText: TextView = findViewById(R.id.itemText)
+ protected val widgetContainer: ViewGroup = findViewById(R.id.widgetContainer)
+
+ override fun onUpdate() {
+ updateImage()
+ updateText()
+ updateWidget()
+ updateAction()
+ }
+
+ protected open fun updateImage() {
+ itemData.iconRes?.let {
+ itemIcon.isVisible = true
+ itemIcon.setImageResource(it)
+ return
+ }
+
+ itemIcon.isVisible = false
+ itemIcon.setImageDrawable(null)
+ }
+
+ protected open fun updateText() {
+ itemData.textRes?.let {
+ itemText.setText(it)
+ return
+ }
+ itemData.text?.let {
+ itemText.setText(it)
+ return
+ }
+ itemText.text = ""
+ }
+
+ private fun updateAction() {
+ if (itemData.action == null) {
+ setOnClickListener(null)
+ isClickable = false
+ isFocusable = false
+ } else {
+ setOnClickListener(clickListener)
+ isClickable = true
+ isFocusable = true
+ }
+ }
+
+ protected open fun updateWidget() {
+ itemData.widget.let { state ->
+ when (state) {
+ is WidgetState.ImageState -> {
+ if (widgetController !is WidgetViewController.StandardController) {
+ widgetContainer.removeAllViews()
+ widgetContainer.isVisible = true
+ widgetController = WidgetViewController.StandardController(widgetContainer)
+ }
+ (widgetController as WidgetViewController.StandardController).updateState(state)
+ }
+ is WidgetState.SwitchState -> {
+ if (widgetController !is WidgetViewController.SwitchController) {
+ widgetContainer.removeAllViews()
+ widgetContainer.isVisible = true
+ widgetController = WidgetViewController.SwitchController(widgetContainer)
+ }
+ (widgetController as WidgetViewController.SwitchController).updateState(state)
+ }
+ null -> {
+ if (widgetController != null) {
+ widgetController = null
+ widgetContainer.removeAllViews()
+ widgetContainer.isInvisible = true
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ListItemView.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ListItemView.kt
new file mode 100644
index 0000000000..3042e4e5af
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ListItemView.kt
@@ -0,0 +1,42 @@
+package net.mullvad.mullvadvpn.ui.listitemview
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import androidx.annotation.DimenRes
+import androidx.annotation.LayoutRes
+import androidx.constraintlayout.widget.ConstraintLayout
+import net.mullvad.mullvadvpn.model.ListItemData
+import net.mullvad.mullvadvpn.ui.ListItemListener
+
+abstract class ListItemView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) {
+ private val inflater: LayoutInflater = LayoutInflater.from(context)
+ @get:LayoutRes
+ protected abstract val layoutRes: Int
+ @get:DimenRes
+ protected abstract val heightRes: Int?
+ protected lateinit var itemData: ListItemData
+ var listItemListener: ListItemListener? = null
+
+ init {
+ val view = inflater.inflate(layoutRes, this, true)
+ val height = if (heightRes != null) {
+ resources.getDimensionPixelSize(heightRes!!)
+ } else {
+ LayoutParams.WRAP_CONTENT
+ }
+ view.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, height)
+ }
+
+ fun update(data: ListItemData) {
+ itemData = data
+ onUpdate()
+ }
+
+ protected open fun onUpdate() {}
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/PlainListItemView.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/PlainListItemView.kt
new file mode 100644
index 0000000000..f472c444df
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/PlainListItemView.kt
@@ -0,0 +1,30 @@
+package net.mullvad.mullvadvpn.ui.listitemview
+
+import android.content.Context
+import android.widget.TextView
+import androidx.appcompat.view.ContextThemeWrapper
+import net.mullvad.mullvadvpn.R
+
+class PlainListItemView(context: Context) :
+ ListItemView(ContextThemeWrapper(context, R.style.ListItem_PlainText)) {
+ override val layoutRes: Int
+ get() = R.layout.list_item_plain_text
+ override val heightRes: Int? = null
+ private val plainText: TextView = findViewById(R.id.plain_text)
+
+ override fun onUpdate() {
+ updateText()
+ }
+
+ private fun updateText() {
+ itemData.textRes?.let {
+ plainText.setText(it)
+ return
+ }
+ itemData.text?.let {
+ plainText.text = it
+ return
+ }
+ plainText.text = ""
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ProgressListItemView.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ProgressListItemView.kt
new file mode 100644
index 0000000000..724caf0c61
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ProgressListItemView.kt
@@ -0,0 +1,15 @@
+package net.mullvad.mullvadvpn.ui.listitemview
+
+import android.content.Context
+import androidx.appcompat.view.ContextThemeWrapper
+import net.mullvad.mullvadvpn.R
+
+class ProgressListItemView(context: Context) :
+ ListItemView(ContextThemeWrapper(context, R.style.ListItem_DividerGroup)) {
+
+ override val layoutRes: Int
+ get() = R.layout.list_item_progress
+
+ override val heightRes: Int
+ get() = R.dimen.progress_size
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/TwoActionListItemView.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/TwoActionListItemView.kt
new file mode 100644
index 0000000000..0f2bb3fa5b
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/TwoActionListItemView.kt
@@ -0,0 +1,12 @@
+package net.mullvad.mullvadvpn.ui.listitemview
+
+import android.content.Context
+import androidx.appcompat.view.ContextThemeWrapper
+import net.mullvad.mullvadvpn.R
+
+class TwoActionListItemView(context: Context) :
+ ActionListItemView(ContextThemeWrapper(context, R.style.ListItem_Action_Double)) {
+
+ override val layoutRes: Int
+ get() = R.layout.list_item_two_action
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/WidgetViewController.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/WidgetViewController.kt
new file mode 100644
index 0000000000..f1ec04bd1d
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/WidgetViewController.kt
@@ -0,0 +1,39 @@
+package net.mullvad.mullvadvpn.ui.listitemview
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.annotation.LayoutRes
+import androidx.appcompat.widget.SwitchCompat
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.model.WidgetState
+
+sealed class WidgetViewController<T : WidgetState>(val parent: ViewGroup) {
+ @get:LayoutRes
+ protected abstract val layoutRes: Int
+
+ init {
+ LayoutInflater.from(parent.context).inflate(layoutRes, parent)
+ }
+
+ abstract fun updateState(state: T)
+
+ class StandardController(parent: ViewGroup) :
+ WidgetViewController<WidgetState.ImageState>(parent) {
+ override val layoutRes: Int
+ get() = R.layout.list_item_widget_image
+ private val imageView: ImageView = parent.findViewById(R.id.widgetImage)
+ override fun updateState(state: WidgetState.ImageState) =
+ imageView.setImageResource(state.imageRes)
+ }
+
+ class SwitchController(parent: ViewGroup) :
+ WidgetViewController<WidgetState.SwitchState>(parent) {
+ override val layoutRes: Int
+ get() = R.layout.list_item_widget_switch
+ private val switch: SwitchCompat = parent.findViewById(R.id.widgetSwitch)
+ override fun updateState(state: WidgetState.SwitchState) {
+ switch.isChecked = state.isChecked
+ }
+ }
+}
diff --git a/android/src/main/res/layout/list_item_action.xml b/android/src/main/res/layout/list_item_action.xml
new file mode 100644
index 0000000000..d1ebee41c7
--- /dev/null
+++ b/android/src/main/res/layout/list_item_action.xml
@@ -0,0 +1,28 @@
+<?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"
+ style="@style/ListItem.Action"
+ tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
+ <include layout="@layout/list_item_base" />
+ <FrameLayout android:id="@+id/widgetContainer"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:paddingStart="@dimen/cell_right_padding"
+ android:paddingEnd="@dimen/cell_right_padding"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/itemText"
+ app:layout_constraintTop_toTopOf="parent" />
+ <androidx.constraintlayout.widget.Guideline android:id="@+id/endGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_end="@dimen/cell_right_padding" />
+ <androidx.constraintlayout.widget.Barrier android:id="@+id/widgetBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierDirection="start"
+ app:constraint_referenced_ids="widgetContainer,endGuideline" />
+</merge>
diff --git a/android/src/main/res/layout/list_item_base.xml b/android/src/main/res/layout/list_item_base.xml
new file mode 100644
index 0000000000..d4b723d70a
--- /dev/null
+++ b/android/src/main/res/layout/list_item_base.xml
@@ -0,0 +1,41 @@
+<?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="@dimen/cell_height"
+ tools:background="@color/green"
+ tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
+ <androidx.appcompat.widget.AppCompatImageView android:id="@+id/itemIcon"
+ android:layout_width="@dimen/icon_size"
+ android:layout_height="@dimen/icon_size"
+ android:layout_marginEnd="@dimen/cell_inner_spacing"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/itemText"
+ app:layout_constraintStart_toStartOf="@id/startGuideline"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:visibility="gone"
+ tools:src="@drawable/launch_logo" />
+ <androidx.appcompat.widget.AppCompatTextView android:id="@+id/itemText"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:textAppearance="@style/TextAppearance.Mullvad.Title1"
+ android:textStyle="bold"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/widgetBarrier"
+ app:layout_constraintStart_toEndOf="@id/itemIcon"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:background="@color/white20"
+ tools:text="WireGuard MTU" />
+ <androidx.constraintlayout.widget.Guideline android:id="@+id/startGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_begin="@dimen/screen_vertical_margin" />
+ <androidx.constraintlayout.widget.Barrier android:id="@+id/widgetBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierDirection="start"
+ app:constraint_referenced_ids="parent" />
+</merge>
diff --git a/android/src/main/res/layout/list_item_group_divider.xml b/android/src/main/res/layout/list_item_group_divider.xml
new file mode 100644
index 0000000000..9546d55c98
--- /dev/null
+++ b/android/src/main/res/layout/list_item_group_divider.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/vertical_space"
+ tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout" />
diff --git a/android/src/main/res/layout/list_item_plain_text.xml b/android/src/main/res/layout/list_item_plain_text.xml
new file mode 100644
index 0000000000..f17bc6ed5e
--- /dev/null
+++ b/android/src/main/res/layout/list_item_plain_text.xml
@@ -0,0 +1,29 @@
+<?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.appcompat.widget.AppCompatTextView android:id="@+id/plain_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:paddingTop="0dp"
+ android:paddingBottom="0dp"
+ android:textAppearance="@style/TextAppearance.Mullvad.Small"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/endGuideline"
+ app:layout_constraintStart_toStartOf="@id/startGuideline"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Choose the apps you want to exclude from the VPN tunnel." />
+ <androidx.constraintlayout.widget.Guideline android:id="@+id/startGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_begin="@dimen/screen_vertical_margin" />
+ <androidx.constraintlayout.widget.Guideline android:id="@+id/endGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_end="@dimen/screen_vertical_margin" />
+</merge>
diff --git a/android/src/main/res/layout/list_item_progress.xml b/android/src/main/res/layout/list_item_progress.xml
new file mode 100644
index 0000000000..221947ea85
--- /dev/null
+++ b/android/src/main/res/layout/list_item_progress.xml
@@ -0,0 +1,17 @@
+<?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"
+ tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
+ <ProgressBar android:id="@+id/loading_spinner"
+ android:layout_width="@dimen/progress_size"
+ android:layout_height="@dimen/progress_size"
+ android:indeterminate="true"
+ android:indeterminateDrawable="@drawable/icon_spinner"
+ android:indeterminateDuration="600"
+ android:indeterminateOnly="true"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+</merge>
diff --git a/android/src/main/res/layout/list_item_two_action.xml b/android/src/main/res/layout/list_item_two_action.xml
new file mode 100644
index 0000000000..4928fef27b
--- /dev/null
+++ b/android/src/main/res/layout/list_item_two_action.xml
@@ -0,0 +1,39 @@
+<?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="@dimen/cell_height"
+ tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
+ <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/container_without_widget"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="?android:attr/selectableItemBackground"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/widgetBarrier"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ <include layout="@layout/list_item_base" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ <FrameLayout android:id="@+id/widgetContainer"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="@dimen/cell_right_padding"
+ android:paddingEnd="@dimen/cell_right_padding"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:visibility="visible"
+ app:layout_constraintStart_toEndOf="@id/container_without_widget"
+ app:layout_constraintTop_toTopOf="parent" />
+ <androidx.constraintlayout.widget.Guideline android:id="@+id/endGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_end="@dimen/cell_right_padding" />
+ <androidx.constraintlayout.widget.Barrier android:id="@+id/widgetBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierDirection="start"
+ app:constraint_referenced_ids="widgetContainer,endGuideline" />
+</merge>
diff --git a/android/src/main/res/layout/list_item_widget_edit_text.xml b/android/src/main/res/layout/list_item_widget_edit_text.xml
new file mode 100644
index 0000000000..13b24c9d80
--- /dev/null
+++ b/android/src/main/res/layout/list_item_widget_edit_text.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.AppCompatEditText xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@drawable/cell_input_background"
+ android:digits="0123456789"
+ android:gravity="center"
+ android:hint="@string/hint_default"
+ android:imeOptions="flagNoPersonalizedLearning"
+ android:inputType="number"
+ android:maxWidth="130dp"
+ android:maxLength="4"
+ android:minWidth="@dimen/cell_input_width"
+ android:paddingHorizontal="8dp"
+ android:paddingVertical="4dp"
+ android:singleLine="true"
+ android:textColor="@color/white"
+ android:textColorHint="@color/white80"
+ android:textCursorDrawable="@drawable/cell_input_cursor"
+ android:textSize="@dimen/text_medium_plus" />
diff --git a/android/src/main/res/layout/list_item_widget_image.xml b/android/src/main/res/layout/list_item_widget_image.xml
new file mode 100644
index 0000000000..95034e46e3
--- /dev/null
+++ b/android/src/main/res/layout/list_item_widget_image.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.AppCompatImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/widgetImage"
+ android:layout_width="@dimen/icon_size"
+ android:layout_height="@dimen/icon_size"
+ android:layout_gravity="center"
+ android:tint="@color/white40"
+ android:tintMode="multiply"
+ tools:src="@drawable/icon_extlink" />
diff --git a/android/src/main/res/layout/list_item_widget_switch.xml b/android/src/main/res/layout/list_item_widget_switch.xml
new file mode 100644
index 0000000000..9c4e342660
--- /dev/null
+++ b/android/src/main/res/layout/list_item_widget_switch.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.SwitchCompat xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/widgetSwitch"
+ style="@style/AppTheme.Switch"
+ android:layout_gravity="center"
+ android:clickable="false"
+ android:focusable="false"
+ android:background="@null"
+ android:focusableInTouchMode="false"
+ tools:checked="false" />
diff --git a/android/src/main/res/values/attrs.xml b/android/src/main/res/values/attrs.xml
index 21db41e41c..8eefb1c173 100644
--- a/android/src/main/res/values/attrs.xml
+++ b/android/src/main/res/values/attrs.xml
@@ -55,4 +55,8 @@
<attr name="withToken"
format="boolean" />
</declare-styleable>
+ <attr name="actionListItemViewStyle"
+ type="reference" />
+ <attr name="applicationListItemViewStyle"
+ type="reference" />
</resources>
diff --git a/android/src/main/res/values/dimensions.xml b/android/src/main/res/values/dimensions.xml
index 0f0bd9ad43..ac8e3c5843 100644
--- a/android/src/main/res/values/dimensions.xml
+++ b/android/src/main/res/values/dimensions.xml
@@ -42,6 +42,8 @@
<dimen name="button_separation">18dp</dimen>
<dimen name="screen_vertical_margin">22dp</dimen>
<dimen name="app_list_item_icon_size">35dp</dimen>
+ <dimen name="progress_size">60dp</dimen>
+ <dimen name="icon_size">24dp</dimen>
<!-- Switch Dimens-->
<dimen name="switch_width">46dp</dimen>
<dimen name="switch_height">30dp</dimen>
diff --git a/android/src/main/res/values/styles.xml b/android/src/main/res/values/styles.xml
index d85ee24363..2a2734505a 100644
--- a/android/src/main/res/values/styles.xml
+++ b/android/src/main/res/values/styles.xml
@@ -6,6 +6,8 @@
<item name="android:statusBarColor">@color/blue</item>
<item name="android:windowBackground">@color/blue</item>
<item name="switchStyle">@style/AppTheme.Switch</item>
+ <item name="actionListItemViewStyle">@style/ListItem.Action</item>
+ <item name="applicationListItemViewStyle">@style/ListItem.Action.Application</item>
</style>
<style name="InputText"
parent="Widget.AppCompat.EditText">
@@ -56,6 +58,57 @@
parent="SettingsHeader">
<item name="android:textSize">@dimen/text_medium</item>
</style>
+ <style name="TextAppearance.Mullvad"
+ parent="TextAppearance.AppCompat" />
+ <style name="TextAppearance.Mullvad.Title1">
+ <item name="android:textColor">@color/white</item>
+ <item name="android:textSize">@dimen/text_medium_plus</item>
+ </style>
+ <style name="TextAppearance.Mullvad.Title2">
+ <item name="android:textColor">@color/white</item>
+ <item name="android:textSize">@dimen/text_medium</item>
+ </style>
+ <style name="TextAppearance.Mullvad.Small">
+ <item name="android:textColor">@color/white60</item>
+ <item name="android:textSize">@dimen/text_small</item>
+ </style>
+ <style name="ListItem">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
+ <style name="ListItem.DividerGroup">
+ <item name="android:layout_height">@dimen/vertical_space</item>
+ </style>
+ <style name="ListItem.PlainText">
+ <item name="android:focusable">false</item>
+ <item name="android:clickable">false</item>
+ <item name="android:paddingTop">5dp</item>
+ </style>
+ <style name="ListItem.Action">
+ <item name="android:height">@dimen/cell_height</item>
+ <item name="android:layout_height">@dimen/cell_height</item>
+ <item name="android:background">@drawable/cell_button_background</item>
+ <item name="android:clickable">true</item>
+ <item name="android:focusable">true</item>
+ </style>
+ <style name="ListItem.Action.Application">
+ <item name="android:background">@drawable/app_list_item_background</item>
+ </style>
+ <style name="ListItem.Action.Double">
+ <item name="android:clickable">false</item>
+ <item name="android:focusable">false</item>
+ </style>
+ <style name="TextAppearance.Mullvad.CollapsingToolbar">
+ <item name="android:textColor">@color/white</item>
+ </style>
+ <style name="TextAppearance.Mullvad.CollapsingToolbar.Expanded">
+ <item name="android:textSize">30sp</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+ <style name="TextAppearance.Mullvad.CollapsingToolbar.Collapsed">
+ <item name="android:textSize">20sp</item>
+ <item name="android:textStyle">bold</item>
+ </style>
<!-- Switch Style -->
<style name="AppTheme.Switch">
<item name="android:layout_width">@dimen/switch_width</item>