From 3e007584e5c19b94b4b9a7ca2668c4a201ac0d02 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Fri, 28 Jan 2022 16:40:45 -0300 Subject: [PATCH] Implement Onboarding UI --- mailbox-android/build.gradle | 7 + mailbox-android/src/main/AndroidManifest.xml | 3 +- .../mailbox/android/MailboxViewModel.kt | 4 + .../mailbox/android/MainActivity.kt | 32 ++-- .../mailbox/android/ui/OnboardingActivity.kt | 32 ++++ .../mailbox/android/ui/OnboardingFragment.kt | 143 ++++++++++++++++ mailbox-android/src/main/res/anim/enter.xml | 11 ++ mailbox-android/src/main/res/anim/exit.xml | 6 + .../src/main/res/anim/pop_enter.xml | 6 + .../src/main/res/anim/pop_exit.xml | 11 ++ .../src/main/res/drawable/ic_circle.xml | 10 ++ .../res/layout-land/fragment_onboarding.xml | 159 ++++++++++++++++++ .../src/main/res/layout/activity_main.xml | 4 + .../main/res/layout/activity_onboarding.xml | 11 ++ .../main/res/layout/fragment_onboarding.xml | 149 ++++++++++++++++ .../src/main/res/navigation/nav_main.xml | 19 +++ .../main/res/navigation/nav_onboarding.xml | 51 ++++++ .../src/main/res/values-night/themes.xml | 15 +- mailbox-android/src/main/res/values/attrs.xml | 6 + .../src/main/res/values/colors.xml | 3 + .../src/main/res/values/strings.xml | 12 ++ .../src/main/res/values/styles.xml | 8 + .../src/main/res/values/themes.xml | 13 +- 23 files changed, 688 insertions(+), 27 deletions(-) create mode 100644 mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingActivity.kt create mode 100644 mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt create mode 100644 mailbox-android/src/main/res/anim/enter.xml create mode 100644 mailbox-android/src/main/res/anim/exit.xml create mode 100644 mailbox-android/src/main/res/anim/pop_enter.xml create mode 100644 mailbox-android/src/main/res/anim/pop_exit.xml create mode 100644 mailbox-android/src/main/res/drawable/ic_circle.xml create mode 100644 mailbox-android/src/main/res/layout-land/fragment_onboarding.xml create mode 100644 mailbox-android/src/main/res/layout/activity_onboarding.xml create mode 100644 mailbox-android/src/main/res/layout/fragment_onboarding.xml create mode 100644 mailbox-android/src/main/res/navigation/nav_main.xml create mode 100644 mailbox-android/src/main/res/navigation/nav_onboarding.xml create mode 100644 mailbox-android/src/main/res/values/attrs.xml create mode 100644 mailbox-android/src/main/res/values/styles.xml diff --git a/mailbox-android/build.gradle b/mailbox-android/build.gradle index f8405470..744978cd 100644 --- a/mailbox-android/build.gradle +++ b/mailbox-android/build.gradle @@ -30,6 +30,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + buildFeatures { + viewBinding true + } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 @@ -66,6 +69,10 @@ dependencies { implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" + def nav_version = "2.4.0" + implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" + implementation "androidx.navigation:navigation-ui-ktx:$nav_version" + implementation "androidx.constraintlayout:constraintlayout:$androidx_constraintlayout_version" implementation "com.google.android.material:material:$google_material_version" implementation "com.google.dagger:hilt-android:$hilt_version" diff --git a/mailbox-android/src/main/AndroidManifest.xml b/mailbox-android/src/main/AndroidManifest.xml index ee9849f4..4466f33c 100644 --- a/mailbox-android/src/main/AndroidManifest.xml +++ b/mailbox-android/src/main/AndroidManifest.xml @@ -20,7 +20,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.Briarmailbox"> + android:theme="@style/Theme.BriarMailbox"> <service android:name=".android.MailboxService" /> @@ -33,6 +33,7 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:name=".android.ui.OnboardingActivity" /> <receiver android:name=".core.system.AlarmReceiver" /> diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxViewModel.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxViewModel.kt index 7cdf4dfa..48661e9d 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxViewModel.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxViewModel.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.flow.StateFlow import org.briarproject.android.dontkillmelib.DozeHelper import org.briarproject.mailbox.core.lifecycle.LifecycleManager import org.briarproject.mailbox.core.lifecycle.LifecycleManager.LifecycleState +import org.briarproject.mailbox.core.setup.SetupManager import org.briarproject.mailbox.core.system.DozeWatchdog import javax.inject.Inject import kotlin.concurrent.thread @@ -41,6 +42,7 @@ class MailboxViewModel @Inject constructor( private val dozeWatchdog: DozeWatchdog, handle: SavedStateHandle, private val lifecycleManager: LifecycleManager, + private val setupManager: SetupManager, ) : AndroidViewModel(app) { val needToShowDoNotKillMeFragment get() = dozeHelper.needToShowDoNotKillMeFragment(app) @@ -53,6 +55,8 @@ class MailboxViewModel @Inject constructor( val lifecycleState: StateFlow<LifecycleState> = lifecycleManager.lifecycleStateFlow + val isSetUp: Boolean get() = setupManager.hasDb + @UiThread fun onDoNotKillComplete() { _doNotKillComplete.value = true diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MainActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MainActivity.kt index 11932673..1aad5c7f 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MainActivity.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MainActivity.kt @@ -19,35 +19,45 @@ package org.briarproject.mailbox.android +import android.content.Intent import android.os.Bundle import androidx.activity.viewModels import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment +import androidx.navigation.NavController +import androidx.navigation.fragment.NavHostFragment import dagger.hilt.android.AndroidEntryPoint import org.briarproject.android.dontkillmelib.PowerUtils.needsDozeWhitelisting import org.briarproject.mailbox.R +import org.briarproject.mailbox.android.ui.OnboardingActivity @AndroidEntryPoint class MainActivity : AppCompatActivity() { private val viewModel: MailboxViewModel by viewModels() + private val nav: NavController by lazy { + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.fragmentContainer) as NavHostFragment + navHostFragment.navController + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel.doNotKillComplete.observe(this) { complete -> - if (complete) showFragment(MainFragment()) + if (complete) nav.popBackStack() } if (savedInstanceState == null) { - val f = if (viewModel.needToShowDoNotKillMeFragment) { - DoNotKillMeFragment() - } else { - MainFragment() + if (viewModel.needToShowDoNotKillMeFragment) { + nav.navigate(R.id.action_mainFragment_to_doNotKillMeFragment) + } + if (!viewModel.isSetUp) { + Intent(this, OnboardingActivity::class.java).also { i -> + startActivity(i) + } } - showFragment(f) } } @@ -58,16 +68,10 @@ class MainActivity : AppCompatActivity() { } } - private fun showFragment(f: Fragment) { - supportFragmentManager.beginTransaction() - .replace(R.id.fragmentContainer, f) - .commitNow() - } - private fun showDozeDialog() = AlertDialog.Builder(this) .setMessage(R.string.warning_dozed) .setPositiveButton(R.string.fix) { dialog, _ -> - showFragment(DoNotKillMeFragment()) + nav.navigate(R.id.action_mainFragment_to_doNotKillMeFragment) dialog.dismiss() } .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() } diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingActivity.kt new file mode 100644 index 00000000..acfbeaf7 --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingActivity.kt @@ -0,0 +1,32 @@ +/* + * Briar Mailbox + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +package org.briarproject.mailbox.android.ui + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import org.briarproject.mailbox.R + +class OnboardingActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_onboarding) + } +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt new file mode 100644 index 00000000..c332dede --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt @@ -0,0 +1,143 @@ +/* + * Briar Mailbox + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +package org.briarproject.mailbox.android.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.core.content.ContextCompat.getColorStateList +import androidx.core.widget.ImageViewCompat +import androidx.fragment.app.Fragment +import androidx.navigation.NavController +import androidx.navigation.fragment.findNavController +import org.briarproject.mailbox.R +import org.briarproject.mailbox.databinding.FragmentOnboardingBinding + +class Onboarding1Fragment : OnboardingFragment( + number = 1, + icon = R.mipmap.ic_launcher_round, + title = R.string.onboarding_1_title, + description = R.string.onboarding_1_description, + topButtonAction = { nav -> + nav.navigate(R.id.action_onboarding_1_to_2) + }, + bottomButtonText = R.string.button_skip_intro, + bottomButtonAction = { + requireActivity().supportFinishAfterTransition() + }, +) + +class Onboarding2Fragment : OnboardingFragment( + number = 2, + icon = R.mipmap.ic_launcher_round, + title = R.string.onboarding_2_title, + description = R.string.onboarding_2_description, + topButtonAction = { nav -> + nav.navigate(R.id.action_onboarding_2_to_3) + }, + bottomButtonAction = { nav -> + nav.popBackStack() + }, +) + +class Onboarding3Fragment : OnboardingFragment( + number = 3, + icon = R.mipmap.ic_launcher_round, + title = R.string.onboarding_3_title, + description = R.string.onboarding_3_description, + topButtonAction = { nav -> + nav.navigate(R.id.action_onboarding_3_to_4) + }, + bottomButtonAction = { nav -> + nav.popBackStack() + }, +) + +class Onboarding4Fragment : OnboardingFragment( + number = 4, + icon = R.mipmap.ic_launcher_round, + title = R.string.onboarding_4_title, + description = R.string.onboarding_4_description, + topButtonAction = { + requireActivity().supportFinishAfterTransition() + }, + bottomButtonAction = { nav -> + nav.popBackStack() + }, +) + +abstract class OnboardingFragment( + private val number: Int, + @DrawableRes + private val icon: Int, + @StringRes + private val title: Int, + @StringRes + private val description: Int, + @StringRes + private val bottomButtonText: Int = R.string.button_back, + private val topButtonAction: Fragment.(NavController) -> Unit, + private val bottomButtonAction: Fragment.(NavController) -> Unit, +) : Fragment() { + + private var _ui: FragmentOnboardingBinding? = null + + /** + * This property is only valid between [onCreateView] and [onDestroyView]. + */ + private val ui get() = _ui!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + _ui = FragmentOnboardingBinding.inflate(inflater, container, false) + return ui.root + } + + override fun onViewCreated(v: View, savedInstanceState: Bundle?) { + ui.icon.setImageResource(icon) + ui.title.setText(title) + ui.description.setText(description) + listOf(ui.bullet1, ui.bullet2, ui.bullet3, ui.bullet4).forEachIndexed { i, imageView -> + val color = if (i + 1 <= number) R.color.briar_green else R.color.briar_night + val tintList = getColorStateList(requireContext(), color) + ImageViewCompat.setImageTintList(imageView, tintList) + } + val nav = findNavController() + ui.topButton.setOnClickListener { + topButtonAction(nav) + } + ui.bottomButton.setText(bottomButtonText) + ui.bottomButton.setOnClickListener { + bottomButtonAction(nav) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _ui = null + } + +} diff --git a/mailbox-android/src/main/res/anim/enter.xml b/mailbox-android/src/main/res/anim/enter.xml new file mode 100644 index 00000000..32b84f2d --- /dev/null +++ b/mailbox-android/src/main/res/anim/enter.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- slide in from right --> + <translate + android:duration="@integer/animationSpeed" + android:fromXDelta="100%p" + android:interpolator="@android:interpolator/decelerate_quad" + android:toXDelta="0" /> + +</set> diff --git a/mailbox-android/src/main/res/anim/exit.xml b/mailbox-android/src/main/res/anim/exit.xml new file mode 100644 index 00000000..205435cc --- /dev/null +++ b/mailbox-android/src/main/res/anim/exit.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<alpha xmlns:android="http://schemas.android.com/apk/res/android" + android:duration="@integer/animationSpeed" + android:fromAlpha="1.0" + android:interpolator="@android:interpolator/accelerate_quad" + android:toAlpha="0.0" /> diff --git a/mailbox-android/src/main/res/anim/pop_enter.xml b/mailbox-android/src/main/res/anim/pop_enter.xml new file mode 100644 index 00000000..4b3a8d60 --- /dev/null +++ b/mailbox-android/src/main/res/anim/pop_enter.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<alpha xmlns:android="http://schemas.android.com/apk/res/android" + android:duration="@integer/animationSpeed" + android:fromAlpha="0.0" + android:interpolator="@android:interpolator/decelerate_quad" + android:toAlpha="1.0" /> diff --git a/mailbox-android/src/main/res/anim/pop_exit.xml b/mailbox-android/src/main/res/anim/pop_exit.xml new file mode 100644 index 00000000..2c514d82 --- /dev/null +++ b/mailbox-android/src/main/res/anim/pop_exit.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- slide out to right --> + <translate + android:duration="@integer/animationSpeed" + android:fromXDelta="0" + android:interpolator="@android:interpolator/accelerate_quad" + android:toXDelta="100%p" /> + +</set> diff --git a/mailbox-android/src/main/res/drawable/ic_circle.xml b/mailbox-android/src/main/res/drawable/ic_circle.xml new file mode 100644 index 00000000..bf54d3e4 --- /dev/null +++ b/mailbox-android/src/main/res/drawable/ic_circle.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2z" /> +</vector> diff --git a/mailbox-android/src/main/res/layout-land/fragment_onboarding.xml b/mailbox-android/src/main/res/layout-land/fragment_onboarding.xml new file mode 100644 index 00000000..e5492eec --- /dev/null +++ b/mailbox-android/src/main/res/layout-land/fragment_onboarding.xml @@ -0,0 +1,159 @@ +<?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="match_parent"> + + <ImageView + android:id="@+id/icon" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_margin="32dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintDimensionRatio="1:1" + app:layout_constraintEnd_toStartOf="@+id/guideline" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@mipmap/ic_launcher" + tools:ignore="ContentDescription" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_percent="0.5" /> + + <TextView + android:id="@+id/title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="16dp" + android:gravity="center" + android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium" + app:layout_constraintBottom_toTopOf="@+id/description" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@+id/guideline" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" + tools:text="@string/onboarding_1_title" /> + + <TextView + android:id="@+id/description" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="16dp" + android:textAppearance="@style/TextAppearance.Material3.BodyLarge" + app:layout_constraintBottom_toTopOf="@+id/bullet1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@+id/guideline" + app:layout_constraintTop_toBottomOf="@+id/title" + tools:text="@string/onboarding_1_description" /> + + <ImageView + android:id="@+id/bullet1" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@+id/topButton" + app:layout_constraintEnd_toStartOf="@id/bullet2" + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintStart_toStartOf="@+id/guideline" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintVertical_chainStyle="packed" + app:srcCompat="@drawable/ic_circle" + app:tint="?attr/colorSecondary" /> + + <ImageView + android:id="@+id/bullet2" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginStart="4dp" + android:layout_marginLeft="4dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@+id/topButton" + app:layout_constraintEnd_toStartOf="@id/bullet3" + app:layout_constraintStart_toEndOf="@+id/bullet1" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintVertical_chainStyle="packed" + app:srcCompat="@drawable/ic_circle" + app:tint="?attr/colorPrimary" /> + + <ImageView + android:id="@+id/bullet3" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginStart="4dp" + android:layout_marginLeft="4dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@+id/topButton" + app:layout_constraintEnd_toStartOf="@id/bullet4" + app:layout_constraintStart_toEndOf="@+id/bullet2" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintVertical_chainStyle="packed" + app:srcCompat="@drawable/ic_circle" + app:tint="?attr/colorPrimary" /> + + <ImageView + android:id="@+id/bullet4" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginStart="4dp" + android:layout_marginLeft="4dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@+id/topButton" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/bullet3" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintVertical_chainStyle="packed" + app:srcCompat="@drawable/ic_circle" + app:tint="?attr/colorPrimary" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/topButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginLeft="16dp" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:text="@string/button_continue" + app:layout_constraintBottom_toTopOf="@+id/bottomButton" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@+id/guideline" + app:layout_constraintTop_toBottomOf="@+id/bullet1" + app:layout_constraintVertical_bias="1.0" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/bottomButton" + style="@style/TextButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginLeft="16dp" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:text="@string/button_skip_intro" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@+id/guideline" + app:layout_constraintTop_toBottomOf="@+id/topButton" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/mailbox-android/src/main/res/layout/activity_main.xml b/mailbox-android/src/main/res/layout/activity_main.xml index 0e985efe..d8d6ae61 100644 --- a/mailbox-android/src/main/res/layout/activity_main.xml +++ b/mailbox-android/src/main/res/layout/activity_main.xml @@ -1,7 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.fragment.app.FragmentContainerView 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:id="@+id/fragmentContainer" + android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" + app:defaultNavHost="true" + app:navGraph="@navigation/nav_main" tools:context=".android.MainActivity" /> diff --git a/mailbox-android/src/main/res/layout/activity_onboarding.xml b/mailbox-android/src/main/res/layout/activity_onboarding.xml new file mode 100644 index 00000000..24bf07ef --- /dev/null +++ b/mailbox-android/src/main/res/layout/activity_onboarding.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.fragment.app.FragmentContainerView 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:id="@+id/fragmentContainer" + android:name="androidx.navigation.fragment.NavHostFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:defaultNavHost="true" + app:navGraph="@navigation/nav_onboarding" + tools:context=".android.ui.OnboardingActivity" /> diff --git a/mailbox-android/src/main/res/layout/fragment_onboarding.xml b/mailbox-android/src/main/res/layout/fragment_onboarding.xml new file mode 100644 index 00000000..2c07cce9 --- /dev/null +++ b/mailbox-android/src/main/res/layout/fragment_onboarding.xml @@ -0,0 +1,149 @@ +<?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="match_parent"> + + <ImageView + android:id="@+id/icon" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_margin="32dp" + app:layout_constraintBottom_toTopOf="@+id/title" + app:layout_constraintDimensionRatio="1:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@mipmap/ic_launcher" + tools:ignore="ContentDescription" /> + + <TextView + android:id="@+id/title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="16dp" + android:gravity="center" + android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium" + app:layout_constraintBottom_toTopOf="@+id/description" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/icon" + app:layout_constraintVertical_chainStyle="packed" + tools:text="@string/onboarding_1_title" /> + + <TextView + android:id="@+id/description" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="16dp" + android:textAppearance="@style/TextAppearance.Material3.BodyLarge" + app:layout_constraintBottom_toTopOf="@+id/bullet1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/title" + tools:text="@string/onboarding_1_description" /> + + <ImageView + android:id="@+id/bullet1" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginStart="16dp" + android:layout_marginLeft="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@+id/topButton" + app:layout_constraintEnd_toStartOf="@id/bullet2" + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintVertical_chainStyle="packed" + app:srcCompat="@drawable/ic_circle" + app:tint="?attr/colorSecondary" /> + + <ImageView + android:id="@+id/bullet2" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginStart="4dp" + android:layout_marginLeft="4dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@+id/topButton" + app:layout_constraintEnd_toStartOf="@id/bullet3" + app:layout_constraintStart_toEndOf="@+id/bullet1" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintVertical_chainStyle="packed" + app:srcCompat="@drawable/ic_circle" + app:tint="?attr/colorPrimary" /> + + <ImageView + android:id="@+id/bullet3" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginStart="4dp" + android:layout_marginLeft="4dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@+id/topButton" + app:layout_constraintEnd_toStartOf="@id/bullet4" + app:layout_constraintStart_toEndOf="@+id/bullet2" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintVertical_chainStyle="packed" + app:srcCompat="@drawable/ic_circle" + app:tint="?attr/colorPrimary" /> + + <ImageView + android:id="@+id/bullet4" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginStart="4dp" + android:layout_marginLeft="4dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@+id/topButton" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/bullet3" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintVertical_chainStyle="packed" + app:srcCompat="@drawable/ic_circle" + app:tint="?attr/colorPrimary" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/topButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:text="@string/button_continue" + app:layout_constraintBottom_toTopOf="@+id/bottomButton" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/bullet1" + app:layout_constraintVertical_bias="1.0" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/bottomButton" + style="@style/TextButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button_skip_intro" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/topButton" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/mailbox-android/src/main/res/navigation/nav_main.xml b/mailbox-android/src/main/res/navigation/nav_main.xml new file mode 100644 index 00000000..2a915152 --- /dev/null +++ b/mailbox-android/src/main/res/navigation/nav_main.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/nav_onboarding" + app:startDestination="@id/mainFragment"> + + <fragment + android:id="@+id/mainFragment" + android:name="org.briarproject.mailbox.android.MainFragment" + android:label="MainFragment"> + <action + android:id="@+id/action_mainFragment_to_doNotKillMeFragment" + app:destination="@id/doNotKillMeFragment" /> + </fragment> + <fragment + android:id="@+id/doNotKillMeFragment" + android:name="org.briarproject.mailbox.android.DoNotKillMeFragment" + android:label="DoNotKillMeFragment" /> +</navigation> diff --git a/mailbox-android/src/main/res/navigation/nav_onboarding.xml b/mailbox-android/src/main/res/navigation/nav_onboarding.xml new file mode 100644 index 00000000..74a5c755 --- /dev/null +++ b/mailbox-android/src/main/res/navigation/nav_onboarding.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation 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:id="@+id/nav_onboarding" + app:startDestination="@id/onboarding1Fragment"> + <fragment + android:id="@+id/onboarding1Fragment" + android:name="org.briarproject.mailbox.android.ui.Onboarding1Fragment" + android:label="Onboarding1Fragment" + tools:layout="@layout/fragment_onboarding"> + <action + android:id="@+id/action_onboarding_1_to_2" + app:destination="@id/onboarding2Fragment" + app:enterAnim="@anim/enter" + app:exitAnim="@anim/exit" + app:popEnterAnim="@anim/pop_enter" + app:popExitAnim="@anim/pop_exit" /> + </fragment> + <fragment + android:id="@+id/onboarding2Fragment" + android:name="org.briarproject.mailbox.android.ui.Onboarding2Fragment" + android:label="Onboarding2Fragment" + tools:layout="@layout/fragment_onboarding"> + <action + android:id="@+id/action_onboarding_2_to_3" + app:destination="@id/onboarding3Fragment" + app:enterAnim="@anim/enter" + app:exitAnim="@anim/exit" + app:popEnterAnim="@anim/pop_enter" + app:popExitAnim="@anim/pop_exit" /> + </fragment> + <fragment + android:id="@+id/onboarding3Fragment" + android:name="org.briarproject.mailbox.android.ui.Onboarding3Fragment" + android:label="Onboarding3Fragment" + tools:layout="@layout/fragment_onboarding"> + <action + android:id="@+id/action_onboarding_3_to_4" + app:destination="@id/onboarding4Fragment" + app:enterAnim="@anim/enter" + app:exitAnim="@anim/exit" + app:popEnterAnim="@anim/pop_enter" + app:popExitAnim="@anim/pop_exit" /> + </fragment> + <fragment + android:id="@+id/onboarding4Fragment" + android:name="org.briarproject.mailbox.android.ui.Onboarding4Fragment" + android:label="Onboarding4Fragment" + tools:layout="@layout/fragment_onboarding" /> +</navigation> diff --git a/mailbox-android/src/main/res/values-night/themes.xml b/mailbox-android/src/main/res/values-night/themes.xml index 8b6f2d27..0a74b49e 100644 --- a/mailbox-android/src/main/res/values-night/themes.xml +++ b/mailbox-android/src/main/res/values-night/themes.xml @@ -1,10 +1,13 @@ -<resources> +<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> - <style name="Theme.Briarmailbox" parent="Theme.AppCompat.Light.DarkActionBar"> + <style name="Theme.BriarMailbox" parent="Theme.Material3.DayNight.NoActionBar"> <!-- Primary brand color. --> - <item name="colorPrimary">@color/purple_200</item> - <item name="colorPrimaryDark">@color/purple_700</item> - <item name="colorAccent">@color/teal_200</item> + <item name="colorPrimary">@color/briar_night</item> + <item name="colorPrimaryDark">#1F78D1</item> + <item name="colorSecondary">@color/briar_green</item> <!-- Customize your theme here. --> + <item name="android:background">@color/black</item> + <item name="android:statusBarColor" tools:targetApi="l">?attr/background</item> + <item name="colorOnPrimary">@color/white</item> </style> -</resources> \ No newline at end of file +</resources> diff --git a/mailbox-android/src/main/res/values/attrs.xml b/mailbox-android/src/main/res/values/attrs.xml new file mode 100644 index 00000000..3704cec4 --- /dev/null +++ b/mailbox-android/src/main/res/values/attrs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <integer name="animationSpeed">@android:integer/config_mediumAnimTime</integer> + +</resources> diff --git a/mailbox-android/src/main/res/values/colors.xml b/mailbox-android/src/main/res/values/colors.xml index f8c6127d..ff2144f4 100644 --- a/mailbox-android/src/main/res/values/colors.xml +++ b/mailbox-android/src/main/res/values/colors.xml @@ -7,4 +7,7 @@ <color name="teal_700">#FF018786</color> <color name="black">#FF000000</color> <color name="white">#FFFFFFFF</color> + + <color name="briar_green">#74B816</color> + <color name="briar_night">#435B77</color> </resources> \ No newline at end of file diff --git a/mailbox-android/src/main/res/values/strings.xml b/mailbox-android/src/main/res/values/strings.xml index 3622cbb1..f895cbf1 100644 --- a/mailbox-android/src/main/res/values/strings.xml +++ b/mailbox-android/src/main/res/values/strings.xml @@ -6,6 +6,18 @@ <string name="start">Start mailbox</string> <string name="stop">Stop mailbox</string> <string name="wipe">Wipe mailbox</string> + <string name="button_continue">Continue</string> + <string name="button_skip_intro">Skip Intro</string> + <string name="button_back">Back</string> + + <string name="onboarding_1_title">Stay Reachable</string> + <string name="onboarding_1_description">The Mailbox helps you to stay in touch with your contacts and synchronizes your messages to Briar.</string> + <string name="onboarding_2_title">Use a spare device</string> + <string name="onboarding_2_description">Install the mailbox app on a spare phone or tablet and leave it connected to power and WiFi. </string> + <string name="onboarding_3_title">Message delivery</string> + <string name="onboarding_3_description">When your Mailbox is online, your contacts can leave messages for you even if your Briar is offline. The Mailbox will receive the messages, and forward them to Briar when you come online.</string> + <string name="onboarding_4_title">Encrypted</string> + <string name="onboarding_4_description">Messages going through the Mailbox are encrypted and cannot be read by the Mailbox or this phone. The Mailbox simply forwards them to your Briar Messenger phone.</string> <!-- TODO: We might want to copy string from don't kill me lib, so translation memory can auto-translate most of them. --> diff --git a/mailbox-android/src/main/res/values/styles.xml b/mailbox-android/src/main/res/values/styles.xml new file mode 100644 index 00000000..e547d159 --- /dev/null +++ b/mailbox-android/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <style name="TextButton" parent="Widget.Material3.Button.TextButton"> + <item name="android:textColor">?attr/colorSecondary</item> + </style> + +</resources> \ No newline at end of file diff --git a/mailbox-android/src/main/res/values/themes.xml b/mailbox-android/src/main/res/values/themes.xml index 9d0dfedb..ce3da263 100644 --- a/mailbox-android/src/main/res/values/themes.xml +++ b/mailbox-android/src/main/res/values/themes.xml @@ -1,10 +1,11 @@ -<resources> +<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> - <style name="Theme.Briarmailbox" parent="Theme.AppCompat.Light.DarkActionBar"> + <style name="Theme.BriarMailbox" parent="Theme.Material3.DayNight.NoActionBar"> <!-- Primary brand color. --> - <item name="colorPrimary">@color/purple_500</item> - <item name="colorPrimaryDark">@color/purple_700</item> - <item name="colorAccent">@color/teal_200</item> + <item name="colorPrimary">@color/briar_night</item> + <item name="colorPrimaryDark">#1F78D1</item> + <item name="colorSecondary">@color/briar_green</item> <!-- Customize your theme here. --> + <item name="android:statusBarColor" tools:targetApi="l">@color/black</item> </style> -</resources> \ No newline at end of file +</resources> -- GitLab