AndroidCareProvider - Code Quality & Technical Debt Report

Repository: AndroidCareProvider
Platform: Android (Java + Kotlin)
Analysis Date: November 7, 2025
Version: 2.0.33 (Build 68)
Language Distribution: Java (85%), Kotlin (15%)


Executive Summary

This code quality analysis identifies significant technical debt across the AndroidCareProvider codebase. The application contains ~150,000+ lines of Java code with numerous code smells, anti-patterns, and maintainability issues. Key problems include God Classes (2,800+ LOC files), excessive copy-paste code, poor error handling, and lack of architectural consistency.

Quality Metrics Summary

Metric Value Status Industry Standard
Total LOC ~150,000+ ⚠️ Large -
Largest File 2,963 LOC 🔴 Critical <500 LOC
God Classes 5+ 🔴 Critical 0
TODO Comments 50+ 🟠 High <10
Generic Exception Catches 100+ 🔴 Critical 0
Duplicated Permission Classes 3 🟠 High 1
Static Mutable State 15+ 🟠 High 0
Naming Convention Issues 20+ 🟡 Medium 0

Technical Debt Assessment

Category Severity Count
God Classes 🔴 Critical 5
Error Handling 🔴 Critical 100+
Code Duplication 🟠 High 30%+
Static State 🟠 High 15+
Naming Violations 🟡 Medium 20+
Deprecated Code 🟡 Medium 10+

Table of Contents

  1. Code Complexity & Size Issues
  2. Error Handling Anti-Patterns
  3. Code Duplication
  4. Naming Conventions
  5. Architecture & Design Issues
  6. Memory & Performance
  7. Code Smells
  8. Deprecated & Dead Code
  9. Documentation Issues
  10. Refactoring Recommendations

Code Complexity & Size Issues

🔴 CRITICAL: God Classes (5 Files >2,000 LOC)

Issue: Multiple files exceed 2,000 lines of code, violating Single Responsibility Principle.

File Lines Complexity Issue
CalendarCustomView.java 2,963 🔴 Extreme Calendar logic, API calls, UI, scheduling all mixed
WeeklyScheduleFragment.java 2,824 🔴 Extreme Schedule management, validation, API, UI combined
Utils.java 2,000+ 🔴 Extreme Utility dumping ground with 100+ methods
LoginActivity.java 1,500+ 🟠 High Auth, social login, navigation, validation
RegisterBasicInfo.java 919 🟠 High Registration logic with excessive UI code

Example - CalendarCustomView.java:

// Lines 1-2963 - Single file doing EVERYTHING
public class CalendarCustomView extends LinearLayout {
    // 50+ instance variables
    private TextView previousButton, current_year;
    private ExpandableHeightGridView calendarGridView;
    private SimpleDateFormat formatter1, formatter2;
    private Calendar cal, cal1;
    private GridAdapter mAdapter;
    private SlotsGridAdapter slotsAdapter;
    // ... 40 more fields ...

    // 80+ methods covering:
    // - Calendar rendering
    // - Slot booking
    // - API calls (10+ endpoints)
    // - Payment integration
    // - Date validation
    // - UI state management
    // - Event handling
    // - Data parsing

    private class DoCalculationTask extends AsyncTask<String, Void, Void> {
        // Async task inside view class (anti-pattern)
    }
}

Problems:
- Single Responsibility violated - doing calendar UI, business logic, networking, and data management
- Impossible to test - no separation of concerns
- High coupling - changes in one area break others
- Performance issues - massive object creation
- Merge conflicts - multiple developers editing same file

Refactoring Plan:

// Recommended structure:

// 1. ViewModel for business logic
class CalendarViewModel : ViewModel() {
    private val repository = CalendarRepository()
    val slots = MutableLiveData<List<Slot>>()

    fun loadSlots(date: Date) {
        viewModelScope.launch {
            slots.value = repository.getSlots(date)
        }
    }
}

// 2. Repository for data layer
class CalendarRepository(private val api: CalendarApi) {
    suspend fun getSlots(date: Date): List<Slot> {
        return api.getSlots(date)
    }
}

// 3. Simple View class (<300 LOC)
class CalendarCustomView(context: Context) : LinearLayout(context) {
    private val viewModel: CalendarViewModel by viewModels()

    init {
        setupUI()
        observeData()
    }

    private fun setupUI() { /* UI setup only */ }
    private fun observeData() { /* Observe ViewModel */ }
}

// 4. Separate utility classes
object DateUtils { /* Date operations */ }
object ValidationUtils { /* Validation logic */ }

🟠 HIGH: Excessively Long Methods

Issue: Many methods exceed 100 lines, making them hard to understand and test.

File Method Lines Issue
WeeklyScheduleFragment.java onCreateView() 300+ Initialization hell
CalendarCustomView.java FillBookingGridList() 200+ Complex booking logic
LoginActivity.java FBLogin() 150+ Facebook login with inline callbacks
RegisterBasicInfo.java RegisterCareProvider() 200+ Form validation + API call

Example:

// WeeklyScheduleFragment.java - onCreateView()
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_weekly_schedule, container, false);

    // 300 lines of:
    // - findViewById calls (50+)
    // - Click listener setup (30+)
    // - Adapter initialization (10+)
    // - API calls (5+)
    // - Validation logic (50+)
    // - Date calculations (40+)
    // - UI state setup (60+)
    // - Permission checks (20+)

    return view;  // Line 300+
}

Recommendation:

// Break into smaller functions
override fun onCreateView(...): View {
    val view = inflater.inflate(R.layout.fragment_weekly_schedule, container, false)
    initializeViews(view)
    setupAdapters()
    setupClickListeners()
    observeViewModel()
    return view
}

private fun initializeViews(view: View) { /* <30 LOC */ }
private fun setupAdapters() { /* <20 LOC */ }
private fun setupClickListeners() { /* <40 LOC */ }
private fun observeViewModel() { /* <20 LOC */ }

🟡 MEDIUM: High Cyclomatic Complexity

Issue: Deep nesting and excessive branching logic.

Example:

// Typical pattern found throughout
public void processSlot(Slot slot) {
    if (slot != null) {
        if (slot.isAvailable()) {
            if (userIsLoggedIn()) {
                if (hasPaymentMethod()) {
                    if (slotNotExpired()) {
                        if (userHasPermission()) {
                            // Finally do something (6 levels deep!)
                            bookSlot(slot);
                        } else {
                            showPermissionError();
                        }
                    } else {
                        showExpiredError();
                    }
                } else {
                    showPaymentError();
                }
            } else {
                redirectToLogin();
            }
        } else {
            showUnavailableError();
        }
    }
}

Better Approach:

fun processSlot(slot: Slot?) {
    // Early returns reduce nesting
    if (slot == null) return
    if (!slot.isAvailable()) return showUnavailableError()
    if (!userIsLoggedIn()) return redirectToLogin()
    if (!hasPaymentMethod()) return showPaymentError()
    if (slotNotExpired()) return showExpiredError()
    if (!userHasPermission()) return showPermissionError()

    bookSlot(slot)
}

Error Handling Anti-Patterns

🔴 CRITICAL: Generic Exception Catching (100+ Occurrences)

Severity: Critical
Count: 100+ instances

Issue: Catching broad Exception or Throwable hides bugs and makes debugging impossible.

Examples Found:

// 1. Silent failures (50+ occurrences)
try {
    // Complex operation
    performCriticalOperation();
} catch (Exception e) {
    // NO ACTION TAKEN - failure silently ignored!
}

// 2. Only printStackTrace (30+ occurrences)
try {
    processPayment();
} catch (Exception e) {
    e.printStackTrace();  // ❌ Not useful in production
    // User sees nothing, payment fails silently
}

// 3. Catching Throwable (10+ occurrences)
try {
    savePatientData();
} catch (Throwable t) {  // ❌ Too broad, catches OutOfMemoryError etc.
    // Application should crash for serious errors
}

// 4. Commented exception handling
try {
    authenticate();
} catch (Exception e) {
    // e.printStackTrace();  // ❌ Commented out, no error handling
}

Specific Examples:

// clsShow_Alarm.java - Lines 42-48
try {
    // Complex alarm logic
} catch (Exception e) {
    e.printStackTrace();  // ❌ User never knows alarm failed
}

try {
    // More logic
} catch (Exception e) {
    e.printStackTrace();
}

try {
    // Even more
} catch (Exception ex) {
    // No action at all! ❌
}

// CareProvider/Schedule/CalendarCareProviderView.java - Line 948
try {
    parseSlotData(jsonArray);
} catch (Throwable t) {  // ❌ Catching Throwable
    // Silent failure
}

// PaymentWebActivity.java - Lines 125-126
try {
    processPaymentResponse();
} catch (Exception e) {
    e.printStackTrace();  // ❌ Payment failure not communicated
}

// LoginActivity.java - Lines 521-522 (Commented)
//} catch (Exception e) {
//    e.printStackTrace();  // ❌ Exception handling removed, not replaced
//}

Impact:
- Silent failures in payment processing
- Lost patient data without user notification
- Impossible debugging in production
- HIPAA violation (no audit trail of errors)

Recommended Fix:

// Specific exception handling
try {
    performCriticalOperation()
} catch (NetworkException e) {
    showError(R.string.network_error)
    logError("NetworkError in performCriticalOperation", e)
} catch (ValidationException e) {
    showError(R.string.validation_error)
    logError("ValidationError", e)
} catch (Exception e) {
    // Last resort - log and notify user
    showError(R.string.unexpected_error)
    Crashlytics.logException(e)
    logError("Unexpected error", e)
}

// Custom logging utility
object ErrorLogger {
    fun logError(message: String, throwable: Throwable) {
        Log.e(TAG, message, throwable)

        if (BuildConfig.DEBUG) {
            // Show detailed error in debug
            Toast.makeText(context, message, Toast.LENGTH_LONG).show()
        }

        // Send to crash reporting
        FirebaseCrashlytics.getInstance().recordException(throwable)

        // Audit log for HIPAA compliance
        AuditLogger.logError(message, throwable)
    }
}

Refactoring Priority: HIGH - Fix immediately for critical paths (payment, patient data)


🟠 HIGH: No Error Recovery Strategies

Issue: When errors occur, application doesn’t attempt recovery.

Example:

// Typical API call pattern
AndroidNetworking.post(url)
    .addHeaders(Utils.getHeaders())
    .build()
    .getAsJSONObject(new JSONObjectRequestListener() {
        @Override
        public void onResponse(JSONObject response) {
            // Happy path only
            parseAndDisplay(response);
        }

        @Override
        public void onError(ANError error) {
            // ❌ Just hide loading, show generic error
            progressDialog.dismiss();
            Toast.makeText(context, "Error occurred", Toast.LENGTH_SHORT).show();
            // No retry, no offline cache, no fallback
        }
    });

Better Approach:

class RetryInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var attempt = 0
        var response: Response

        do {
            try {
                response = chain.proceed(chain.request())
                if (response.isSuccessful) return response
            } catch (e: IOException) {
                if (attempt >= MAX_RETRIES) throw e
            }
            attempt++
            delay(RETRY_DELAY_MS * attempt)
        } while (attempt < MAX_RETRIES)

        return response
    }
}

// ViewModel with error handling
class PatientViewModel : ViewModel() {
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    val uiState: StateFlow<UiState> = _uiState

    fun loadPatientData(patientId: String) {
        viewModelScope.launch {
            _uiState.value = UiState.Loading

            try {
                val data = repository.getPatientData(patientId)
                _uiState.value = UiState.Success(data)
            } catch (e: NetworkException) {
                // Try cache
                val cachedData = repository.getCachedData(patientId)
                if (cachedData != null) {
                    _uiState.value = UiState.Success(cachedData, isOffline = true)
                } else {
                    _uiState.value = UiState.Error(
                        message = "Network error",
                        canRetry = true
                    )
                }
            } catch (e: Exception) {
                _uiState.value = UiState.Error(
                    message = e.message ?: "Unknown error",
                    canRetry = false
                )
                Crashlytics.logException(e)
            }
        }
    }
}

Code Duplication

🟠 HIGH: Permission Handling Duplication (3 Identical Classes)

Issue: Three nearly identical classes for permission handling.

File Purpose LOC Duplication
clsCameraPermission.java Camera permission 150 95% duplicate
clsRecordingPermission.java Audio permission 150 95% duplicate
clsStoragePermission.java Storage permission 150 95% duplicate
clsDrawOverAppPermission.java Overlay permission 150 80% duplicate

Example - Identical Code in All Three:

// clsCameraPermission.java
public static final int REQUEST_CAMERA_PERMISSION = 200;
public static String[] permissions = {Manifest.permission.CAMERA};

public static void Check(Context context, Button btnAction, int opt) {
    if (hasPermissions(context, permissions)) {
        // Grant action
    } else {
        // Request permission
    }
}

public static boolean hasPermissions(Context context, String... permissions) {
    if (context != null && permissions != null) {
        for (String permission : permissions) {
            if (ActivityCompat.checkSelfPermission(context, permission) 
                != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
    }
    return true;
}

// ❌ EXACT SAME CODE in clsRecordingPermission.java with different permission
// ❌ EXACT SAME CODE in clsStoragePermission.java with different permission

Refactored Solution:

// Single permission manager
class PermissionManager(private val activity: Activity) {

    enum class Permission(val manifest: String, val requestCode: Int) {
        CAMERA(Manifest.permission.CAMERA, 200),
        AUDIO(Manifest.permission.RECORD_AUDIO, 201),
        STORAGE(Manifest.permission.WRITE_EXTERNAL_STORAGE, 202),
        OVERLAY(Manifest.permission.SYSTEM_ALERT_WINDOW, 203)
    }

    fun checkPermission(
        permission: Permission,
        onGranted: () -> Unit,
        onDenied: (shouldShowRationale: Boolean) -> Unit
    ) {
        when {
            hasPermission(permission) -> onGranted()
            shouldShowRationale(permission) -> {
                onDenied(true)
            }
            else -> {
                requestPermission(permission)
                onDenied(false)
            }
        }
    }

    private fun hasPermission(permission: Permission): Boolean {
        return ContextCompat.checkSelfPermission(activity, permission.manifest) 
            == PackageManager.PERMISSION_GRANTED
    }

    private fun shouldShowRationale(permission: Permission): Boolean {
        return ActivityCompat.shouldShowRequestPermissionRationale(
            activity, permission.manifest
        )
    }

    private fun requestPermission(permission: Permission) {
        ActivityCompat.requestPermissions(
            activity,
            arrayOf(permission.manifest),
            permission.requestCode
        )
    }
}

// Usage
val permissionManager = PermissionManager(this)

permissionManager.checkPermission(
    Permission.CAMERA,
    onGranted = { startCamera() },
    onDenied = { shouldShowRationale ->
        if (shouldShowRationale) {
            showPermissionRationale()
        } else {
            showPermissionSettings()
        }
    }
)

Estimated Savings: Delete 450 LOC, replace with 80 LOC


🟡 MEDIUM: Duplicated WebViewClient Classes

Issue: Two identical YXWebViewClient classes in different activities.

// ViewDocWebActivity.java - Line 53
public class YXWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        view.loadUrl(url);
        return true;
    }
}

// AddWalletAmountWebActivity.java - Line 120
public class YXWebViewClient extends WebViewClient {  // ❌ EXACT DUPLICATE
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        view.loadUrl(url);
        return true;
    }
}

Solution: Create single shared class in Stats/ package.


🟡 MEDIUM: Repeated API Header Code

Issue: API header construction duplicated across 100+ files.

// Pattern repeated everywhere:
.addHeaders("Authorization", "Bearer " + Utils.TokenPsyter)
.addHeaders("DeviceId", mpref.GetDeviceID())
.addHeaders("Language", Language)
.addHeaders("Culture", Culture)

Solution:

object ApiHeaderProvider {
    fun getDefaultHeaders(context: Context): Map<String, String> {
        val prefs = MySharedPreferences(context)
        return mapOf(
            "Authorization" to "Bearer ${Utils.TokenPsyter}",
            "DeviceId" to prefs.GetDeviceID(),
            "Language" to prefs.GetAppLanguage(),
            "Culture" to getCurrentCulture()
        )
    }
}

// Retrofit interceptor (even better)
class HeaderInterceptor(private val context: Context) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val original = chain.request()
        val builder = original.newBuilder()

        ApiHeaderProvider.getDefaultHeaders(context).forEach { (key, value) ->
            builder.header(key, value)
        }

        return chain.proceed(builder.build())
    }
}

Naming Conventions

🟡 MEDIUM: Hungarian Notation & Poor Class Names (20+ Files)

Issue: Inconsistent and non-standard naming conventions.

Current Name Issue Recommended
clsCameraPermission Hungarian notation (cls prefix) CameraPermissionManager
clsRecordingPermission Hungarian notation AudioPermissionManager
clsStoragePermission Hungarian notation StoragePermissionManager
clsDrawOverAppPermission Hungarian + unclear OverlayPermissionManager
clsShow_Alarm Hungarian + underscore AlarmDisplayManager
mpref Abbreviation sharedPreferences
mAdapter Hungarian (m prefix) adapter
MySharedPreferences Generic “My” prefix AppPreferences
MyFirebaseMessagingService Generic “My” AppMessagingService
CPPreferencesColumn Unclear abbreviation CareProviderPreferences

Example - Hungarian Notation Throughout:

// Current (bad)
public class clsCameraPermission {
    public static final int REQUEST_CAMERA_PERMISSION = 200;
    private static String mPermission = Manifest.permission.CAMERA;

    public static void Check(Context ctx, Button btnAction, int opt) {
        // ...
    }
}

// Recommended
public class CameraPermissionManager {
    companion object {
        const val REQUEST_CODE = 200
        const val PERMISSION = Manifest.permission.CAMERA
    }

    fun checkPermission(context: Context, button: Button, option: Int) {
        // ...
    }
}

Android Studio Inspection Settings:

<!-- Add to .idea/inspectionProfiles/Project_Default.xml -->
<profile>
    <inspection_tool class="MethodNamingConvention" enabled="true" level="WARNING">
        <option name="m_regex" value="[a-z][a-zA-Z0-9]*" />
        <!-- No Hungarian notation -->
    </inspection_tool>
    <inspection_tool class="ClassNamingConvention" enabled="true" level="WARNING">
        <option name="m_regex" value="[A-Z][a-zA-Z0-9]*" />
        <!-- No prefixes like cls, My, etc. -->
    </inspection_tool>
</profile>


🟡 MEDIUM: Inconsistent Variable Naming

Issue: Mix of camelCase, snake_case, and Hungarian notation.

// Found in same file:
private MySharedPreferences mpref;       // Hungarian
private String user_id = "userid";       // snake_case
private TextView day1, day2, day3;       // numeric suffix (should use array)
private LinearLayout day1Lay, day2Lay;   // Abbreviated
private ImageView weekIcon, monthIcon;   // Good camelCase
private Calendar c, cal, cal1;           // Inconsistent abbreviations

Recommended:

private val sharedPreferences: AppPreferences  // Full name
private val userId: String                     // camelCase
private val dayTextViews: Array<TextView>      // Array instead of day1, day2...
private val dayLayouts: Array<LinearLayout>    // Full words
private val weekIcon: ImageView                // Consistent
private val currentCalendar: Calendar          // Descriptive
private val displayCalendar: Calendar          // Clear purpose

Architecture & Design Issues

🔴 CRITICAL: Global Mutable State (15+ Static Variables)

Issue: Extensive use of static mutable variables for shared state.

Examples:

// Utils.java - Global mutable state (BAD)
public class Utils {
    public static String TokenPsyter = "";           // ❌ Auth token
    public static String TokenScheduling = "";       // ❌ Another token
    public static String UserType = "";              // ❌ User type
    public static String DocumentURL = "";           // ❌ Document URL
    public static String userId = "";                // ❌ User ID
    public static int slotDurationId = 0;           // ❌ Slot duration
    public static boolean isFromClient = false;      // ❌ Navigation state

    // Plus 50+ more static variables...
}

// RegisterVerification.java - Mutable static flags
public static boolean isChangeNeeded = false;       // ❌ State flag
public static boolean moveToEmail = false;          // ❌ Navigation flag
public static boolean moveToNumber = false;         // ❌ Another flag

Problems:
- Thread safety - no synchronization
- Memory leaks - static references never garbage collected
- Testing impossible - state persists between tests
- Race conditions - multiple activities modifying same static state
- Debugging nightmare - who modified the state?

Proper Solution:

// 1. Singleton with proper scoping (for app-level data)
object SessionManager {
    private var _authToken: String? = null
    val authToken: String?
        get() = _authToken

    fun setAuthToken(token: String) {
        _authToken = token
    }

    fun clearSession() {
        _authToken = null
    }
}

// 2. ViewModel for screen-level state (better)
class DocumentViewModel : ViewModel() {
    private val _documentUrl = MutableLiveData<String>()
    val documentUrl: LiveData<String> = _documentUrl

    fun setDocumentUrl(url: String) {
        _documentUrl.value = url
    }
}

// 3. Navigation args for activity communication (best)
// From Activity A
val intent = Intent(this, DocumentActivity::class.java)
intent.putExtra("DOCUMENT_URL", documentUrl)
startActivity(intent)

// In Activity B
val documentUrl = intent.getStringExtra("DOCUMENT_URL")

// Or with Navigation Component
val action = MainFragmentDirections.actionMainToDocument(documentUrl)
findNavController().navigate(action)

🟠 HIGH: AsyncTask Usage (Deprecated Since API 30)

Issue: Using deprecated AsyncTask instead of Kotlin Coroutines.

Found in:

// CalendarCustomView.java - Line 324
private class DoCalculationTask extends AsyncTask<String, Void, Void> {
    @Override
    protected Void doInBackground(String... params) {
        // Complex calculation on background thread
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        // Update UI
    }
}

// Usage
new DoCalculationTask().execute(param1, param2);

Migration to Coroutines:

class CalendarViewModel : ViewModel() {

    fun performCalculation(param1: String, param2: String) {
        viewModelScope.launch {
            // Switch to IO dispatcher
            val result = withContext(Dispatchers.IO) {
                // Background work
                complexCalculation(param1, param2)
            }

            // Automatically back on Main thread
            updateUI(result)
        }
    }

    private suspend fun complexCalculation(p1: String, p2: String): Result {
        // Heavy computation
        return Result()
    }

    private fun updateUI(result: Result) {
        // Update LiveData/StateFlow
    }
}

🟠 HIGH: Tight Coupling to Activities

Issue: Business logic embedded in Activities instead of ViewModels/Presenters.

Example:

// LoginActivity.java - 1500+ LOC
public class LoginActivity extends AppCompatActivity {

    // ❌ API logic in Activity
    private void performLogin() {
        AndroidNetworking.post(BaseURLS.BaseURL + "api/Login")
            .addBodyParameter("username", username)
            .addBodyParameter("password", password)
            .build()
            .getAsJSONObject(new JSONObjectRequestListener() {
                @Override
                public void onResponse(JSONObject response) {
                    // ❌ JSON parsing in Activity
                    try {
                        String token = response.getString("token");
                        String userId = response.getString("userId");

                        // ❌ SharedPreferences access in Activity
                        mpref.SetAuthClaimToken(token);
                        mpref.SetUserID(userId);

                        // ❌ Navigation logic mixed with data logic
                        if (userType.equals("1")) {
                            startActivity(new Intent(this, CareProviderMain.class));
                        } else {
                            startActivity(new Intent(this, ClientMain.class));
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onError(ANError error) {
                    Toast.makeText(LoginActivity.this, "Error", Toast.LENGTH_SHORT).show();
                }
            });
    }
}

Proper MVVM Architecture:

// 1. Data Layer - Repository
class AuthRepository(
    private val api: AuthApi,
    private val preferences: AppPreferences
) {
    suspend fun login(username: String, password: String): Result<User> {
        return try {
            val response = api.login(username, password)
            preferences.saveAuthToken(response.token)
            preferences.saveUserId(response.userId)
            Result.Success(response.user)
        } catch (e: Exception) {
            Result.Error(e)
        }
    }
}

// 2. ViewModel - Business Logic
class LoginViewModel(
    private val repository: AuthRepository
) : ViewModel() {

    private val _loginState = MutableStateFlow<LoginState>(LoginState.Idle)
    val loginState: StateFlow<LoginState> = _loginState

    fun login(username: String, password: String) {
        viewModelScope.launch {
            _loginState.value = LoginState.Loading

            val result = repository.login(username, password)

            _loginState.value = when (result) {
                is Result.Success -> LoginState.Success(result.data)
                is Result.Error -> LoginState.Error(result.exception.message)
            }
        }
    }
}

sealed class LoginState {
    object Idle : LoginState()
    object Loading : LoginState()
    data class Success(val user: User) : LoginState()
    data class Error(val message: String?) : LoginState()
}

// 3. Activity/Fragment - UI Only (<200 LOC)
class LoginActivity : AppCompatActivity() {
    private val viewModel: LoginViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        observeLoginState()
        setupClickListeners()
    }

    private fun observeLoginState() {
        lifecycleScope.launch {
            viewModel.loginState.collect { state ->
                when (state) {
                    LoginState.Idle -> hideLoading()
                    LoginState.Loading -> showLoading()
                    is LoginState.Success -> navigateToMain(state.user)
                    is LoginState.Error -> showError(state.message)
                }
            }
        }
    }

    private fun setupClickListeners() {
        binding.loginButton.setOnClickListener {
            viewModel.login(
                binding.usernameInput.text.toString(),
                binding.passwordInput.text.toString()
            )
        }
    }

    private fun navigateToMain(user: User) {
        val intent = when (user.type) {
            UserType.CARE_PROVIDER -> Intent(this, CareProviderMain::class.java)
            UserType.CLIENT -> Intent(this, ClientMain::class.java)
        }
        startActivity(intent)
        finish()
    }
}

Memory & Performance

🟠 HIGH: Memory Leaks from Static Context References

Issue: Storing Activity/Context in static variables.

// Utils.java
public class Utils {
    public static Context context;  // ❌ MEMORY LEAK

    public static void setContext(Context ctx) {
        context = ctx;  // ❌ Activity reference never released
    }
}

// Usage in Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
    Utils.setContext(this);  // ❌ This Activity will never be garbage collected
}

Impact: Activities remain in memory after destruction, causing OutOfMemoryError.

Fix:

// Use Application context for singletons
class MyApplication : Application() {
    companion object {
        lateinit var instance: MyApplication
            private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}

// Or use dependency injection
@Singleton
class Utils @Inject constructor(
    @ApplicationContext private val context: Context  // Application context, not Activity
) {
    // ...
}

🟡 MEDIUM: Excessive findViewById Calls

Issue: Repeated findViewById without caching.

// Typical pattern - called multiple times
public void updateUI() {
    ((TextView) findViewById(R.id.title)).setText("Title");      // ❌ Find view
    ((TextView) findViewById(R.id.subtitle)).setText("Subtitle"); // ❌ Find again
    ((ImageView) findViewById(R.id.icon)).setImageResource(R.drawable.ic_icon);
}

public void clearUI() {
    ((TextView) findViewById(R.id.title)).setText("");           // ❌ Find view again
    ((TextView) findViewById(R.id.subtitle)).setText("");
}

Solution - ViewBinding:

class MyActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMyBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMyBinding.inflate(layoutInflater)
        setContentView(binding.root)

        updateUI()
    }

    private fun updateUI() {
        binding.title.text = "Title"           // Direct reference, no findViewById
        binding.subtitle.text = "Subtitle"
        binding.icon.setImageResource(R.drawable.ic_icon)
    }
}

Code Smells

🟡 MEDIUM: Magic Numbers & Strings

Issue: Hardcoded values throughout code.

// Magic numbers everywhere
if (userType.equals("1")) {  // ❌ What is "1"?
    // Care provider logic
} else if (userType.equals("0")) {  // ❌ What is "0"?
    // Client logic
}

if (status == 2) {  // ❌ What does 2 mean?
    // Approved
} else if (status == 3) {  // ❌ What does 3 mean?
    // Rejected
}

// Magic strings
String url = BaseURLS.BaseURL + "api/Login";  // ❌ Hardcoded endpoint

Solution:

// Constants
object UserType {
    const val CARE_PROVIDER = "1"
    const val CLIENT = "0"
}

enum class AppointmentStatus(val code: Int) {
    PENDING(1),
    APPROVED(2),
    REJECTED(3),
    CANCELLED(4)
}

object ApiEndpoints {
    const val LOGIN = "api/Login"
    const val REGISTER = "api/Register"
    const val GET_APPOINTMENTS = "api/Appointments"
}

// Usage
if (userType == UserType.CARE_PROVIDER) {
    // Clear and self-documenting
}

when (AppointmentStatus.fromCode(status)) {
    AppointmentStatus.APPROVED -> handleApproved()
    AppointmentStatus.REJECTED -> handleRejected()
    else -> handlePending()
}

🟡 MEDIUM: Commented-Out Code (50+ Blocks)

Issue: Large blocks of commented code throughout the codebase.

Examples:

// MyFirebaseInstanceIdService.java - Lines 19-51 (ENTIRE SERVICE commented)
//public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
//    private static final String TAG = "MyFirebaseIIDService";
//    @Override
//    public void onNewToken(@NonNull String s) {
//            String CurrentToken = FirebaseInstanceId.getInstance().getToken();
//            ... 30 more lines ...
//    }
//}

// ViewDocWebActivity.java - Line 36
//settings.setJavaScriptEnabled(true);  // ❌ Dangerous if uncommented

// LoginActivity.java - Lines 521-522
//} catch (Exception e) {
//    e.printStackTrace();
//}

// Many more instances...

Action Required:
- Delete all commented code - use Git for history
- If code might be needed, create feature flag instead


🟡 MEDIUM: Inconsistent TODO Comments (50+)

Issue: 50+ “TODO Auto-generated method stub” comments never addressed.

// Repeated pattern (auto-generated, never removed):
@Override
public void onClick(View v) {
    // TODO Auto-generated method stub  // ❌ Template comment, no actual TODO
}

@Override
public void onError(ANError error) {
    // TODO Auto-generated method stub  // ❌ Should have error handling
}

Better Approach:

// Remove placeholder comments
override fun onClick(view: View) {
    when (view.id) {
        R.id.login_button -> performLogin()
        R.id.register_button -> navigateToRegister()
    }
}

// Or if truly incomplete:
override fun onError(error: ANError) {
    // TODO: Implement proper error handling for network failures
    //       Priority: High, Assigned: @username, Ticket: PSY-123
    showGenericError()
}

Deprecated & Dead Code

🟡 MEDIUM: Entire Commented Service Classes

File: MyFirebaseInstanceIdService.java
Status: Entire file commented out (50+ lines)

//public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
//    // Entire service implementation commented...
//}

Action: Delete file, functionality moved to MyFirebaseMessagingService.


🟡 MEDIUM: Legacy WebRTC Code Still Present

File: Collaboration/Presence/CollaborationMain.java
Status: Old video call implementation (deprecated)

// Old WebRTC implementation still in codebase
public class CollaborationMain extends AppCompatActivity {
    // ❌ Legacy code - replaced by VideoSDK (OneToOneCallActivity.kt)
    // Still occupies ~500 LOC
}

Files to Delete:
- Collaboration/Presence/CollaborationMain.java
- Collaboration/Presence/WebAPICall.java (hardcoded test credentials)
- Collaboration/Presence/NaiveSSLContext.java (disables SSL verification!)
- Collaboration/Presence/StatusAdapter.java
- Collaboration/Presence/IncomingCall.java

Estimated Cleanup: Remove ~2,000 LOC of dead code


Documentation Issues

🟠 HIGH: No JavaDoc for Public APIs

Issue: Public methods lack documentation.

// Current (no documentation)
public class Utils {
    public static String ConvertDate(String dob) {  // ❌ What format? What does it convert to?
        // Complex date conversion logic
        return convertedDate;
    }

    public static void showAlertDialog(Context context, String title, String message, String type) {
        // ❌ What is "type"? What values are valid?
    }
}

Recommended:

/**
 * Converts a date string from one format to another.
 *
 * @param dob Date of birth in format "dd/MM/yyyy"
 * @return Formatted date string in "yyyy-MM-dd" format
 * @throws ParseException if input date format is invalid
 */
fun convertDate(dob: String): String {
    val inputFormat = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())
    val outputFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
    val date = inputFormat.parse(dob) ?: throw ParseException("Invalid date format", 0)
    return outputFormat.format(date)
}

/**
 * Displays an alert dialog to the user.
 *
 * @param context Android context for dialog creation
 * @param title Dialog title text
 * @param message Dialog body message
 * @param type Dialog type: "success", "error", "warning", or "info"
 */
fun showAlertDialog(
    context: Context,
    title: String,
    message: String,
    @DialogType type: String
) {
    // Implementation
}

@StringDef(TYPE_SUCCESS, TYPE_ERROR, TYPE_WARNING, TYPE_INFO)
@Retention(AnnotationRetention.SOURCE)
annotation class DialogType

companion object {
    const val TYPE_SUCCESS = "success"
    const val TYPE_ERROR = "error"
    const val TYPE_WARNING = "warning"
    const val TYPE_INFO = "info"
}

Refactoring Recommendations

Priority 1: Critical Refactoring (Do Now)

Task Impact Files Affected
Break up God Classes High 5 files (2,500+ LOC → 500 LOC each)
Fix Exception Handling Critical 100+ files
Remove Deprecated AsyncTask Medium 10+ files
Delete Dead Code Low 15 files (2,000 LOC)

Week 1:
- Refactor CalendarCustomView.java (2,963 → 500 LOC)
- Extract CalendarViewModel
- Extract CalendarRepository
- Extract DateUtils

Week 2:
- Refactor WeeklyScheduleFragment.java (2,824 → 500 LOC)
- Extract ScheduleViewModel
- Extract SlotManager
- Extract ValidationUtils

Week 3:
- Fix exception handling in critical paths
- Payment processing
- Patient data operations
- Authentication flows

Week 4:
- Remove deprecated code
- Delete legacy WebRTC files
- Remove commented code
- Delete unused classes


Priority 2: High Priority (Do Next)

Task Impact
Consolidate Permission Classes Medium
Migrate to ViewBinding Medium
Remove Static State High
Add Proper Error Logging High
Implement Retry Logic Medium

Priority 3: Long-term Improvements (Plan)

Task Impact
Full Kotlin Migration High
Migrate to Jetpack Compose Medium
Implement MVVM Throughout High
Add Unit Tests High
Setup CI/CD with Quality Gates Medium

Static Analysis Tools

# SonarQube
./gradlew sonarqube

# Android Lint
./gradlew lint

# Detekt (Kotlin static analysis)
./gradlew detekt

# Checkstyle
./gradlew checkstyle

Quality Gates (CI/CD)

# GitHub Actions / Azure Pipelines
quality_gates:
  - code_coverage: >= 60%
  - duplicated_code: < 3%
  - critical_issues: 0
  - high_issues: < 5
  - code_smells: < 50
  - technical_debt_ratio: < 10%

Metrics to Track

Metric Current Target Deadline
Lines of Code ~150,000 ~120,000 3 months
Files >500 LOC 20+ <5 2 months
Code Coverage ~10% 60% 6 months
Code Duplication ~30% <5% 3 months
Cyclomatic Complexity High Medium 3 months
Technical Debt Ratio ~20% <5% 6 months

Conclusion

The AndroidCareProvider codebase suffers from significant technical debt accumulated over time. The primary issues are:

  1. God Classes - Files exceeding 2,000 LOC need immediate refactoring
  2. Poor Error Handling - 100+ generic exception catches hiding bugs
  3. Code Duplication - 30%+ duplicated code (permission classes, API calls)
  4. Static Mutable State - 15+ static variables causing memory leaks and race conditions
  5. Deprecated APIs - AsyncTask, old Firebase services still in use

Immediate Actions (This Sprint):

Week 1: Fix critical exception handling in payment & auth
Week 2: Refactor CalendarCustomView.java (extract ViewModel + Repository)
Week 3: Delete dead code (legacy WebRTC, commented services)
Week 4: Consolidate permission classes into single manager


Document Status: Complete
Next Steps: UX Review
Maintained By: Engineering Team
Version: 1.0