diff --git a/app/build.gradle b/app/build.gradle index 11157863ac..395e3e7e5f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,4 +45,5 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' + implementation 'com.google.android.material:material:1.0.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea793b5a59..6189c7579b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,8 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - + + diff --git a/app/src/main/java/com/navi/medici/android_customer_app/api/RetrofitService.kt b/app/src/main/java/com/navi/medici/android_customer_app/api/RetrofitService.kt index 25c4e9a252..2fe747335e 100644 --- a/app/src/main/java/com/navi/medici/android_customer_app/api/RetrofitService.kt +++ b/app/src/main/java/com/navi/medici/android_customer_app/api/RetrofitService.kt @@ -2,9 +2,16 @@ package com.navi.medici.android_customer_app.api import retrofit2.Converter import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +private const val BASE_URL = "https://jsonplaceholder.typicode.com/" +private val gsonConverterFactory = GsonConverterFactory.create() object RetrofitService { - fun build(baseURL: String, converterFactory: Converter.Factory): Retrofit { + fun build( + baseURL: String = BASE_URL, + converterFactory: Converter.Factory = gsonConverterFactory + ): Retrofit { val retrofitBuilder = Retrofit.Builder().baseUrl(baseURL) return retrofitBuilder.addConverterFactory(converterFactory).build() } diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/BottomNavigationActivity.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/BottomNavigationActivity.kt new file mode 100644 index 0000000000..da76efc6a9 --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/BottomNavigationActivity.kt @@ -0,0 +1,39 @@ +package com.navi.medici.android_customer_app.bottomNavigation + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.databinding.DataBindingUtil +import com.navi.medici.android_customer_app.R +import com.navi.medici.android_customer_app.bottomNavigation.myLoans.MyLoansFragment +import com.navi.medici.android_customer_app.databinding.ActivityBottomNavigationBinding + +class BottomNavigationActivity : AppCompatActivity() { + + private lateinit var binding: ActivityBottomNavigationBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = DataBindingUtil.setContentView(this, R.layout.activity_bottom_navigation) + + binding.bottomNavigationView.setOnNavigationItemSelectedListener { + when (it.itemId) { + R.id.my_loans -> navigateToMyLoans() + else -> false + } + } + binding.bottomNavigationView.selectedItemId = R.id.my_loans + } + + private fun navigateToMyLoans(): Boolean { + val fragmentManager = supportFragmentManager.beginTransaction() + supportFragmentManager.findFragmentByTag(MyLoansFragment.TAG)?.let { fragment -> + fragmentManager.replace(R.id.content_view, fragment, MyLoansFragment.TAG) + } ?: run { + val fragment = MyLoansFragment() + fragmentManager.add(R.id.content_view, fragment, MyLoansFragment.TAG) + fragmentManager.replace(R.id.content_view, fragment, MyLoansFragment.TAG) + } + fragmentManager.commit() + return true + } +} diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/Loan.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/Loan.kt new file mode 100644 index 0000000000..f228064f4f --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/Loan.kt @@ -0,0 +1,12 @@ +package com.navi.medici.android_customer_app.bottomNavigation.myLoans + +data class Loan( + var status: LoanStatus?, + var emiDue: Long?, + var amount: Long?, + var dueDate: String?, + var type: LoanType?, + var startDate: String?, + var interestRate: Float?, + var completionDate: String? +) diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/LoanStatus.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/LoanStatus.kt new file mode 100644 index 0000000000..5b45bb6ae8 --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/LoanStatus.kt @@ -0,0 +1,6 @@ +package com.navi.medici.android_customer_app.bottomNavigation.myLoans + +enum class LoanStatus(val value: String) { + ACTIVE("Active"), + COMPLETED("Closed") +} diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/LoanType.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/LoanType.kt new file mode 100644 index 0000000000..bf0d95fe10 --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/LoanType.kt @@ -0,0 +1,6 @@ +package com.navi.medici.android_customer_app.bottomNavigation.myLoans + +enum class LoanType(val value: String) { + COUNTDOWN("Countdown Loan"), + PERSONAL("Personal Loan") +} diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansAdapter.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansAdapter.kt new file mode 100644 index 0000000000..45537b7804 --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansAdapter.kt @@ -0,0 +1,56 @@ +package com.navi.medici.android_customer_app.bottomNavigation.myLoans + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.navi.medici.android_customer_app.R +import com.navi.medici.android_customer_app.databinding.LoanCardBinding + +class MyLoansAdapter(private val context: Context) : + RecyclerView.Adapter() { + private val loans = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyLoansViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val view = LoanCardBinding.inflate(layoutInflater, parent, false) + return MyLoansViewHolder(view) + } + + fun setLoans(loans: List) { + this.loans.clear() + this.loans.addAll(loans) + notifyDataSetChanged() + } + + override fun getItemCount() = loans.count() + + override fun onBindViewHolder(holder: MyLoansViewHolder, position: Int) { + val loan = loans[position] + holder.binding.apply { + loanAmountText.text = + context.getString(R.string.rupees_format, loan.amount) + loanTypeText.text = loan.type?.value + startDateText.text = loan.startDate + interestRateText.text = + context.getString(R.string.percentage_format, loan.interestRate) + if (loan.status == LoanStatus.ACTIVE) { + emiDueOrCompletionDateText.text = + context.getString(R.string.rupees_format, loan.emiDue) + emiDueOrCompletionDateLabelText.text = + context.getString(R.string.emi_due) + dueDateOrStatusLabelText.text = context.getString(R.string.due_date) + dueDateOrStatusText.text = loan.dueDate + } else { + emiDueOrCompletionDateText.text = loan.completionDate + emiDueOrCompletionDateLabelText.text = + context.getString(R.string.completion_date) + dueDateOrStatusLabelText.text = context.getString(R.string.status) + dueDateOrStatusText.text = loan.status?.value + } + } + } + + class MyLoansViewHolder(val binding: LoanCardBinding) : RecyclerView.ViewHolder(binding.root) { + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansApi.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansApi.kt new file mode 100644 index 0000000000..378a81637e --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansApi.kt @@ -0,0 +1,16 @@ +package com.navi.medici.android_customer_app.bottomNavigation.myLoans + +import com.navi.medici.android_customer_app.api.RetrofitService +import retrofit2.Response +import retrofit2.http.GET + +interface MyLoansApi { + @GET("posts") + suspend fun fetchMyLoans(): Response> + + companion object { + operator fun invoke(): MyLoansApi { + return RetrofitService.build().create(MyLoansApi::class.java) + } + } +} diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansFragment.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansFragment.kt new file mode 100644 index 0000000000..fd0b60c21d --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansFragment.kt @@ -0,0 +1,60 @@ +package com.navi.medici.android_customer_app.bottomNavigation.myLoans + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.LinearLayoutManager +import com.navi.medici.android_customer_app.R +import com.navi.medici.android_customer_app.databinding.MyLoansFragmentBinding + + +class MyLoansFragment : Fragment() { + private lateinit var viewModel: MyLoansViewModel + private lateinit var binding: MyLoansFragmentBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DataBindingUtil.inflate( + inflater, + R.layout.my_loans_fragment, + container, + false + ) + viewModel = ViewModelProviders.of(this).get(MyLoansViewModel::class.java) + context?.let { + val myActiveLoansAdapter = MyLoansAdapter(it) + binding.activeLoansRecycler.layoutManager = LinearLayoutManager(it) + binding.activeLoansRecycler.adapter = myActiveLoansAdapter + + val myCompletedLoansAdapter = MyLoansAdapter(it) + binding.completedLoansRecycler.layoutManager = LinearLayoutManager(it) + binding.completedLoansRecycler.adapter = myCompletedLoansAdapter + + viewModel.myLoans.observe(this, Observer { loans -> + val myActiveLoans = loans.filter { loan -> + loan.status == LoanStatus.ACTIVE + } + myActiveLoansAdapter.setLoans(myActiveLoans) + + val myCompletedLoans = loans.filter { loan -> + loan.status == LoanStatus.COMPLETED + } + myCompletedLoansAdapter.setLoans(myCompletedLoans) + }) + + viewModel.fetchMyLoans() + } + return binding.root + } + + companion object { + const val TAG = "MY_LOANS_FRAGMENT" + } +} diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansRepository.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansRepository.kt new file mode 100644 index 0000000000..afbf3407a6 --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansRepository.kt @@ -0,0 +1,7 @@ +package com.navi.medici.android_customer_app.bottomNavigation.myLoans + +import com.navi.medici.android_customer_app.api.SafeApiRequest + +class MyLoansRepository(private val myLoansApi: MyLoansApi) : SafeApiRequest() { + suspend fun fetchMyLoans() = apiRequest { myLoansApi.fetchMyLoans() } +} diff --git a/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansViewModel.kt b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansViewModel.kt new file mode 100644 index 0000000000..53079a00b7 --- /dev/null +++ b/app/src/main/java/com/navi/medici/android_customer_app/bottomNavigation/myLoans/MyLoansViewModel.kt @@ -0,0 +1,40 @@ +package com.navi.medici.android_customer_app.bottomNavigation.myLoans + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class MyLoansViewModel : ViewModel() { + private val myLoansRepository = MyLoansRepository(MyLoansApi()) + private val coroutineScope = CoroutineScope(Dispatchers.Main) + val myLoans = MutableLiveData>() + + fun fetchMyLoans() { + coroutineScope.launch { + val response = myLoansRepository.fetchMyLoans() + response.forEachIndexed { index, loan -> + loan.apply { + if (index % 2 == 0) { + amount = 25000 + dueDate = "24th December" + interestRate = 12.5f + startDate = "24th September" + emiDue = 2500 + status = LoanStatus.ACTIVE + type = LoanType.COUNTDOWN + } else { + amount = 50000 + interestRate = 16.7f + startDate = "24th August" + completionDate = "21st February" + status = LoanStatus.COMPLETED + type = LoanType.PERSONAL + } + } + } + myLoans.value = response.subList(0,7) + } + } +} diff --git a/app/src/main/java/com/navi/medici/android_customer_app/login/LoginActivity.kt b/app/src/main/java/com/navi/medici/android_customer_app/login/LoginActivity.kt index 3dcf777b21..9e9bf6f05f 100644 --- a/app/src/main/java/com/navi/medici/android_customer_app/login/LoginActivity.kt +++ b/app/src/main/java/com/navi/medici/android_customer_app/login/LoginActivity.kt @@ -1,11 +1,14 @@ package com.navi.medici.android_customer_app.login +import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.core.widget.addTextChangedListener import androidx.databinding.DataBindingUtil +import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import com.navi.medici.android_customer_app.R +import com.navi.medici.android_customer_app.bottomNavigation.BottomNavigationActivity import com.navi.medici.android_customer_app.databinding.ActivityLoginBinding class LoginActivity : AppCompatActivity() { @@ -16,7 +19,12 @@ class LoginActivity : AppCompatActivity() { super.onCreate(savedInstanceState) loginViewModel = ViewModelProviders.of(this).get(LoginViewModel::class.java) binding = DataBindingUtil.setContentView(this, R.layout.activity_login) - + loginViewModel.isLoggedIn.observe(this, Observer { isLoggedIn -> + if (isLoggedIn) { + val intent = Intent(this, BottomNavigationActivity::class.java) + startActivity(intent) + } + }) binding.phoneEdit.addTextChangedListener { loginViewModel.onChangePhoneNumber(it.toString()) } } diff --git a/app/src/main/java/com/navi/medici/android_customer_app/login/LoginApi.kt b/app/src/main/java/com/navi/medici/android_customer_app/login/LoginApi.kt index 4b1177ad2a..f4035ab160 100644 --- a/app/src/main/java/com/navi/medici/android_customer_app/login/LoginApi.kt +++ b/app/src/main/java/com/navi/medici/android_customer_app/login/LoginApi.kt @@ -2,20 +2,15 @@ package com.navi.medici.android_customer_app.login import com.navi.medici.android_customer_app.api.RetrofitService import retrofit2.Response -import retrofit2.converter.gson.GsonConverterFactory import retrofit2.http.GET -private const val BASE_URL = "https://jsonplaceholder.typicode.com/" -private val gsonConverterFactory = GsonConverterFactory.create() - interface LoginApi { @GET("posts/1") suspend fun submitPhoneNumber(): Response companion object { operator fun invoke(): LoginApi { - return RetrofitService.build(BASE_URL, gsonConverterFactory) - .create(LoginApi::class.java) + return RetrofitService.build().create(LoginApi::class.java) } } } \ No newline at end of file diff --git a/app/src/main/java/com/navi/medici/android_customer_app/login/LoginViewModel.kt b/app/src/main/java/com/navi/medici/android_customer_app/login/LoginViewModel.kt index 0a9cb9377a..6fd7e82545 100644 --- a/app/src/main/java/com/navi/medici/android_customer_app/login/LoginViewModel.kt +++ b/app/src/main/java/com/navi/medici/android_customer_app/login/LoginViewModel.kt @@ -1,11 +1,16 @@ package com.navi.medici.android_customer_app.login +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch class LoginViewModel : ViewModel() { + private val _isLoggedIn = MutableLiveData(false) + val isLoggedIn: LiveData + get() = _isLoggedIn private val login = Login() private val loginRepository = LoginRepository(LoginApi()) private val coroutineScope = CoroutineScope(Dispatchers.Main) @@ -19,6 +24,10 @@ class LoginViewModel : ViewModel() { private fun submitPhoneNumber() { coroutineScope.launch { + val response = loginRepository.submitPhoneNumber(login.phoneNumber) + if (response.id != 0) { + _isLoggedIn.value = true + } loginRepository.submitPhoneNumber(login.phoneNumber) } } diff --git a/app/src/main/res/layout/activity_bottom_navigation.xml b/app/src/main/res/layout/activity_bottom_navigation.xml new file mode 100644 index 0000000000..a7b0801b99 --- /dev/null +++ b/app/src/main/res/layout/activity_bottom_navigation.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/loan_card.xml b/app/src/main/res/layout/loan_card.xml new file mode 100644 index 0000000000..a3eb979da5 --- /dev/null +++ b/app/src/main/res/layout/loan_card.xml @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/my_loans_fragment.xml b/app/src/main/res/layout/my_loans_fragment.xml new file mode 100644 index 0000000000..cd94392c2e --- /dev/null +++ b/app/src/main/res/layout/my_loans_fragment.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_navigation_menu.xml b/app/src/main/res/menu/bottom_navigation_menu.xml new file mode 100644 index 0000000000..32fbbf6311 --- /dev/null +++ b/app/src/main/res/menu/bottom_navigation_menu.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b3ad6ebc01..6ba801fb7e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,4 +4,19 @@ Enter your mobile number to register with Navi Create New Account + Home + My Loans + Profile + Active Loans + Completed Loans + EMI Due : + Rs %d + Loan Amount : + Due Date + Loan Type + Start Date + Interest Rate + %.1f %% + Completion Date + Status