Skip to content

Commit

Permalink
refactor: Home Module (#2651)
Browse files Browse the repository at this point in the history
Co-authored-by: Rajan Maurya <[email protected]>
  • Loading branch information
akashmeruva9 and therajanmaurya committed Jul 10, 2024
1 parent 0dfe892 commit c3f9985
Show file tree
Hide file tree
Showing 27 changed files with 467 additions and 42 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ dependencies {
implementation(projects.feature.about)
implementation(projects.feature.settings)
implementation(projects.feature.updatePassword)
implementation(projects.feature.home)
implementation(projects.feature.auth)
implementation(projects.feature.userProfile)


implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation(libs.androidx.lifecycle.ktx)
implementation(libs.androidx.lifecycle.extensions)
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/org/mifos/mobile/ui/home/HomeOldFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ import org.mifos.mobile.ui.savings_account.SavingsMakeTransferComposeFragment
import org.mifos.mobile.ui.third_party_transfer.ThirdPartyTransferComposeFragment
import org.mifos.mobile.ui.user_profile.UserProfileActivity
import org.mifos.mobile.core.datastore.PreferencesHelper
import org.mifos.mobile.feature.home.screens.HomeScreen
import org.mifos.mobile.feature.home.viewmodel.HomeCardItem
import org.mifos.mobile.feature.home.viewmodel.HomeViewModel
import org.mifos.mobile.utils.MaterialDialog
import org.mifos.mobile.utils.Toaster
import javax.inject.Inject
Expand Down
33 changes: 17 additions & 16 deletions app/src/test/java/org/mifos/mobile/viewModels/HomeViewModelTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ package org.mifos.mobile.viewModels

import CoroutineTestRule
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import io.reactivex.Observer
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
Expand All @@ -20,9 +18,9 @@ import org.mifos.mobile.models.accounts.savings.SavingAccount
import org.mifos.mobile.models.client.Client
import org.mifos.mobile.models.client.ClientAccounts
import org.mifos.mobile.repositories.HomeRepositoryImp
import org.mifos.mobile.ui.home.HomeState
import org.mifos.mobile.ui.home.HomeUiState
import org.mifos.mobile.ui.home.HomeViewModel
import org.mifos.mobile.feature.home.viewmodels.HomeState
import org.mifos.mobile.feature.home.viewmodels.HomeUiState
import org.mifos.mobile.feature.home.viewmodels.HomeViewModel
import org.mifos.mobile.util.RxSchedulersOverrideRule
import org.mockito.Mock
import org.mockito.Mockito.`when`
Expand Down Expand Up @@ -51,12 +49,12 @@ class HomeViewModelTest {
private lateinit var mockPreferencesHelper: PreferencesHelper


private lateinit var viewModel: HomeViewModel
private lateinit var viewModel: org.mifos.mobile.feature.home.viewmodels.HomeViewModel

@Before
fun setUp() {
MockitoAnnotations.openMocks(this)
viewModel = HomeViewModel(homeRepositoryImp)
viewModel = org.mifos.mobile.feature.home.viewmodels.HomeViewModel(homeRepositoryImp)
viewModel.preferencesHelper = mockPreferencesHelper
}

Expand All @@ -81,9 +79,12 @@ class HomeViewModelTest {
viewModel.loadClientAccountDetails()

assertEquals(viewModel.homeUiState.value,
HomeUiState.Success(
HomeState( loanAmount = expectedLoanBalance,
savingsAmount = expectedSavingBalance))
org.mifos.mobile.feature.home.viewmodels.HomeUiState.Success(
org.mifos.mobile.feature.home.viewmodels.HomeState(
loanAmount = expectedLoanBalance,
savingsAmount = expectedSavingBalance
)
)
)

}
Expand All @@ -96,8 +97,8 @@ class HomeViewModelTest {

viewModel.loadClientAccountDetails()

assertTrue(viewModel.homeUiState.value is HomeUiState.Error)
assertEquals(errorMessageResId, (viewModel.homeUiState.value as HomeUiState.Error).errorMessage)
assertTrue(viewModel.homeUiState.value is org.mifos.mobile.feature.home.viewmodels.HomeUiState.Error)
assertEquals(errorMessageResId, (viewModel.homeUiState.value as org.mifos.mobile.feature.home.viewmodels.HomeUiState.Error).errorMessage)
}

@Test
Expand All @@ -107,9 +108,9 @@ class HomeViewModelTest {
`when`(homeRepositoryImp.currentClient()).thenReturn(flowOf(mockClient))

viewModel.getUserDetails()
assertTrue(viewModel.homeUiState.value is HomeUiState.Success)
assertTrue(viewModel.homeUiState.value is org.mifos.mobile.feature.home.viewmodels.HomeUiState.Success)
assertEquals(mockClient.officeName,
(viewModel.homeUiState.value as HomeUiState.Success).homeState.username)
(viewModel.homeUiState.value as org.mifos.mobile.feature.home.viewmodels.HomeUiState.Success).homeState.username)
}

@Test(expected = Exception::class)
Expand All @@ -119,8 +120,8 @@ class HomeViewModelTest {
`when`(homeRepositoryImp.currentClient()).thenThrow( Exception())

viewModel.getUserDetails()
assertTrue(viewModel.homeUiState.value is HomeUiState.Error)
assertEquals(errorMessageResId,HomeUiState.Error(R.string.error_fetching_client))
assertTrue(viewModel.homeUiState.value is org.mifos.mobile.feature.home.viewmodels.HomeUiState.Error)
assertEquals(errorMessageResId, org.mifos.mobile.feature.home.viewmodels.HomeUiState.Error(R.string.error_fetching_client))

}

Expand Down
1 change: 1 addition & 0 deletions feature/home/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
25 changes: 25 additions & 0 deletions feature/home/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
alias(libs.plugins.mifos.android.feature)
alias(libs.plugins.mifos.android.library.compose)
}

android {
namespace = "org.mifos.mobile.feature.home"
}

dependencies {
implementation(projects.ui)
implementation(projects.core.common)
implementation(projects.core.model)
implementation(projects.core.data)

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.espresso.core)
implementation(libs.squareup.okhttp)
api(libs.androidx.compose.ui.util)
implementation(libs.androidx.camera.camera2)
implementation(libs.androidx.camera.lifecycle)
implementation(libs.androidx.camera.view)
implementation(libs.androidx.camera.core)
}
Empty file added feature/home/consumer-rules.pro
Empty file.
21 changes: 21 additions & 0 deletions feature/home/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.mifos.mobile.feature.home

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("org.mifos.mobile.feature.home.test", appContext.packageName)
}
}
4 changes: 4 additions & 0 deletions feature/home/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.mifos.mobile.ui.home
package org.mifos.mobile.feature.home.screens

import android.graphics.Bitmap
import androidx.compose.foundation.clickable
Expand Down Expand Up @@ -34,12 +34,13 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.mifos.mobile.R
import org.mifos.mobile.core.ui.component.MifosHiddenTextRow
import org.mifos.mobile.core.ui.component.MifosLinkText
import org.mifos.mobile.core.ui.component.MifosUserImage
import org.mifos.mobile.core.ui.theme.MifosMobileTheme
import org.mifos.mobile.core.common.utils.CurrencyUtil
import org.mifos.mobile.feature.home.R
import org.mifos.mobile.feature.home.viewmodel.HomeCardItem

@Composable
fun HomeContent(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package org.mifos.mobile.ui.home
package org.mifos.mobile.feature.home.screens

import android.graphics.Bitmap
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import org.mifos.mobile.R
import org.mifos.mobile.core.ui.component.EmptyDataView
import org.mifos.mobile.core.ui.component.MifosProgressIndicator
import org.mifos.mobile.core.ui.theme.MifosMobileTheme

import org.mifos.mobile.feature.home.R
import org.mifos.mobile.feature.home.utils.HomeState
import org.mifos.mobile.feature.home.utils.HomeUiState
import org.mifos.mobile.feature.home.viewmodel.HomeCardItem

@Composable
fun HomeScreen(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.mifos.mobile.feature.home.utils

import android.graphics.Bitmap

sealed class HomeUiState {
data object Loading : HomeUiState()
data class Error(val errorMessage: Int) : HomeUiState()
data class Success(val homeState: HomeState) : HomeUiState()
}

data class HomeState(
val username: String? = "",
val image: Bitmap? = null,
val loanAmount: Double = 0.0,
val savingsAmount: Double = 0.0
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package org.mifos.mobile.feature.home.utils

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import android.util.Log

/**
* Created by dilpreet on 10/8/17.
*/
class ImageUtil {
// Default width and height
fun compressImage(decodedBytes: ByteArray): Bitmap? {
return compress(decodedBytes, 816.0f, 612.0f)
}

fun compressImage(decodedBytes: ByteArray, maxHeight: Float, maxWidth: Float): Bitmap? {
return compress(decodedBytes, maxHeight, maxWidth)
}

private fun compress(decodedBytes: ByteArray, maxHeight: Float, maxWidth: Float): Bitmap? {
var scaledBitmap: Bitmap? = null
val options = BitmapFactory.Options()

// by setting this field as true, the actual bitmap pixels are not loaded in the memory.
// Just the bounds are loaded. If
// you try the use the bitmap here, you will get null.
options.inJustDecodeBounds = true
var bmp = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options)
var actualHeight = options.outHeight
var actualWidth = options.outWidth
var imgRatio = actualWidth / actualHeight.toFloat()
val maxRatio = maxWidth / maxHeight

// width and height values are set maintaining the aspect ratio of the image
if (actualHeight > maxHeight || actualWidth > maxWidth) {
if (imgRatio < maxRatio) {
imgRatio = maxHeight / actualHeight
actualWidth = (imgRatio * actualWidth).toInt()
actualHeight = maxHeight.toInt()
} else if (imgRatio > maxRatio) {
imgRatio = maxWidth / actualWidth
actualHeight = (imgRatio * actualHeight).toInt()
actualWidth = maxWidth.toInt()
} else {
actualHeight = maxHeight.toInt()
actualWidth = maxWidth.toInt()
}
}

// setting inSampleSize value allows to load a scaled down version of the original image
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight)

// inJustDecodeBounds set to false to load the actual bitmap
options.inJustDecodeBounds = false

// this options allow android to claim the bitmap memory if it runs low on memory
options.inPurgeable = true
options.inInputShareable = true
options.inTempStorage = ByteArray(16 * 1024)
try {
bmp = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options)
} catch (exception: OutOfMemoryError) {
Log.e(ImageUtil::class.java.name, exception.toString())
}
try {
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888)
} catch (exception: OutOfMemoryError) {
Log.e(ImageUtil::class.java.name, exception.toString())
}
val ratioX = actualWidth / options.outWidth.toFloat()
val ratioY = actualHeight / options.outHeight.toFloat()
val middleX = actualWidth / 2.0f
val middleY = actualHeight / 2.0f
val scaleMatrix = Matrix()
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY)
val canvas = Canvas(scaledBitmap!!)
canvas.setMatrix(scaleMatrix)
canvas.drawBitmap(
bmp,
middleX - bmp.width / 2,
middleY - bmp.height / 2,
Paint(Paint.FILTER_BITMAP_FLAG),
)
scaledBitmap = Bitmap.createBitmap(
scaledBitmap,
0,
0,
scaledBitmap.width,
scaledBitmap.height,
null,
true,
)
return scaledBitmap
}

private fun calculateInSampleSize(
options: BitmapFactory.Options,
reqWidth: Int,
reqHeight: Int,
): Int {
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val heightRatio = Math.round(height.toFloat() / reqHeight.toFloat())
val widthRatio = Math.round(width.toFloat() / reqWidth.toFloat())
inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
}
val totalPixels = width * height.toFloat()
val totalReqPixelsCap = reqWidth * reqHeight * 2.toFloat()
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++
}
return inSampleSize
}

companion object {
/**
* Reference : https://developer.android.com/topic/performance/graphics/load-bitmap.html
* And for scaling :
* https://stackoverflow.com/questions/8722359/scale-rotate-bitmap-using-matrix-in-android/8722592#8722592
*/
@JvmStatic
var instance: ImageUtil? = null
get() {
if (field == null) {
field = ImageUtil()
}
return field
}
private set
}
}
Loading

0 comments on commit c3f9985

Please sign in to comment.