diff --git a/app/src/main/java/com/example/resbuilder/ui/MainActivity.kt b/app/src/main/java/com/example/resbuilder/ui/MainActivity.kt
index 3883f63..a145023 100644
--- a/app/src/main/java/com/example/resbuilder/ui/MainActivity.kt
+++ b/app/src/main/java/com/example/resbuilder/ui/MainActivity.kt
@@ -5,7 +5,6 @@ import android.graphics.PorterDuff
import android.os.Bundle
import android.os.Handler
import android.os.Looper
-import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ImageView
@@ -13,18 +12,21 @@ import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
+import androidx.drawerlayout.widget.DrawerLayout
import com.example.resbuilder.R
import com.example.resbuilder.data.model.User
import com.example.resbuilder.data.remote.ApiClient
import com.google.android.material.appbar.MaterialToolbar
-import com.google.android.material.tabs.TabLayout
+import com.google.android.material.navigation.NavigationView
class MainActivity : AppCompatActivity(), JobFormFragment.JobFormListener, ResultFragment.OnNewListener {
+ private lateinit var drawerLayout: DrawerLayout
private lateinit var toolbar: MaterialToolbar
- private lateinit var tabLayout: TabLayout
+ private lateinit var navView: NavigationView
private lateinit var healthIndicator: ImageView
private lateinit var userName: TextView
+ private lateinit var userEmail: TextView
private lateinit var usageBanner: TextView
private val handler = Handler(Looper.getMainLooper())
@@ -32,43 +34,65 @@ class MainActivity : AppCompatActivity(), JobFormFragment.JobFormListener, Resul
private var healthRunnable: Runnable? = null
companion object {
- private const val TAB_RESUME = 0
- private const val TAB_COVER = 1
+ private const val NAV_BUILD_RESUME = 0
+ private const val NAV_BUILD_COVER_LETTER = 1
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
+ drawerLayout = findViewById(R.id.drawerLayout)
toolbar = findViewById(R.id.toolbar)
- tabLayout = findViewById(R.id.tabLayout)
- healthIndicator = findViewById(R.id.healthIndicator)
- userName = findViewById(R.id.userName)
+ navView = findViewById(R.id.navView)
usageBanner = findViewById(R.id.usageBanner)
+ val header = navView.getHeaderView(0)
+ healthIndicator = header.findViewById(R.id.healthIndicator)
+ userName = header.findViewById(R.id.userName)
+ userEmail = header.findViewById(R.id.userEmail)
+
setSupportActionBar(toolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_menu_hamburger)
- tabLayout.addTab(tabLayout.newTab().setText(R.string.tab_build_resume))
- tabLayout.addTab(tabLayout.newTab().setText(R.string.tab_build_cover_letter))
-
- tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
- override fun onTabSelected(tab: TabLayout.Tab?) {
- switchFormTab(tab?.position ?: TAB_RESUME)
- }
-
- override fun onTabUnselected(tab: TabLayout.Tab?) {}
- override fun onTabReselected(tab: TabLayout.Tab?) {
- switchFormTab(tab?.position ?: TAB_RESUME)
- }
- })
+ navView.setNavigationItemSelectedListener { item ->
+ drawerLayout.closeDrawers()
+ handleNavItem(item.itemId)
+ true
+ }
loadUser()
startHealthPolling()
if (savedInstanceState == null) {
- switchFormTab(TAB_RESUME)
+ showBuildForm(NAV_BUILD_RESUME)
}
}
+ private fun handleNavItem(itemId: Int) {
+ when (itemId) {
+ R.id.navBuildResume -> showBuildForm(NAV_BUILD_RESUME)
+ R.id.navBuildCoverLetter -> showBuildForm(NAV_BUILD_COVER_LETTER)
+ R.id.navMyResumes -> showMyResumes()
+ R.id.navAdmin -> startActivity(Intent(this, AdminActivity::class.java))
+ R.id.navSignOut -> signOut()
+ }
+ }
+
+ private fun showBuildForm(position: Int) {
+ val type = if (position == NAV_BUILD_COVER_LETTER) JobFormFragment.TYPE_COVER_LETTER else JobFormFragment.TYPE_RESUME
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.fragmentContainer, JobFormFragment.newInstance(type))
+ .commitAllowingStateLoss()
+ }
+
+ private fun showMyResumes() {
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.fragmentContainer, MyResumesFragment())
+ .addToBackStack(null)
+ .commitAllowingStateLoss()
+ }
+
private fun loadUser() {
Thread {
try {
@@ -86,20 +110,13 @@ class MainActivity : AppCompatActivity(), JobFormFragment.JobFormListener, Resul
private fun updateUserUI() {
userName.text = user.name ?: user.email ?: getString(R.string.unknown_user)
- if (!user.authenticated || (!user.is_admin && !user.can_submit)) {
- usageBanner.visibility = View.VISIBLE
+ userEmail.text = user.email ?: getString(R.string.unknown_email)
+ usageBanner.visibility = if (!user.authenticated || (!user.is_admin && !user.can_submit)) {
+ View.VISIBLE
} else {
- usageBanner.visibility = View.GONE
+ View.GONE
}
- invalidateOptionsMenu()
- }
-
- private fun switchFormTab(position: Int) {
- val type = if (position == TAB_COVER) JobFormFragment.TYPE_COVER_LETTER else JobFormFragment.TYPE_RESUME
- val fragment = JobFormFragment.newInstance(type)
- supportFragmentManager.beginTransaction()
- .replace(R.id.fragmentContainer, fragment)
- .commitAllowingStateLoss()
+ navView.menu.findItem(R.id.navAdmin)?.isVisible = user.is_admin
}
private fun startHealthPolling() {
@@ -130,34 +147,12 @@ class MainActivity : AppCompatActivity(), JobFormFragment.JobFormListener, Resul
}.start()
}
- override fun onCreateOptionsMenu(menu: Menu?): Boolean {
- menuInflater.inflate(R.menu.menu_main, menu)
- return true
- }
-
- override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
- menu?.findItem(R.id.menuAdminPanel)?.isVisible = user.is_admin
- return super.onPrepareOptionsMenu(menu)
- }
-
override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.menuMyResumes -> {
- supportFragmentManager.beginTransaction()
- .replace(R.id.fragmentContainer, MyResumesFragment())
- .addToBackStack(null)
- .commitAllowingStateLoss()
- true
- }
- R.id.menuSignOut -> {
- signOut()
- true
- }
- R.id.menuAdminPanel -> {
- startActivity(Intent(this, AdminActivity::class.java))
- true
- }
- else -> super.onOptionsItemSelected(item)
+ return if (item.itemId == android.R.id.home) {
+ drawerLayout.openDrawer(navView)
+ true
+ } else {
+ super.onOptionsItemSelected(item)
}
}
@@ -183,8 +178,8 @@ class MainActivity : AppCompatActivity(), JobFormFragment.JobFormListener, Resul
fun onResultNewJob() {
supportFragmentManager.popBackStackImmediate()
- tabLayout.getTabAt(TAB_RESUME)?.select()
- switchFormTab(TAB_RESUME)
+ navView.menu.getItem(NAV_BUILD_RESUME)?.isChecked = true
+ showBuildForm(NAV_BUILD_RESUME)
}
override fun onNewJob() {
diff --git a/app/src/main/res/drawable/bg_gradient_primary.xml b/app/src/main/res/drawable/bg_gradient_primary.xml
index f405319..ed6da10 100644
--- a/app/src/main/res/drawable/bg_gradient_primary.xml
+++ b/app/src/main/res/drawable/bg_gradient_primary.xml
@@ -1,8 +1,9 @@
diff --git a/app/src/main/res/drawable/bg_gradient_primary_bottom.xml b/app/src/main/res/drawable/bg_gradient_primary_bottom.xml
new file mode 100644
index 0000000..db08bea
--- /dev/null
+++ b/app/src/main/res/drawable/bg_gradient_primary_bottom.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_document.xml b/app/src/main/res/drawable/ic_document.xml
new file mode 100644
index 0000000..5039c59
--- /dev/null
+++ b/app/src/main/res/drawable/ic_document.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_hamburger.xml b/app/src/main/res/drawable/ic_menu_hamburger.xml
new file mode 100644
index 0000000..bbe3fff
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_hamburger.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_sparkle.xml b/app/src/main/res/drawable/ic_sparkle.xml
new file mode 100644
index 0000000..f1565a8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_sparkle.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 7308c05..74d3ffb 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -7,7 +7,7 @@
android:background="?attr/colorSurface"
tools:context=".ui.LoginActivity">
-
+
+
+
+ app:layout_constraintGuide_percent="0.40" />
+ android:padding="@dimen/space_xl">
-
+
+ app:cardCornerRadius="@dimen/card_radius_large"
+ app:cardElevation="0dp"
+ app:strokeWidth="0dp">
@@ -71,7 +83,7 @@
android:id="@+id/loginTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
+ android:layout_marginTop="@dimen/space_lg"
android:text="@string/app_title"
android:textAlignment="center"
android:textAppearance="?attr/textAppearanceHeadlineMedium"
@@ -83,7 +95,7 @@
android:id="@+id/loginSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
+ android:layout_marginTop="@dimen/space_sm"
android:text="@string/welcome_subtitle"
android:textAlignment="center"
android:textAppearance="?attr/textAppearanceBodyLarge"
@@ -93,16 +105,17 @@
-
+
-
-
+
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
+
+ app:elevation="0dp">
-
-
-
-
-
-
-
+
+
-
-
+ android:layout_height="match_parent"
+ android:background="?attr/colorSurface" />
+
-
+
+ android:layout_gravity="start"
+ android:background="?attr/colorSurfaceContainerLowest"
+ app:headerLayout="@layout/nav_header"
+ app:itemHorizontalPadding="@dimen/space_md"
+ app:itemIconPadding="@dimen/space_md"
+ app:itemIconTint="?attr/colorOnSurfaceVariant"
+ app:itemRippleColor="?attr/colorPrimaryContainer"
+ app:itemTextAppearance="?attr/textAppearanceBodyLarge"
+ app:itemTextColor="?attr/colorOnSurface"
+ app:itemVerticalPadding="@dimen/space_md"
+ app:menu="@menu/menu_nav_drawer" />
-
+
diff --git a/app/src/main/res/layout/fragment_job_form.xml b/app/src/main/res/layout/fragment_job_form.xml
index e05c1e5..99cefd3 100644
--- a/app/src/main/res/layout/fragment_job_form.xml
+++ b/app/src/main/res/layout/fragment_job_form.xml
@@ -6,182 +6,244 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:fillViewport="true"
+ android:clipToPadding="false"
+ android:paddingBottom="@dimen/space_md"
tools:context=".ui.JobFormFragment">
+ android:orientation="vertical">
-
+
+ android:background="@drawable/bg_gradient_primary"
+ android:minHeight="@dimen/hero_header_min_height"
+ android:padding="@dimen/space_lg">
+
+
+ android:layout_gravity="bottom|start"
+ android:orientation="vertical">
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:textAppearance="?attr/textAppearanceHeadlineMedium"
+ android:textColor="?attr/colorOnPrimary" />
-
+
-
+ android:orientation="vertical"
+ android:padding="@dimen/space_md">
-
+
+ app:cardBackgroundColor="?attr/colorSurfaceContainerLow"
+ app:cardCornerRadius="@dimen/card_radius"
+ app:cardElevation="0dp"
+ app:strokeColor="?attr/colorOutlineVariant"
+ app:strokeWidth="1dp">
+ android:orientation="vertical"
+ android:padding="@dimen/space_md">
-
-
-
-
-
-
-
-
-
+ android:layout_marginTop="@dimen/space_sm"
+ android:hint="@string/hint_job_title"
+ style="@style/Widget.Material3.TextInputLayout.FilledBox"
+ app:boxBackgroundColor="?attr/colorSurfaceContainerHighest"
+ app:boxStrokeColor="?attr/colorPrimary"
+ app:cursorColor="?attr/colorPrimary"
+ app:hintTextColor="?attr/colorOnSurfaceVariant">
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_job_status.xml b/app/src/main/res/layout/fragment_job_status.xml
index 8aa4818..0196d29 100644
--- a/app/src/main/res/layout/fragment_job_status.xml
+++ b/app/src/main/res/layout/fragment_job_status.xml
@@ -7,14 +7,15 @@
android:background="?attr/colorSurface"
android:gravity="center"
android:orientation="vertical"
- android:padding="16dp">
+ android:padding="@dimen/space_md">
@@ -24,27 +25,36 @@
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
- android:padding="24dp">
+ android:padding="@dimen/space_lg">
-
+
+ app:cardBackgroundColor="?attr/colorPrimaryContainer"
+ app:cardCornerRadius="16dp"
+ app:cardElevation="0dp"
+ app:strokeWidth="0dp">
+
+
+
diff --git a/app/src/main/res/layout/fragment_my_resumes.xml b/app/src/main/res/layout/fragment_my_resumes.xml
index 56b7c7d..b0ae36a 100644
--- a/app/src/main/res/layout/fragment_my_resumes.xml
+++ b/app/src/main/res/layout/fragment_my_resumes.xml
@@ -5,24 +5,55 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
- android:orientation="vertical"
- android:padding="16dp">
+ android:orientation="vertical">
-
+
+ android:background="@drawable/bg_gradient_primary"
+ android:minHeight="@dimen/hero_header_min_height"
+ android:padding="@dimen/space_lg">
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml
index 6c3463b..e516be7 100644
--- a/app/src/main/res/layout/fragment_result.xml
+++ b/app/src/main/res/layout/fragment_result.xml
@@ -8,13 +8,15 @@
android:orientation="vertical"
tools:context=".ui.ResultFragment">
+
@@ -23,23 +25,31 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
- android:minHeight="48dp"
+ android:minHeight="@dimen/touch_target_min"
android:orientation="horizontal"
- android:padding="12dp">
+ android:padding="@dimen/space_sm">
-
+ app:cardBackgroundColor="?attr/colorSuccessContainer"
+ app:cardCornerRadius="16dp"
+ app:cardElevation="0dp"
+ app:strokeWidth="0dp">
+
+
+
@@ -59,8 +69,8 @@
android:id="@+id/newButton"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginStart="4dp"
+ android:layout_height="@dimen/button_height"
+ android:layout_marginStart="@dimen/space_xs"
android:contentDescription="@string/cd_new_job"
android:minWidth="64dp"
android:text="@string/new_job" />
@@ -71,17 +81,19 @@
android:id="@+id/resultWebView"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginHorizontal="16dp"
+ android:layout_marginHorizontal="@dimen/space_md"
android:layout_weight="1"
android:contentDescription="@string/result_preview" />
+
@@ -90,14 +102,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:padding="12dp">
+ android:padding="@dimen/space_sm">
@@ -106,8 +118,8 @@
android:id="@+id/docxButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
- android:layout_height="48dp"
- android:layout_marginStart="8dp"
+ android:layout_height="@dimen/button_height"
+ android:layout_marginStart="@dimen/space_sm"
android:layout_weight="1"
android:contentDescription="@string/cd_export_docx"
android:text="@string/export_docx" />
diff --git a/app/src/main/res/layout/item_job_summary.xml b/app/src/main/res/layout/item_job_summary.xml
index 5d15a3a..c506f06 100644
--- a/app/src/main/res/layout/item_job_summary.xml
+++ b/app/src/main/res/layout/item_job_summary.xml
@@ -4,36 +4,73 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginVertical="6dp"
- app:cardCornerRadius="12dp"
+ android:layout_marginVertical="@dimen/space_sm"
+ app:cardBackgroundColor="?attr/colorSurfaceContainerLow"
+ app:cardCornerRadius="@dimen/card_radius"
app:cardElevation="0dp"
+ app:rippleColor="?attr/colorPrimaryContainer"
app:strokeColor="?attr/colorOutlineVariant"
app:strokeWidth="1dp">
+ android:gravity="center_vertical"
+ android:minHeight="@dimen/touch_target_min"
+ android:orientation="horizontal"
+ android:padding="@dimen/space_md">
-
+
-
+
+
+
+ android:layout_marginStart="@dimen/space_md"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml
new file mode 100644
index 0000000..b592fb2
--- /dev/null
+++ b/app/src/main/res/layout/nav_header.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
deleted file mode 100644
index 64943dc..0000000
--- a/app/src/main/res/menu/menu_main.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
diff --git a/app/src/main/res/menu/menu_nav_drawer.xml b/app/src/main/res/menu/menu_nav_drawer.xml
new file mode 100644
index 0000000..926b79a
--- /dev/null
+++ b/app/src/main/res/menu/menu_nav_drawer.xml
@@ -0,0 +1,39 @@
+
+
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index 7c10538..6d8acf2 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -1,41 +1,43 @@