diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 39cb9bf..9f8938b 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -38,6 +38,9 @@
+
diff --git a/app/src/main/java/com/dscvit/vitty/activity/AuthActivity.kt b/app/src/main/java/com/dscvit/vitty/activity/AuthActivity.kt
index 7882045..a58db1c 100755
--- a/app/src/main/java/com/dscvit/vitty/activity/AuthActivity.kt
+++ b/app/src/main/java/com/dscvit/vitty/activity/AuthActivity.kt
@@ -10,6 +10,7 @@ import android.view.View
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.databinding.DataBindingUtil
@@ -24,6 +25,7 @@ import com.dscvit.vitty.util.Constants.TOKEN
import com.dscvit.vitty.util.Constants.UID
import com.dscvit.vitty.util.Constants.USER_INFO
import com.dscvit.vitty.util.NotificationPermissionHelper
+import com.dscvit.vitty.util.MaintenanceChecker
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInClient
@@ -62,6 +64,22 @@ class AuthActivity : AppCompatActivity() {
configureGoogleSignIn()
setupUI()
+ setupBackPressedHandler()
+ }
+
+ private fun setupBackPressedHandler() {
+ val callback = object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ binding.apply {
+ if (introPager.currentItem == 0 || loginClick) {
+ finish()
+ } else {
+ introPager.currentItem--
+ }
+ }
+ }
+ }
+ onBackPressedDispatcher.addCallback(this, callback)
}
private fun setupNotificationPermissionLauncher() {
@@ -266,8 +284,9 @@ class AuthActivity : AppCompatActivity() {
val email = firebaseAuth.currentUser?.email
Timber.d("Firebase authentication successful - uid: $uid, email: $email")
saveInfo(acct.idToken, uid)
- authViewModel.signInAndGetTimeTable("", "", uid ?: "", "")
- leadToNextPage()
+
+ // Quick maintenance check for new login
+ checkMaintenanceBeforeProceed()
} else {
Timber.e("Firebase authentication failed: ${authResult.exception?.message}")
logoutFailed()
@@ -278,6 +297,20 @@ class AuthActivity : AppCompatActivity() {
}
}
+ private fun checkMaintenanceBeforeProceed() {
+ MaintenanceChecker.checkMaintenanceStatusAsync(this) { isUnderMaintenance ->
+ if (isUnderMaintenance) {
+ binding.loadingView.visibility = View.GONE
+ val intent = Intent(this, MaintenanceActivity::class.java)
+ startActivity(intent)
+ finish()
+ } else {
+ authViewModel.signInAndGetTimeTable("", "", firebaseAuth.currentUser?.uid ?: "", "")
+ leadToNextPage()
+ }
+ }
+ }
+
private fun leadToNextPage() {
authViewModel.signInResponse.observe(this) {
if (it != null) {
@@ -323,14 +356,4 @@ class AuthActivity : AppCompatActivity() {
}
}
}
-
- override fun onBackPressed() {
- binding.apply {
- if (introPager.currentItem == 0 || loginClick) {
- super.onBackPressed()
- } else {
- introPager.currentItem--
- }
- }
- }
}
diff --git a/app/src/main/java/com/dscvit/vitty/activity/MaintenanceActivity.kt b/app/src/main/java/com/dscvit/vitty/activity/MaintenanceActivity.kt
new file mode 100644
index 0000000..1b4fef1
--- /dev/null
+++ b/app/src/main/java/com/dscvit/vitty/activity/MaintenanceActivity.kt
@@ -0,0 +1,52 @@
+package com.dscvit.vitty.activity
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.FragmentActivity
+import com.dscvit.vitty.R
+import com.dscvit.vitty.databinding.ActivityMaintenanceBinding
+import com.dscvit.vitty.theme.VittyTheme
+import com.dscvit.vitty.ui.maintenance.MaintenanceScreen
+import com.dscvit.vitty.util.MaintenanceChecker
+
+class MaintenanceActivity : FragmentActivity() {
+
+ private lateinit var binding: ActivityMaintenanceBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_maintenance)
+
+ binding.composeView.apply {
+ setViewCompositionStrategy(
+ ViewCompositionStrategy.DisposeOnLifecycleDestroyed(this@MaintenanceActivity)
+ )
+ setContent {
+ VittyTheme {
+ MaintenanceScreen(
+ onRetryClick = { retryConnection() },
+ onExitClick = { exitApp() }
+ )
+ }
+ }
+ }
+ }
+
+ private fun retryConnection() {
+ MaintenanceChecker.checkMaintenanceStatusAsync(this) { isUnderMaintenance ->
+ if (!isUnderMaintenance) {
+ val intent = Intent(this, InstructionsActivity::class.java)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ startActivity(intent)
+ finish()
+ }
+ }
+ }
+
+ private fun exitApp() {
+ finishAffinity()
+ }
+}
diff --git a/app/src/main/java/com/dscvit/vitty/network/api/community/APICommunity.kt b/app/src/main/java/com/dscvit/vitty/network/api/community/APICommunity.kt
index bbc5bfe..f3a0380 100644
--- a/app/src/main/java/com/dscvit/vitty/network/api/community/APICommunity.kt
+++ b/app/src/main/java/com/dscvit/vitty/network/api/community/APICommunity.kt
@@ -29,6 +29,9 @@ import retrofit2.http.Path
import retrofit2.http.Query
interface APICommunity {
+ @GET("/")
+ fun checkServerStatus(): Call
+
@Headers("Content-Type: application/json")
@POST("/api/v3/auth/check-username")
fun checkUsername(
diff --git a/app/src/main/java/com/dscvit/vitty/network/api/community/APICommunityRestClient.kt b/app/src/main/java/com/dscvit/vitty/network/api/community/APICommunityRestClient.kt
index d9ef161..5ef302a 100644
--- a/app/src/main/java/com/dscvit/vitty/network/api/community/APICommunityRestClient.kt
+++ b/app/src/main/java/com/dscvit/vitty/network/api/community/APICommunityRestClient.kt
@@ -1050,4 +1050,30 @@ class APICommunityRestClient {
},
)
}
+
+ fun checkServerStatus(
+ retrofitServerStatusListener: RetrofitServerStatusListener,
+ ) {
+ mApiUser = retrofit.create(APICommunity::class.java)
+ val apiServerStatusCall = mApiUser!!.checkServerStatus()
+ apiServerStatusCall.enqueue(
+ object : Callback {
+ override fun onResponse(
+ call: Call,
+ response: Response,
+ ) {
+ Timber.d("ServerStatus: ${response.body()}")
+ retrofitServerStatusListener.onSuccess(call, response.body(), response.isSuccessful)
+ }
+
+ override fun onFailure(
+ call: Call,
+ t: Throwable,
+ ) {
+ Timber.d("ServerStatusError: ${t.message}")
+ retrofitServerStatusListener.onError(call, t)
+ }
+ },
+ )
+ }
}
diff --git a/app/src/main/java/com/dscvit/vitty/network/api/community/RetrofitCommunityListener.kt b/app/src/main/java/com/dscvit/vitty/network/api/community/RetrofitCommunityListener.kt
index 41ab0b8..f178876 100644
--- a/app/src/main/java/com/dscvit/vitty/network/api/community/RetrofitCommunityListener.kt
+++ b/app/src/main/java/com/dscvit/vitty/network/api/community/RetrofitCommunityListener.kt
@@ -169,3 +169,15 @@ interface RetrofitActiveFriendsListener {
t: Throwable,
)
}
+
+interface RetrofitServerStatusListener {
+ fun onSuccess(
+ call: Call,
+ response: String?,
+ isSuccessful: Boolean
+ )
+ fun onError(
+ call: Call,
+ t: Throwable
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/dscvit/vitty/ui/main/MainComposeApp.kt b/app/src/main/java/com/dscvit/vitty/ui/main/MainComposeApp.kt
index 0437040..58d4ad1 100644
--- a/app/src/main/java/com/dscvit/vitty/ui/main/MainComposeApp.kt
+++ b/app/src/main/java/com/dscvit/vitty/ui/main/MainComposeApp.kt
@@ -103,8 +103,10 @@ import com.dscvit.vitty.ui.schedule.ScheduleViewModel
import com.dscvit.vitty.util.Analytics
import com.dscvit.vitty.util.Constants
import com.dscvit.vitty.util.LogoutHelper
+import com.dscvit.vitty.util.MaintenanceChecker
import com.dscvit.vitty.util.SemesterUtils
import com.dscvit.vitty.util.UtilFunctions
+import com.dscvit.vitty.ui.maintenance.MaintenanceBannerDialog
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.net.URLDecoder
@@ -113,6 +115,7 @@ import java.nio.charset.StandardCharsets
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import timber.log.Timber
@Composable
fun MainComposeApp() {
@@ -129,6 +132,9 @@ fun MainComposeApp() {
var campus by remember { mutableStateOf(prefs.getString(Constants.COMMUNITY_CAMPUS, "") ?: "") }
var showCampusDialog by remember { mutableStateOf(campus.isEmpty()) }
+
+ // Maintenance banner state
+ var showMaintenanceBanner by remember { mutableStateOf(false) }
DisposableEffect(Unit) {
val listener =
@@ -208,13 +214,24 @@ fun MainComposeApp() {
}
}
+ LaunchedEffect(Unit) {
+ if (UtilFunctions.isNetworkAvailable(context)) {
+ MaintenanceChecker.checkMaintenanceStatusAsync(context) { isUnderMaintenance ->
+ Timber.d("Dialog check result: isUnderMaintenance=%s", isUnderMaintenance)
+ showMaintenanceBanner = isUnderMaintenance
+ }
+ } else {
+ Timber.d("No network available")
+ }
+ }
+
VittyTheme {
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
DrawerContent(
navController = navController,
- onCloseDrawer = { scope.launch { drawerState.close() } },
+ onCloseDrawer = { scope.launch { drawerState.close() } }
)
},
) {
@@ -866,6 +883,19 @@ fun MainComposeApp() {
onDismiss = { showCampusDialog = false },
)
}
+
+ // Maintenance Banner Dialog
+ MaintenanceBannerDialog(
+ isVisible = showMaintenanceBanner,
+ onDismiss = {
+ showMaintenanceBanner = false
+ },
+ onRetryClick = {
+ MaintenanceChecker.checkMaintenanceStatusAsync(context) { isUnderMaintenance ->
+ showMaintenanceBanner = isUnderMaintenance
+ }
+ }
+ )
}
}
}
@@ -1091,7 +1121,7 @@ fun NavigationItem(
@Composable
fun DrawerContent(
navController: NavHostController,
- onCloseDrawer: () -> Unit,
+ onCloseDrawer: () -> Unit
) {
val context = LocalContext.current
val prefs = remember { context.getSharedPreferences(Constants.USER_INFO, 0) }
diff --git a/app/src/main/java/com/dscvit/vitty/ui/maintenance/MaintenanceBanner.kt b/app/src/main/java/com/dscvit/vitty/ui/maintenance/MaintenanceBanner.kt
new file mode 100644
index 0000000..161adc5
--- /dev/null
+++ b/app/src/main/java/com/dscvit/vitty/ui/maintenance/MaintenanceBanner.kt
@@ -0,0 +1,270 @@
+package com.dscvit.vitty.ui.maintenance
+
+import androidx.compose.animation.*
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Warning
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogProperties
+import com.dscvit.vitty.R
+import com.dscvit.vitty.theme.*
+
+@Composable
+fun MaintenanceBannerDialog(
+ isVisible: Boolean,
+ onDismiss: () -> Unit,
+ onRetryClick: () -> Unit
+) {
+ if (isVisible) {
+ Dialog(
+ onDismissRequest = onDismiss,
+ properties = DialogProperties(
+ dismissOnBackPress = true,
+ dismissOnClickOutside = true
+ )
+ ) {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ shape = RoundedCornerShape(20.dp),
+ colors = CardDefaults.cardColors(
+ containerColor = Background
+ ),
+ elevation = CardDefaults.cardElevation(
+ defaultElevation = 8.dp
+ )
+ ) {
+ Column(
+ modifier = Modifier.padding(20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ // Header with close button
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ IconButton(
+ onClick = onDismiss,
+ modifier = Modifier.size(24.dp)
+ ) {
+ Icon(
+ imageVector = Icons.Default.Close,
+ contentDescription = "Close",
+ tint = Accent.copy(alpha = 0.7f),
+ modifier = Modifier.size(20.dp)
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // Maintenance icon with gradient background
+ Box(
+ modifier = Modifier
+ .size(80.dp)
+ .clip(RoundedCornerShape(16.dp))
+ .background(
+ brush = Brush.radialGradient(
+ colors = listOf(
+ Accent.copy(alpha = 0.2f),
+ Accent.copy(alpha = 0.1f)
+ )
+ )
+ ),
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_maintenance),
+ contentDescription = "Maintenance",
+ modifier = Modifier.size(40.dp),
+ tint = Accent
+ )
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // Title
+ Text(
+ text = "Server Maintenance",
+ fontSize = 22.sp,
+ fontWeight = FontWeight.Bold,
+ color = TextColor,
+ textAlign = TextAlign.Center
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ // Message
+ Text(
+ text = "The server is currently under maintenance. Some features may be temporarily unavailable.",
+ fontSize = 14.sp,
+ color = Accent.copy(alpha = 0.8f),
+ textAlign = TextAlign.Center,
+ lineHeight = 18.sp
+ )
+
+ Spacer(modifier = Modifier.height(20.dp))
+
+ // Action buttons
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ // Retry button
+ Button(
+ onClick = onRetryClick,
+ modifier = Modifier.weight(1f),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = Accent,
+ contentColor = Background
+ ),
+ shape = RoundedCornerShape(12.dp)
+ ) {
+ Text(
+ text = "Retry",
+ fontSize = 14.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+
+ // Dismiss button
+ OutlinedButton(
+ onClick = onDismiss,
+ modifier = Modifier.weight(1f),
+ colors = ButtonDefaults.outlinedButtonColors(
+ contentColor = Accent.copy(alpha = 0.8f)
+ ),
+ shape = RoundedCornerShape(12.dp)
+ ) {
+ Text(
+ text = "Continue",
+ fontSize = 14.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ // Footer
+ Text(
+ text = "Thank you for your patience",
+ fontSize = 12.sp,
+ color = Accent.copy(alpha = 0.6f),
+ textAlign = TextAlign.Center,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun MaintenanceBanner(
+ isVisible: Boolean,
+ onDismiss: () -> Unit,
+ onRetryClick: () -> Unit
+) {
+ AnimatedVisibility(
+ visible = isVisible,
+ enter = slideInVertically(
+ initialOffsetY = { -it }
+ ) + fadeIn(),
+ exit = slideOutVertically(
+ targetOffsetY = { -it }
+ ) + fadeOut()
+ ) {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(
+ containerColor = Secondary.copy(alpha = 0.95f)
+ ),
+ elevation = CardDefaults.cardElevation(
+ defaultElevation = 6.dp
+ )
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ // Icon
+ Icon(
+ painter = painterResource(id = R.drawable.ic_maintenance),
+ contentDescription = "Maintenance",
+ modifier = Modifier.size(24.dp),
+ tint = Accent
+ )
+
+ Spacer(modifier = Modifier.width(12.dp))
+
+ // Text content
+ Column(
+ modifier = Modifier.weight(1f)
+ ) {
+ Text(
+ text = "Server Maintenance",
+ fontSize = 14.sp,
+ fontWeight = FontWeight.SemiBold,
+ color = Accent
+ )
+ Text(
+ text = "Some features may be limited",
+ fontSize = 12.sp,
+ color = Accent.copy(alpha = 0.7f)
+ )
+ }
+
+ // Action buttons
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ TextButton(
+ onClick = onRetryClick,
+ contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp)
+ ) {
+ Text(
+ text = "Retry",
+ fontSize = 12.sp,
+ color = Accent,
+ fontWeight = FontWeight.Medium
+ )
+ }
+
+ IconButton(
+ onClick = onDismiss,
+ modifier = Modifier.size(24.dp)
+ ) {
+ Icon(
+ imageVector = Icons.Default.Close,
+ contentDescription = "Dismiss",
+ tint = Accent.copy(alpha = 0.7f),
+ modifier = Modifier.size(16.dp)
+ )
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/dscvit/vitty/ui/maintenance/MaintenanceScreen.kt b/app/src/main/java/com/dscvit/vitty/ui/maintenance/MaintenanceScreen.kt
new file mode 100644
index 0000000..429d00d
--- /dev/null
+++ b/app/src/main/java/com/dscvit/vitty/ui/maintenance/MaintenanceScreen.kt
@@ -0,0 +1,115 @@
+package com.dscvit.vitty.ui.maintenance
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.dscvit.vitty.R
+import com.dscvit.vitty.theme.*
+
+@Composable
+fun MaintenanceScreen(
+ onRetryClick: () -> Unit,
+ onExitClick: () -> Unit
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(32.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_maintenance),
+ contentDescription = stringResource(R.string.maintenance_icon_desc),
+ modifier = Modifier.size(120.dp),
+ colorFilter = ColorFilter.tint(Accent)
+ )
+
+ Spacer(modifier = Modifier.height(32.dp))
+
+ Text(
+ text = stringResource(R.string.maintenance_title),
+ fontSize = 28.sp,
+ fontWeight = FontWeight.SemiBold,
+ color = TextColor,
+ textAlign = TextAlign.Center,
+ lineHeight = 36.sp
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ Text(
+ text = stringResource(R.string.maintenance_message),
+ fontSize = 16.sp,
+ color = Accent,
+ textAlign = TextAlign.Center,
+ lineHeight = 20.sp
+ )
+
+ Spacer(modifier = Modifier.height(24.dp))
+
+ Text(
+ text = stringResource(R.string.maintenance_description),
+ fontSize = 14.sp,
+ color = Accent.copy(alpha = 0.8f),
+ textAlign = TextAlign.Center,
+ lineHeight = 16.sp
+ )
+
+ Spacer(modifier = Modifier.height(48.dp))
+
+ Button(
+ onClick = onRetryClick,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(56.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = Accent,
+ contentColor = Background
+ ),
+ shape = RoundedCornerShape(12.dp)
+ ) {
+ Text(
+ text = stringResource(R.string.retry_connection),
+ fontSize = 16.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ TextButton(
+ onClick = onExitClick
+ ) {
+ Text(
+ text = stringResource(R.string.exit_app),
+ fontSize = 14.sp,
+ color = Accent.copy(alpha = 0.7f)
+ )
+ }
+
+ Spacer(modifier = Modifier.height(32.dp))
+
+ Text(
+ text = stringResource(R.string.maintenance_footer),
+ fontSize = 12.sp,
+ color = Accent.copy(alpha = 0.6f),
+ textAlign = TextAlign.Center
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/dscvit/vitty/util/MaintenanceChecker.kt b/app/src/main/java/com/dscvit/vitty/util/MaintenanceChecker.kt
new file mode 100644
index 0000000..e03b6f3
--- /dev/null
+++ b/app/src/main/java/com/dscvit/vitty/util/MaintenanceChecker.kt
@@ -0,0 +1,57 @@
+package com.dscvit.vitty.util
+
+import android.content.Context
+import com.dscvit.vitty.network.api.community.APICommunityRestClient
+import com.dscvit.vitty.network.api.community.RetrofitServerStatusListener
+import retrofit2.Call
+import timber.log.Timber
+
+object MaintenanceChecker {
+
+ private const val API_OPERATIONAL_MESSAGE = "Welcome to VITTY API!🎉"
+ private var isChecking = false
+
+ fun checkMaintenanceStatusAsync(
+ context: Context,
+ onResult: (isUnderMaintenance: Boolean) -> Unit
+ ) {
+ if (isChecking) return
+
+ isChecking = true
+
+ APICommunityRestClient.instance.checkServerStatus(
+ object : RetrofitServerStatusListener {
+ override fun onSuccess(call: Call, response: String?, isSuccessful: Boolean) {
+ val result = when {
+ !isSuccessful -> {
+ Timber.d("Server returned error")
+ true // Server error = maintenance
+ }
+ response == null -> {
+ Timber.d("No response body received")
+ true // No response = maintenance
+ }
+ response.contains(API_OPERATIONAL_MESSAGE) -> {
+ Timber.d("API operational message found")
+ false // API working
+ }
+ else -> {
+ Timber.d("Different response received: $response")
+ true // Different response = maintenance
+ }
+ }
+
+ Timber.d("Maintenance check: isUnderMaintenance=$result, response=$response")
+ isChecking = false
+ onResult(result)
+ }
+
+ override fun onError(call: Call, t: Throwable) {
+ Timber.e(t, "Maintenance check failed")
+ isChecking = false
+ onResult(false) // Network error = don't assume maintenance
+ }
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_maintenance.xml b/app/src/main/res/drawable/ic_maintenance.xml
new file mode 100644
index 0000000..8554ac4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_maintenance.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_screwdriver.xml b/app/src/main/res/drawable/ic_screwdriver.xml
new file mode 100644
index 0000000..8554ac4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_screwdriver.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_maintenance.xml b/app/src/main/res/layout/activity_maintenance.xml
new file mode 100644
index 0000000..3475e7a
--- /dev/null
+++ b/app/src/main/res/layout/activity_maintenance.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0bf6f78..882d931 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -132,4 +132,12 @@
Hello blank fragment
+
+ Server Under Maintenance
+ We\'re currently performing server maintenance to improve your experience.
+ Please check back in a few minutes. We\'ll be back online soon!
+ Try Again
+ Exit App
+ Thank you for your patience
+ Maintenance Icon