diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index f72638dd..dbd773bd 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,30 +1,30 @@ -**Summary:** +## Summary -Summarise your issue in one sentence (what goes wrong, what did you expect to happen). + -**Steps to reproduce:** +## Steps to reproduce -How can we reproduce the issue? + -**Expected behaviour:** +## Expected behaviour -What did you expect the app to do? + -**Observed behaviour:** +## Observed behaviour -What did you see instead? Describe your issue in detail here. + -**Logcat for the issue** +## Logcat for the issue -Provide logs for the crash here + -**Device and Android version:** +## Device and Android version -What make and model device (e.g., Samsung Galaxy S3) did you encounter this on? What Android + -**Screenshots:** +## Screenshots -Can be created by pressing the Volume Down and Power Button at the same time on Android 4.0 and higher. \ No newline at end of file + \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 64c4aa74..53f416ed 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,14 @@ +## Issue Fix Fixes #Issue_Number -Please Add Screenshots If there are any UI changes. +## Screenshots + -Please make sure these boxes are checked before submitting your pull request - thanks! +## Description + + +## + - [ ] Run the unit tests with `./gradlew check` to make sure you didn't break anything diff --git a/app/build.gradle b/app/build.gradle index 3245137d..3d13b221 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,6 +13,7 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" + multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } @@ -130,6 +131,9 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.8.0' kapt 'com.github.bumptech.glide:compiler:4.8.0' + implementation 'me.dm7.barcodescanner:zxing:1.9.13' + implementation 'com.karumi:dexter:5.0.0' + //dbFlow for database kapt "com.github.Raizlabs.DBFlow:dbflow-processor:$rootProject.dbFlowVersion" implementation "com.github.Raizlabs.DBFlow:dbflow-core:$rootProject.dbFlowVersion" @@ -137,7 +141,7 @@ dependencies { implementation "com.github.Raizlabs.DBFlow:dbflow-kotlinextensions:$rootProject.dbFlowVersion" implementation "com.github.Raizlabs.DBFlow:dbflow-rx2:$rootProject.dbFlowVersion" implementation "com.github.Raizlabs.DBFlow:dbflow-rx2-kotlinextensions:$rootProject.dbFlowVersion" - + implementation 'com.android.support:multidex:1.0.3' //OpenSource Licences implementation "com.google.android.gms:play-services-oss-licenses:17.0.0" @@ -167,32 +171,32 @@ dependencies { testImplementation 'org.robolectric:robolectric:3.3.1' } - // Log out test results to console - tasks.matching { it instanceof Test }.all { - testLogging.events = ["failed", "passed", "skipped"] - } +// Log out test results to console +tasks.matching { it instanceof Test }.all { + testLogging.events = ["failed", "passed", "skipped"] +} - /* +/* Resolves dependency versions across test and production APKs, specifically, transitive dependencies. This is required since Espresso internally has a dependency on support-annotations. */ - configurations.all { - resolutionStrategy { - force 'com.android.support:support-annotations:27.1.1' - force 'com.google.code.findbugs:jsr305:1.3.9' - } +configurations.all { + resolutionStrategy { + force 'com.android.support:support-annotations:27.1.1' + force 'com.google.code.findbugs:jsr305:1.3.9' } +} - /* +/* All direct/transitive dependencies shared between your test and production APKs need to be excluded from the test APK! This is necessary because both APKs will contain the same classes. Not excluding these dependencies from your test configuration will result in an dex pre-verifier error at runtime. More info in this tools bug: (https://code.google.com/p/android/issues/detail?id=192497) */ - configurations.compile.dependencies.each { compileDependency -> +configurations.compile.dependencies.each { compileDependency -> println "Excluding compile dependency: ${compileDependency.getName()}" configurations.androidTestCompile.dependencies.each { androidTestCompileDependency -> configurations.androidTestCompile.exclude module: "${compileDependency.getName()}" } - } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index db3b07b5..bae3ac22 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,31 +1,38 @@ + package="org.mifos.mobile.cn"> - - + + + + + + - + - + - + - - - + + @@ -36,7 +43,13 @@ - + + + + + + diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/data/models/beneficiaries/listBeneficiaries.kt b/app/src/main/kotlin/org/mifos/mobile/cn/data/models/beneficiaries/listBeneficiaries.kt new file mode 100644 index 00000000..49e9aade --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/data/models/beneficiaries/listBeneficiaries.kt @@ -0,0 +1,3 @@ +package org.mifos.mobile.cn.data.models.beneficiaries + +data class Beneficiary(val name: String, val description: String, val price: String) \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/injection/component/ActivityComponent.kt b/app/src/main/kotlin/org/mifos/mobile/cn/injection/component/ActivityComponent.kt index 13cbf11c..b67631ae 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/injection/component/ActivityComponent.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/injection/component/ActivityComponent.kt @@ -4,6 +4,7 @@ package org.mifos.mobile.cn.injection.component import dagger.Subcomponent import org.mifos.mobile.cn.injection.PerActivity import org.mifos.mobile.cn.injection.module.ActivityModule +import org.mifos.mobile.cn.ui.mifos.Account import org.mifos.mobile.cn.ui.mifos.DashboardActivity import org.mifos.mobile.cn.ui.mifos.aboutus.AboutUsFragment import org.mifos.mobile.cn.ui.mifos.accounts.AccountsFragment @@ -39,6 +40,8 @@ interface ActivityComponent { fun inject(passcodeActivity: PasscodeActivity) + fun inject(account :Account) + fun inject(launcherActivity: LauncherActivity) fun inject(dashboardActivity: DashboardActivity) diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/adapter/BeneficiariesAdapter.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/adapter/BeneficiariesAdapter.kt new file mode 100644 index 00000000..61c64a50 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/adapter/BeneficiariesAdapter.kt @@ -0,0 +1,35 @@ +package org.mifos.mobile.cn.ui.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.data.models.beneficiaries.Beneficiary + + +class BeneficiariesAdapter(val beneficiariesList: ArrayList) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val v = LayoutInflater.from(parent.context).inflate(R.layout.item_beneficiaries, parent, false) + return ViewHolder(v) + } + + override fun getItemCount(): Int { + return beneficiariesList.size + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val list: Beneficiary = beneficiariesList[position] + holder.textViewName?.text = list.name + holder.textViewDescription?.text = list.description + holder.textViewPrice?.text = list.price + } + + class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val textViewName = itemView.findViewById(R.id.tv_name) + val textViewDescription = itemView.findViewById(R.id.tv_beneficiaries_decription) + val textViewPrice = itemView.findViewById(R.id.tv_price) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/adapter/PlannedPaymentAdapter.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/adapter/PlannedPaymentAdapter.kt index 1a4352d2..bf23561a 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/adapter/PlannedPaymentAdapter.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/adapter/PlannedPaymentAdapter.kt @@ -10,7 +10,7 @@ import android.widget.TextView import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat import kotlinx.android.synthetic.main.item_header_planned_payment.view.* -import kotlinx.android.synthetic.main.item_panned_payment.view.* +import kotlinx.android.synthetic.main.item_planned_payment.view.* import org.mifos.mobile.cn.R import org.mifos.mobile.cn.data.models.payment.CostComponent import org.mifos.mobile.cn.data.models.payment.PlannedPayment @@ -100,7 +100,7 @@ class PlannedPaymentAdapter @Inject constructor(@ApplicationContext var context: override fun onCreateItemViewHolder(parent: ViewGroup?, itemType: Int): ItemViewHolder { val inflater = LayoutInflater.from(parent!!.context) - val v = inflater.inflate(R.layout.item_panned_payment, parent, false) + val v = inflater.inflate(R.layout.item_planned_payment, parent, false) return ItemViewHolder(v) } diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/base/MifosBaseFragment.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/base/MifosBaseFragment.kt index ff78b31b..c47fb543 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/base/MifosBaseFragment.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/base/MifosBaseFragment.kt @@ -63,7 +63,7 @@ open class MifosBaseFragment : Fragment() { callback.showTabLayout(false) } - override fun onAttach(context: Context?) { + override fun onAttach(context: Context?) { super.onAttach(context) val activity = context as? Activity try { diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Account.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Account.kt new file mode 100644 index 00000000..f5395e73 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Account.kt @@ -0,0 +1,292 @@ +package org.mifos.mobile.cn.ui.mifos + +import android.app.SearchManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.viewpager.widget.ViewPager +import androidx.appcompat.widget.SearchView +import android.view.* +import android.widget.Toast +import kotlinx.android.synthetic.main.fragment_client_accounts.* +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.data.models.accounts.deposit.DepositAccount +import org.mifos.mobile.cn.data.models.accounts.loan.LoanAccount +import org.mifos.mobile.cn.enums.AccountType +import org.mifos.mobile.cn.ui.adapter.ViewPagerAdapter +import org.mifos.mobile.cn.ui.base.MifosBaseActivity +import org.mifos.mobile.cn.ui.base.MifosBaseFragment +import org.mifos.mobile.cn.ui.mifos.accounts.AccountsContract +import org.mifos.mobile.cn.ui.mifos.accounts.AccountsFragment +import org.mifos.mobile.cn.ui.mifos.accounts.AccountsPresenter +import org.mifos.mobile.cn.ui.mifos.accountsFilter.AccountsFilterBottomSheet +import org.mifos.mobile.cn.ui.mifos.customerAccounts.CustomerAccountFragment +import org.mifos.mobile.cn.ui.mifos.loanApplication.loanActivity.LoanApplicationActivity +import org.mifos.mobile.cn.ui.utils.ConstantKeys +import org.mifos.mobile.cn.ui.utils.StatusUtils +import javax.inject.Inject + +class Account : MifosBaseFragment() , AccountsContract.View { + private lateinit var accountType: AccountType + + + @Inject + internal lateinit var accountsPresenter: AccountsPresenter + + companion object { + fun newInstance(accountType: AccountType): CustomerAccountFragment { + val fragment = CustomerAccountFragment() + val args = Bundle() + args.putSerializable(ConstantKeys.ACCOUNT_TYPE, accountType) + fragment.arguments = args + return fragment + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + if (arguments != null) { + accountType = arguments!!.getSerializable(ConstantKeys.ACCOUNT_TYPE) as AccountType + } + + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val rootview: View = inflater.inflate(R.layout.fragment_client_accounts, + container, false) + (activity as MifosBaseActivity).activityComponent.inject(this) + accountsPresenter.attachView(this) + return rootview + + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setUpViewPagerAndTabLayout() + setToolbarTitle(getString(R.string.accounts)) + accountsPresenter.loadLoanAccounts() + accountsPresenter.loadDepositAccounts() + } + + /** + * Returns tag of Fragment present at `position` + * @param position position of Fragment + * @return Tag of Fragment + */ + private fun getFragmentTag(position: Int): String { + return "android:switcher:" + R.id.viewpager + ":" + position + } + + + private fun setUpViewPagerAndTabLayout() { + val viewPagerAdapter = ViewPagerAdapter(childFragmentManager) + viewPagerAdapter.addFragment(AccountsFragment.newInstance(ConstantKeys.DEPOSIT_ACCOUNTS), + getString(R.string.deposit)) + viewPagerAdapter.addFragment(AccountsFragment.newInstance(ConstantKeys.LOAN_ACCOUNTS), + getString(R.string.loan)) + viewpager.adapter = viewPagerAdapter + viewpager.offscreenPageLimit = 2 + when (accountType) { + AccountType.DEPOSIT -> viewpager.currentItem = 0 + AccountType.LOAN -> viewpager.currentItem = 1 + } + + deposit_toggle_btn.setOnClickListener { + viewpager.currentItem = 0 + } + loan_toggle_btn.setOnClickListener { + viewpager.currentItem = 1 + } + + tabs.setupWithViewPager(viewpager) + viewpager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled(position: Int, positionOffset: Float, + positionOffsetPixels: Int) { + activity?.invalidateOptionsMenu() + (activity as MifosBaseActivity).hideKeyBoard(view!!) + } + + override fun onPageSelected(position: Int) {} + + override fun onPageScrollStateChanged(state: Int) {} + }) + + + } + + /** + * It provides with `loanAccounts` fetched from server which is then passed to fragment + * implementing [AccountsContract.View] i.e. [AccountsFragment] which further displays them + * in a recyclerView + * @param loanAccounts [List] of [LoanAccount] + */ + override fun showLoanAccounts(loanAccounts: List) { + + (childFragmentManager.findFragmentByTag(getFragmentTag(1)) as AccountsContract.View) + .showLoanAccounts(loanAccounts) + (childFragmentManager.findFragmentByTag(getFragmentTag(1)) as AccountsContract.View) + .hideProgress() + } + + /** + * It provides with `depositAccounts` fetched from server which is then passed to fragment + * implementing [AccountsContract.View] i.e. [AccountsFragment] which further displays them + * in a recyclerView + * @param depositAccounts [List] of [DepositAccount] + */ + + override fun showDepositAccounts(depositAccounts: List) { + (childFragmentManager.findFragmentByTag(getFragmentTag(0)) as AccountsContract.View) + .showDepositAccounts(depositAccounts) + (childFragmentManager.findFragmentByTag(getFragmentTag(0)) as AccountsContract.View) + .hideProgress() + } + + + override fun showError(message: String) { + (childFragmentManager.findFragmentByTag(getFragmentTag(1)) as AccountsContract.View) + .showError(getString(R.string.loan)) + (childFragmentManager.findFragmentByTag(getFragmentTag(0)) as AccountsContract.View) + .showError(getString(R.string.deposit)) + Toast.makeText(activity, message, Toast.LENGTH_SHORT).show() + } + + override fun showEmptyAccounts(feature: String) { + (childFragmentManager.findFragmentByTag(getFragmentTag(1)) as AccountsContract.View) + .showEmptyAccounts(getString(R.string.loan)) + (childFragmentManager.findFragmentByTag(getFragmentTag(0)) as AccountsContract.View) + .showEmptyAccounts(getString(R.string.deposit)) + } + + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + inflater?.inflate(R.menu.menu_account, menu) + if (viewpager.currentItem == 1) { + loan_toggle_focus_btn.visibility = View.VISIBLE + deposit_toggle_focus_btn.visibility = View.GONE + iv_apply_for_loan.visibility = View.VISIBLE + applyForLoan() + menu?.findItem(R.id.menu_filter_loan)?.isVisible = true + menu?.findItem(R.id.menu_filter_deposit)?.isVisible = false + menu?.findItem(R.id.menu_search_loan)?.isVisible = true + menu?.findItem(R.id.menu_search_deposit)?.isVisible = false + initSearch(menu!!, AccountType.LOAN) + } else if (viewpager.currentItem == 0) { + deposit_toggle_focus_btn.visibility = View.VISIBLE + loan_toggle_focus_btn.visibility = View.GONE + iv_apply_for_loan.visibility = View.GONE + menu?.findItem(R.id.menu_filter_loan)?.isVisible = false + menu?.findItem(R.id.menu_filter_deposit)?.isVisible = true + menu?.findItem(R.id.menu_search_deposit)?.isVisible = true + menu?.findItem(R.id.menu_search_loan)?.isVisible = false + initSearch(menu!!, AccountType.DEPOSIT) + } + super.onCreateOptionsMenu(menu, inflater) + + } + + private fun applyForLoan() { + iv_apply_for_loan.setOnClickListener(View.OnClickListener { + val intent = Intent(activity, LoanApplicationActivity::class.java) + intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER, "customer_identifier") + startActivity(intent) + }) + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId) { + R.id.menu_filter_loan -> showFilterDialog(AccountType.LOAN) + R.id.menu_filter_deposit -> showFilterDialog(AccountType.DEPOSIT) + } + return true + } + + private fun showFilterDialog(accountType: AccountType) { + val accountFilterBottomSheet = AccountsFilterBottomSheet() + when (accountType) { + AccountType.LOAN -> { + if ((childFragmentManager.findFragmentByTag(getFragmentTag(1)) + as AccountsFragment).currentFilterList == null) { + accountFilterBottomSheet.filterList = StatusUtils.getLoanAccountsStatusList(context!!) + } else { + accountFilterBottomSheet.filterList = (childFragmentManager + .findFragmentByTag(getFragmentTag(1)) + as AccountsFragment).currentFilterList + } + } + + AccountType.DEPOSIT -> { + if ((childFragmentManager.findFragmentByTag(getFragmentTag(0)) + as AccountsFragment).currentFilterList == null) { + accountFilterBottomSheet.filterList = StatusUtils.getDepositAccountsStatusList(context!!) + } else { + accountFilterBottomSheet.filterList = (childFragmentManager + .findFragmentByTag(getFragmentTag(0)) + as AccountsFragment).currentFilterList + } + } + + } + + accountFilterBottomSheet.accountType = accountType + accountFilterBottomSheet.show(childFragmentManager, getString(R.string.filter_accounts)) + } + + /** + * Initializes the search option in [Menu] depending upon `account` + * @param menu Interface for managing the items in a menu. + * @param account An enum of [AccountType] + */ + private fun initSearch(menu: Menu, account: AccountType) { + val manager = activity?.getSystemService(Context.SEARCH_SERVICE) as SearchManager + var search: SearchView? = null + + if (account == AccountType.LOAN) { + search = menu.findItem(R.id.menu_search_loan).actionView as SearchView + } else if (account == AccountType.DEPOSIT) { + search = menu.findItem(R.id.menu_search_deposit).actionView as SearchView + } + + search!!.setSearchableInfo(manager.getSearchableInfo(activity?.componentName)) + search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + return false + } + + override fun onQueryTextChange(newText: String): Boolean { + + if (account == AccountType.LOAN) { + (childFragmentManager.findFragmentByTag( + getFragmentTag(1)) as AccountsFragment).searchLoanAccount(newText) + } else if (account == AccountType.DEPOSIT) { + (childFragmentManager.findFragmentByTag( + getFragmentTag(0)) as AccountsFragment).searchDepositAccount(newText) + } + + return false + } + }) + } + + override fun showProgress() { + + } + + override fun hideProgress() { + + } + + + override fun onResume() { + super.onResume() + (activity as MifosBaseActivity).hideToolbarElevation() + } + + override fun onPause() { + super.onPause() + (activity as MifosBaseActivity).setToolbarElevation() + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/DashboardActivity.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/DashboardActivity.kt index 6895b8e2..e54a334b 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/DashboardActivity.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/DashboardActivity.kt @@ -31,12 +31,15 @@ import org.mifos.mobile.cn.ui.mifos.settings.SettingsFragment import org.mifos.mobile.cn.ui.utils.CircularImageView import org.mifos.mobile.cn.ui.utils.Toaster import android.widget.Toast +import com.mifos.mobile.passcode.utils.PasscodePreferencesHelper +import org.mifos.mobile.cn.ui.mifos.passcode.PasscodeActivity +import org.mifos.mobile.cn.ui.utils.ConstantKeys class DashboardActivity : MifosBaseActivity(), View.OnClickListener, NavigationView.OnNavigationItemSelectedListener { @Inject internal lateinit var preferencesHelper: PreferencesHelper - + private var passcodePreferencesHelper: PasscodePreferencesHelper? = null private lateinit var tvUsername: TextView private lateinit var ivCircularUserProfilePicture: CircularImageView private lateinit var ivTextDrawableUserProfilePicture: ImageView @@ -52,7 +55,7 @@ class DashboardActivity : MifosBaseActivity(), View.OnClickListener, NavigationV setupNavigationBar() setToolbarElevation() - replaceFragment(DashboardFragment.newInstance(), false, R.id.container) + replaceFragment(DashboardFragment.newInstance("customer_identifier"), false, R.id.container) } @@ -160,22 +163,30 @@ class DashboardActivity : MifosBaseActivity(), View.OnClickListener, NavigationV when (item.itemId) { R.id.item_home -> { hideToolbarElevation() - replaceFragment(DashboardFragment.newInstance(), true, R.id.container) + replaceFragment(DashboardFragment.newInstance("customer_identifier"), true, R.id.container) } R.id.item_accounts -> { replaceFragment(CustomerAccountFragment.newInstance(AccountType.DEPOSIT), true, R.id.container) } - R.id.item_logout -> { showLogoutDialog() } - + R.id.item_passcode ->{ + if (this != null) { + passcodePreferencesHelper = PasscodePreferencesHelper(this) + val currentPass: String = passcodePreferencesHelper!!.getPassCode() + passcodePreferencesHelper!!.savePassCode("") + val intent = Intent(this, PasscodeActivity::class.java) + intent.putExtra(ConstantKeys.CURR_PASSWORD, currentPass) + intent.putExtra(ConstantKeys.UPDATE_PASSWORD_KEY, true) + startActivity(intent) + } + } R.id.item_product -> { replaceFragment(ProductFragment.Companion.newInstance(), true, R.id.container) } - R.id.item_recent_transactions -> { replaceFragment(RecentTransactionsFragment.Companion.newInstance(),true,R.id.container) } @@ -185,8 +196,14 @@ class DashboardActivity : MifosBaseActivity(), View.OnClickListener, NavigationV R.id.item_settings -> { replaceFragment(SettingsFragment.newInstance(), true, R.id.container) } + R.id.item_share -> { + val sharingIntent = Intent(android.content.Intent.ACTION_SEND) + sharingIntent.type = "text/plain" + sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.app_link)) + sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, getString(R.string.share_message)) + startActivity(Intent.createChooser(sharingIntent, getString(R.string.share_the_app_link))) + } } - // close the drawer drawerLayout.closeDrawer(GravityCompat.START) setNavigationViewSelectedItem(R.id.item_home) diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Home.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Home.kt new file mode 100644 index 00000000..fc2e3ab8 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Home.kt @@ -0,0 +1,80 @@ +package org.mifos.mobile.cn.ui.mifos + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.android.material.bottomsheet.BottomSheetBehavior +import kotlinx.android.synthetic.main.content_fragment_dashboard.* +import kotlinx.android.synthetic.main.fragment_home.* +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.ui.base.MifosBaseFragment +import org.mifos.mobile.cn.ui.mifos.recentTransactions.RecentTransactionsFragment + + +class Home : MifosBaseFragment(), View.OnClickListener { + + private lateinit var rootView: View + private lateinit var sheetBehavior: BottomSheetBehavior<*> + + companion object { + fun newInstance(): Home = Home() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + rootView = inflater.inflate(R.layout.fragment_home, container, false) + val ft = childFragmentManager.beginTransaction() + val rt = RecentTransactionsFragment() + ft.replace(R.id.fl_recentTransactions, rt) + ft.addToBackStack(null) + ft.commit() + return rootView + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + sheetBehavior = BottomSheetBehavior.from(rt_bottom_sheet) + sheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + // React to state change + when (newState) { + BottomSheetBehavior.STATE_HIDDEN -> { + } + BottomSheetBehavior.STATE_EXPANDED -> { + } + BottomSheetBehavior.STATE_COLLAPSED -> { + } + BottomSheetBehavior.STATE_HALF_EXPANDED -> { + } + BottomSheetBehavior.STATE_DRAGGING -> { + } + BottomSheetBehavior.STATE_SETTLING -> { + } + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) { + // React to dragging events + } + }) + receive.setOnClickListener(this) + + } + + override fun onClick(view: View) { + + when (view.id) { + R.id.receive ->{ + qrcode() + } + } + } + + private fun qrcode() { + val intent = Intent(activity, QRGenerator::class.java) + startActivity(intent) + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Main.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Main.kt new file mode 100644 index 00000000..f0aae40f --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Main.kt @@ -0,0 +1,75 @@ +package org.mifos.mobile.cn.ui.mifos + +import android.os.Bundle +import androidx.annotation.NonNull +import androidx.fragment.app.Fragment +import com.google.android.material.bottomnavigation.BottomNavigationView +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.enums.AccountType +import org.mifos.mobile.cn.ui.base.MifosBaseActivity +import org.mifos.mobile.cn.ui.utils.ConstantKeys +import android.view.MenuItem as MenuItem1 + + +class Main : MifosBaseActivity(), BottomNavigationView.OnNavigationItemSelectedListener { + private var bottomNavigationView: BottomNavigationView? = null + private var menuItem = -1 + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_home) + + bottomNavigationView = findViewById(R.id.bottom_navigation) + setupNavigationBar() + bottomNavigationView?.run { setSelectedItemId(R.id.action_home) + } + + + } + + override fun onNavigationItemSelected(item: MenuItem1): Boolean { + clearFragmentBackStack() + setToolbarElevation() + menuItem = item.itemId +navigateFragment(item.itemId,false) + return true + } + private fun setupNavigationBar() { + + bottomNavigationView?.setOnNavigationItemSelectedListener { item -> + navigateFragment(item.itemId, false) + true + } + + } + override fun onBackPressed() { + val fragment: Fragment? = supportFragmentManager + .findFragmentById(R.id.bottom_navigation_fragment_container) + if (fragment != null && fragment !is Home && fragment.isVisible()) { + navigateFragment(R.id.action_home, true) + return + } + val count = supportFragmentManager.backStackEntryCount + if (count == 0) { + super.onBackPressed() + //additional code + } else { + supportFragmentManager.popBackStack() + } + } + private fun navigateFragment(id: Int, shouldSelect: Boolean) { + if (shouldSelect) { + bottomNavigationView?.setSelectedItemId(id) + } else { + when (id) { + R.id.action_home -> replaceFragment(Home.newInstance(), false, + R.id.bottom_navigation_fragment_container) + R.id.action_acounts -> replaceFragment(Account.newInstance(AccountType.DEPOSIT), false, + R.id.bottom_navigation_fragment_container) + R.id.action_transfer -> replaceFragment(Transfer.newInstance(), false, + R.id.bottom_navigation_fragment_container) + R.id.action_profile -> replaceFragment(Profile.newInstance(), false, + R.id.bottom_navigation_fragment_container) + } + } + } +} diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Notification.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Notification.kt new file mode 100644 index 00000000..4755ef9b --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Notification.kt @@ -0,0 +1,14 @@ +package org.mifos.mobile.cn.ui.mifos + +import android.os.Bundle +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.ui.base.MifosBaseActivity + +class Notification: MifosBaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_notification) + showBackButton() + setToolbarTitle("Notifications") + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Profile.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Profile.kt new file mode 100644 index 00000000..e7bd605c --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Profile.kt @@ -0,0 +1,57 @@ +package org.mifos.mobile.cn.ui.mifos + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.PopupWindow +import android.widget.RelativeLayout +import kotlinx.android.synthetic.main.fragment_client_accounts.* +import kotlinx.android.synthetic.main.fragment_profile.* +import kotlinx.android.synthetic.main.menu_layout.* +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.ui.base.MifosBaseFragment +import org.mifos.mobile.cn.ui.mifos.beneficiaries.BeneficiariesActivity + + +class Profile : MifosBaseFragment(){ + var mypopupWindow: PopupWindow? = null + companion object { + fun newInstance(): Profile = Profile() + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + getToolbar().setVisibility(View.GONE); + return inflater.inflate(R.layout.fragment_profile, container, false) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + notify.setOnClickListener { + val intent = Intent(activity, Notification::class.java) + startActivity(intent) + } + setPopUpWindow(view) + val clickListener = View.OnClickListener { view -> + when (view.id) { + R.id.image_menu -> { + mypopupWindow?.showAsDropDown(view,0,0); + } + } + } + + image_menu.setOnClickListener(clickListener) + } + + private fun setPopUpWindow(view: View) { + val view = LayoutInflater.from(context).inflate(R.layout.menu_layout, null) + mypopupWindow = PopupWindow(view, 350, RelativeLayout.LayoutParams.WRAP_CONTENT, true) + val beneficiary = view.findViewById(R.id.text_beneficiary) as RelativeLayout + beneficiary.setOnClickListener(View.OnClickListener { + val intent = Intent(activity, BeneficiariesActivity::class.java) + startActivity(intent) + }) + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/QRCodeHelper.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/QRCodeHelper.kt new file mode 100644 index 00000000..921d60b8 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/QRCodeHelper.kt @@ -0,0 +1,81 @@ +package org.mifos.mobile.cn.ui.mifos + +import android.content.Context +import android.graphics.Bitmap +import android.util.Log +import androidx.annotation.IntRange +import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType +import com.google.zxing.WriterException +import com.google.zxing.qrcode.QRCodeWriter +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import java.util.* + +class QRCodeHelper private constructor(context: Context) { + private var mErrorCorrectionLevel: ErrorCorrectionLevel? = null + private var mMargin = 0 + private var mContent: String? = null + private var mWidth: Int + private var mHeight: Int + val qRCOde: Bitmap? + get() = generate() + + fun setErrorCorrectionLevel(level: ErrorCorrectionLevel?): QRCodeHelper { + mErrorCorrectionLevel = level + return this + } + fun setContent(content: String?): QRCodeHelper { + mContent = content + return this + } + fun setWidthAndHeight(@IntRange(from = 1) width: Int, @IntRange(from = 1) height: Int): QRCodeHelper { + mWidth = width + mHeight = height + return this + } + fun setMargin(@IntRange(from = 0) margin: Int): QRCodeHelper { + mMargin = margin + return this + } + private fun generate(): Bitmap? { + val hintsMap: MutableMap = HashMap() + hintsMap[EncodeHintType.CHARACTER_SET] = "utf-8" + hintsMap[EncodeHintType.ERROR_CORRECTION] = mErrorCorrectionLevel + hintsMap[EncodeHintType.MARGIN] = mMargin + try { + val bitMatrix = QRCodeWriter().encode(mContent, BarcodeFormat.QR_CODE, mWidth, mHeight, hintsMap) + val pixels = IntArray(mWidth * mHeight) + for (i in 0 until mHeight) { + for (j in 0 until mWidth) { + if (bitMatrix[j, i]) { + pixels[i * mWidth + j] = -0x1000000 + } else { + pixels[i * mWidth + j] = -0x1 + } + } + } + return Bitmap.createBitmap(pixels, mWidth, mHeight, Bitmap.Config.ARGB_8888) + } catch (e: WriterException) { + e.printStackTrace() + } + return null + } + + companion object { + private var qrCodeHelper: QRCodeHelper? = null + + fun newInstance(context: Context): QRCodeHelper? { + if (qrCodeHelper == null) { + qrCodeHelper = QRCodeHelper(context) + } + return qrCodeHelper + } + } + + init { + mHeight = (context.resources.displayMetrics.heightPixels / 2.4).toInt() + mWidth = (context.resources.displayMetrics.widthPixels / 1.3).toInt() + Log.e("Dimension = %s", mHeight.toString() + "") + Log.e("Dimension = %s", mWidth.toString() + "") + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/QRGenerator.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/QRGenerator.kt new file mode 100644 index 00000000..5746f331 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/QRGenerator.kt @@ -0,0 +1,59 @@ +package org.mifos.mobile.cn.ui.mifos + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.text.TextUtils +import android.view.View +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import com.google.gson.Gson +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import kotlinx.android.synthetic.main.qrgenerator.* +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.ui.base.MifosBaseActivity + +class QRGenerator: MifosBaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.qrgenerator) + setToolbarTitle(getString(R.string.home)) + getToolbar().setVisibility(View.GONE) + generateQrCodeButton.setOnClickListener { + if (checkEditText()) { + hideKeyboard() + val user = UserObject(name = fullNameEditText.text.toString(), mobile = mobileEditText.text.toString(),email = emailEditText.text.toString(),accountNumber = accountEditText.text.toString()) + val serializeString = Gson().toJson(user) + setImageBitmap(serializeString) + } + } + } + + private fun setImageBitmap(encryptedString: String?) { + val bitmap = QRCodeHelper.newInstance(this)?.setContent(encryptedString)?.setErrorCorrectionLevel(ErrorCorrectionLevel.Q)?.setMargin(2)?.qRCOde + qrCodeImageView.setImageBitmap(bitmap) + } + + /** + * Hides the soft input keyboard if it is shown to the screen. + */ + + private fun hideKeyboard() { + val view = this.currentFocus + if (view != null) { + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(view.windowToken, 0) + } + } + + private fun checkEditText(): Boolean { + if (TextUtils.isEmpty(fullNameEditText.text.toString())) { + Toast.makeText(this, "fullName field cannot be empty!", Toast.LENGTH_SHORT).show() + return false + } else if (TextUtils.isEmpty(mobileEditText.text.toString())) { + Toast.makeText(this, "Mobile number field cannot be empty!", Toast.LENGTH_SHORT).show() + return false + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/ShowActivity.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/ShowActivity.kt new file mode 100644 index 00000000..0f8c5197 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/ShowActivity.kt @@ -0,0 +1,13 @@ +package org.mifos.mobile.cn.ui.mifos + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import org.mifos.mobile.cn.R + +class ShowActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_show) + } +} diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Transfer.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Transfer.kt new file mode 100644 index 00000000..db0d3a93 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/Transfer.kt @@ -0,0 +1,57 @@ +package org.mifos.mobile.cn.ui.mifos + +import android.Manifest +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.google.zxing.Result +import com.karumi.dexter.Dexter +import com.karumi.dexter.PermissionToken +import com.karumi.dexter.listener.PermissionDeniedResponse +import com.karumi.dexter.listener.PermissionGrantedResponse +import com.karumi.dexter.listener.PermissionRequest +import com.karumi.dexter.listener.single.PermissionListener +import kotlinx.android.synthetic.main.fragment_transfer.* +import me.dm7.barcodescanner.zxing.ZXingScannerView +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.ui.base.MifosBaseFragment + +class Transfer : MifosBaseFragment(), ZXingScannerView.ResultHandler { + + companion object { + fun newInstance(): Transfer = Transfer() + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + getToolbar().setVisibility(View.GONE) + + return inflater.inflate(R.layout.fragment_transfer, container, false) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + Dexter.withActivity(requireActivity()) + .withPermission(Manifest.permission.CAMERA) + .withListener(object:PermissionListener{ + override fun onPermissionGranted(response: PermissionGrantedResponse?) { + qrCodeScanner.setResultHandler(this@Transfer) + qrCodeScanner.setAutoFocus(true) + qrCodeScanner.startCamera() + } + + override fun onPermissionRationaleShouldBeShown(permission: PermissionRequest?, token: PermissionToken?) { + TODO("Not yet implemented") + } + + override fun onPermissionDenied(response: PermissionDeniedResponse?) { + Toast.makeText(requireActivity(),"You should enable this permission",Toast.LENGTH_SHORT).show() + } + + }).check() + } + override fun handleResult(rawResult: Result?) { + Toast.makeText(requireActivity(),rawResult!!.text,Toast.LENGTH_SHORT).show() + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/UserObject.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/UserObject.kt new file mode 100644 index 00000000..1170b776 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/UserObject.kt @@ -0,0 +1,3 @@ +package org.mifos.mobile.cn.ui.mifos + +class UserObject(var name: String, var mobile: String, var email: String, var accountNumber: String) \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/accounts/AccountsFragment.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/accounts/AccountsFragment.kt index 66d67927..9ed2aa49 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/accounts/AccountsFragment.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/accounts/AccountsFragment.kt @@ -1,14 +1,13 @@ package org.mifos.mobile.cn.ui.mifos.accounts import android.os.Bundle -import android.util.Log -import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.github.therajanmaurya.sweeterror.SweetUIErrorHandler import kotlinx.android.synthetic.main.fragment_accounts.* +import kotlinx.android.synthetic.main.layout_exception_handler.* import org.mifos.mobile.cn.R import org.mifos.mobile.cn.data.models.accounts.deposit.DepositAccount import org.mifos.mobile.cn.data.models.accounts.loan.LoanAccount @@ -19,7 +18,6 @@ import org.mifos.mobile.cn.ui.base.MifosBaseFragment import org.mifos.mobile.cn.ui.utils.ConstantKeys import org.mifos.mobile.cn.ui.utils.Network import javax.inject.Inject -import kotlinx.android.synthetic.main.layout_sweet_exception_handler.* import org.mifos.mobile.cn.data.models.CheckboxStatus import org.mifos.mobile.cn.ui.base.OnItemClickListener import org.mifos.mobile.cn.ui.mifos.customerDepositDetails.CustomerDepositDetailsFragment @@ -115,8 +113,6 @@ class AccountsFragment : MifosBaseFragment(), AccountsContract.View, OnItemClick layoutManager.orientation = LinearLayoutManager.VERTICAL rv_accounts.layoutManager = layoutManager rv_accounts.setHasFixedSize(true) - rv_accounts.addItemDecoration(DividerItemDecoration(activity, - layoutManager.orientation)) btn_try_again.setOnClickListener { retry() } when (accountType) { @@ -143,7 +139,7 @@ class AccountsFragment : MifosBaseFragment(), AccountsContract.View, OnItemClick (activity as MifosBaseActivity).replaceFragment( CustomerDepositDetailsFragment.newInstance( depositAccounts[position].accountIdentifier!!),true,R.id.container - ) + ) } } } diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/beneficiaries/BeneficiariesActivity.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/beneficiaries/BeneficiariesActivity.kt new file mode 100644 index 00000000..74d5351c --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/beneficiaries/BeneficiariesActivity.kt @@ -0,0 +1,66 @@ +package org.mifos.mobile.cn.ui.mifos.beneficiaries + +import android.annotation.SuppressLint +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.widget.LinearLayout +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.activity_beneficiaries.* +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.data.models.beneficiaries.Beneficiary +import org.mifos.mobile.cn.ui.adapter.BeneficiariesAdapter + +class BeneficiariesActivity : AppCompatActivity() { + + @SuppressLint("ResourceAsColor") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_beneficiaries) + + val name: String = getString(R.string.name) + val description: String = getString(R.string.description) + val price: String = getString(R.string.dummy_price) + + beneficiariesRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false) + + val items = ArrayList() + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + items.add(Beneficiary(name, description, price)) + + val adapter = BeneficiariesAdapter(items) + beneficiariesRecyclerView.adapter = adapter + + btnListBeneficiary.setTextColor(ContextCompat.getColorStateList(getApplicationContext(), R.color.white)) + btnAddBeneficiary.setTextColor(ContextCompat.getColorStateList(getApplicationContext(), R.color.violet)) + btnAddBeneficiary.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), R.color.white)) + btnListBeneficiary.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), R.color.violet)) + + btnAddBeneficiary.setOnClickListener { + mBtnAddFocused.visibility = LinearLayout.VISIBLE + mBtnListFocused.visibility = LinearLayout.INVISIBLE + btnListBeneficiary.setTextColor(ContextCompat.getColorStateList(getApplicationContext(), R.color.violet)) + btnAddBeneficiary.setTextColor(ContextCompat.getColorStateList(getApplicationContext(), R.color.white)) + btnListBeneficiary.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), R.color.white)) + btnAddBeneficiary.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), R.color.violet)) + } + + btnListBeneficiary.setOnClickListener { + mBtnAddFocused.visibility = LinearLayout.INVISIBLE + mBtnListFocused.visibility = LinearLayout.VISIBLE + btnAddBeneficiary.setTextColor(ContextCompat.getColorStateList(getApplicationContext(), R.color.violet)) + btnListBeneficiary.setTextColor(ContextCompat.getColorStateList(getApplicationContext(), R.color.white)) + btnListBeneficiary.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), R.color.violet)) + btnAddBeneficiary.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), R.color.white)) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerAccounts/CustomerAccountFragment.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerAccounts/CustomerAccountFragment.kt index 36598fc8..fc87286b 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerAccounts/CustomerAccountFragment.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerAccounts/CustomerAccountFragment.kt @@ -2,6 +2,7 @@ package org.mifos.mobile.cn.ui.mifos.customerAccounts import android.app.SearchManager import android.content.Context +import android.content.Intent import android.os.Bundle import androidx.viewpager.widget.ViewPager import androidx.appcompat.widget.SearchView @@ -19,6 +20,7 @@ import org.mifos.mobile.cn.ui.mifos.accounts.AccountsContract import org.mifos.mobile.cn.ui.mifos.accounts.AccountsFragment import org.mifos.mobile.cn.ui.mifos.accounts.AccountsPresenter import org.mifos.mobile.cn.ui.mifos.accountsFilter.AccountsFilterBottomSheet +import org.mifos.mobile.cn.ui.mifos.loanApplication.loanActivity.LoanApplicationActivity import org.mifos.mobile.cn.ui.utils.ConstantKeys import org.mifos.mobile.cn.ui.utils.StatusUtils import javax.inject.Inject @@ -93,6 +95,13 @@ class CustomerAccountFragment : MifosBaseFragment(), AccountsContract.View { AccountType.LOAN -> viewpager.currentItem = 1 } + deposit_toggle_btn.setOnClickListener { + viewpager.currentItem = 0 + } + loan_toggle_btn.setOnClickListener { + viewpager.currentItem = 1 + } + tabs.setupWithViewPager(viewpager) viewpager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrolled(position: Int, positionOffset: Float, @@ -157,12 +166,19 @@ class CustomerAccountFragment : MifosBaseFragment(), AccountsContract.View { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { inflater?.inflate(R.menu.menu_account, menu) if (viewpager.currentItem == 1) { + loan_toggle_focus_btn.visibility = View.VISIBLE + deposit_toggle_focus_btn.visibility = View.GONE + iv_apply_for_loan.visibility = View.VISIBLE + applyForLoan() menu?.findItem(R.id.menu_filter_loan)?.isVisible = true menu?.findItem(R.id.menu_filter_deposit)?.isVisible = false menu?.findItem(R.id.menu_search_loan)?.isVisible = true menu?.findItem(R.id.menu_search_deposit)?.isVisible = false initSearch(menu!!, AccountType.LOAN) } else if (viewpager.currentItem == 0) { + deposit_toggle_focus_btn.visibility = View.VISIBLE + loan_toggle_focus_btn.visibility = View.GONE + iv_apply_for_loan.visibility = View.GONE menu?.findItem(R.id.menu_filter_loan)?.isVisible = false menu?.findItem(R.id.menu_filter_deposit)?.isVisible = true menu?.findItem(R.id.menu_search_deposit)?.isVisible = true @@ -173,6 +189,14 @@ class CustomerAccountFragment : MifosBaseFragment(), AccountsContract.View { } + private fun applyForLoan() { + iv_apply_for_loan.setOnClickListener(View.OnClickListener { + val intent = Intent(activity, LoanApplicationActivity::class.java) + intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER, "customer_identifier") + startActivity(intent) + }) + } + override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { R.id.menu_filter_loan -> showFilterDialog(AccountType.LOAN) diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerDetails/CustomerDetailsFragment.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerDetails/CustomerDetailsFragment.kt index 507d1ac1..d8cf978c 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerDetails/CustomerDetailsFragment.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerDetails/CustomerDetailsFragment.kt @@ -28,44 +28,43 @@ import org.mifos.mobile.cn.ui.views.HeaderView import javax.inject.Inject -class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChangedListener,CustomerDetailsContract.View, View.OnClickListener{ - +class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChangedListener, CustomerDetailsContract.View, View.OnClickListener { @Inject lateinit var customerDetailsPresenter: CustomerDetailsPresenter - private lateinit var rootView : View - private lateinit var customerIdentification : String - private var isHideToolbarView: Boolean = false - private lateinit var collapsingToolbarLayout : CollapsingToolbarLayout - private lateinit var customer : Customer - private lateinit var toolbarHeaderView : HeaderView - private lateinit var floatHeaderView : HeaderView - - companion object { - fun newInstance(identifier: String): CustomerDetailsFragment { - val fragment = CustomerDetailsFragment() - val args = Bundle() - args.putString(ConstantKeys.CUSTOMER_IDENTIFIER,identifier) - fragment.arguments = args - return fragment - - } - } + private lateinit var rootView: View + private lateinit var customerIdentification: String + private var isHideToolbarView: Boolean = false + private lateinit var collapsingToolbarLayout: CollapsingToolbarLayout + private lateinit var customer: Customer + private lateinit var toolbarHeaderView: HeaderView + private lateinit var floatHeaderView: HeaderView + + companion object { + fun newInstance(identifier: String): CustomerDetailsFragment { + val fragment = CustomerDetailsFragment() + val args = Bundle() + args.putString(ConstantKeys.CUSTOMER_IDENTIFIER, identifier) + fragment.arguments = args + return fragment + } + } - override fun onCreate(savedInstanceState: Bundle?){ + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setToolbarTitle(getString(R.string.account_overview)) - if(arguments != null){1 + if (arguments != null) { + 1 customerIdentification = arguments!!.getString(ConstantKeys.CUSTOMER_IDENTIFIER) } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - rootView = inflater.inflate(R.layout.fragment_customer_details,container,false) + rootView = inflater.inflate(R.layout.fragment_customer_details, container, false) (activity as MifosBaseActivity).activityComponent.inject(this) customerDetailsPresenter.attachView(this) @@ -88,12 +87,13 @@ class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChange } + override fun onClick(view: View) { - when(view.id){ + when (view.id) { R.id.ll_deposit_accounts -> { openDepositAccount() } - R.id.ll_loan_accounts-> { + R.id.ll_loan_accounts -> { openLoanAccount() } R.id.ll_activities -> { @@ -117,13 +117,12 @@ class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChange override fun onResume() { super.onResume() - cl_customer_details.visibility = View.GONE - collapsingToolbarLayout.title= " " + cl_customer_details.visibility = View.GONE + collapsingToolbarLayout.title = " " customerDetailsPresenter.loadCustomerDetails(customerIdentification) } - override fun showCustomerDetails(customer: Customer) { this.customer = customer cl_customer_details.visibility = View.VISIBLE @@ -132,10 +131,10 @@ class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChange loadCustomerPortrait() tv_current_status.text = customer.currentState!!.name - StatusUtils.setCustomerStatusIcon(customer.currentState!!,iv_current_status,context) + StatusUtils.setCustomerStatusIcon(customer.currentState!!, iv_current_status, context) - val address : Address = customer.address!! + val address: Address = customer.address!! val addressBuilder = StringBuilder() addressBuilder .append(address.street).append(", ") @@ -171,6 +170,7 @@ class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChange } showToolbarTitleSubtitle(title, subtitle) } + override fun showUserInterface() { if (toolbar_customer != null) { (activity as AppCompatActivity).setSupportActionBar(toolbar_customer) @@ -178,12 +178,13 @@ class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChange .setDisplayHomeAsUpEnabled(true) } - collapsingToolbarLayout.title= " " + collapsingToolbarLayout.title = " " app_bar_layout.addOnOffsetChangedListener(this) } + override fun showToolbarTitleSubtitle(title: String, subtitle: String) { - toolbarHeaderView.bindTo(title,subtitle) - floatHeaderView.bindTo(title,subtitle) + toolbarHeaderView.bindTo(title, subtitle) + floatHeaderView.bindTo(title, subtitle) } @@ -206,25 +207,22 @@ class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChange } - override fun loadCustomerPortrait() { val imageLoaderUtils = ImageLoaderUtils(this.context!!) - imageLoaderUtils.loadImage(imageLoaderUtils.buildCustomerPortraitImageUrl(customer.identifier),iv_customer_profile,R.drawable.mifos_logo_new) + imageLoaderUtils.loadImage(imageLoaderUtils.buildCustomerPortraitImageUrl(customer.identifier), iv_customer_profile, R.drawable.mifos_logo_new) } override fun showProgressbar() { - showProgressBar() + showProgressBar() } override fun hideProgressbar() { - hideProgressBar() + hideProgressBar() } - - override fun getCustomerStatus(): Customer.State { return customer.currentState!! } @@ -241,6 +239,7 @@ class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChange hideProgressDialog() customerDetailsPresenter.detachView() } + override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { val maxScroll = appBarLayout!!.totalScrollRange val percentage = Math.abs(verticalOffset).toFloat() / maxScroll.toFloat() @@ -254,28 +253,30 @@ class CustomerDetailsFragment : MifosBaseFragment(), AppBarLayout.OnOffsetChange } } + private fun openDepositAccount() { (activity as MifosBaseActivity) .replaceFragment(CustomerAccountFragment.newInstance(AccountType.DEPOSIT), true, R.id.container) } + private fun openLoanAccount() { (activity as MifosBaseActivity) .replaceFragment(CustomerAccountFragment.newInstance(AccountType.LOAN), true, R.id.container) } + private fun openCustomerActivities() { - val intent = Intent(activity,CustomerActivitiesActivity::class.java) - intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER,"customer_identifier") + val intent = Intent(activity, CustomerActivitiesActivity::class.java) + intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER, "customer_identifier") startActivity(intent) } + private fun openIdentificationCards() { - val intent = Intent(activity,IdentificationsActivity::class.java) - intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER,"customer_identifier") + val intent = Intent(activity, IdentificationsActivity::class.java) + intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER, "customer_identifier") startActivity(intent) } - - } \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerDetails/CustomerDetailsPresenter.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerDetails/CustomerDetailsPresenter.kt index 708dafda..9579692d 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerDetails/CustomerDetailsPresenter.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerDetails/CustomerDetailsPresenter.kt @@ -1,21 +1,19 @@ package org.mifos.mobile.cn.ui.mifos.customerDetails import android.content.Context -import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.observers.DisposableObserver import io.reactivex.schedulers.Schedulers import org.mifos.mobile.cn.R -import org.mifos.mobile.cn.data.models.customer.Customer import org.mifos.mobile.cn.data.datamanager.DataManagerCustomer import org.mifos.mobile.cn.data.datamanager.contracts.ManagerCustomer -import org.mifos.mobile.cn.fakesource.FakeRemoteDataSource +import org.mifos.mobile.cn.data.models.customer.Customer import org.mifos.mobile.cn.injection.ApplicationContext import org.mifos.mobile.cn.injection.ConfigPersistent import org.mifos.mobile.cn.ui.base.BasePresenter -import java.util.concurrent.Callable import javax.inject.Inject + @ConfigPersistent class CustomerDetailsPresenter @Inject constructor(@ApplicationContext context: Context, dataManagerCustomer: DataManagerCustomer): BasePresenter(context), CustomerDetailsContract.Presenter { diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerProfile/CustomerProfileActivity.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerProfile/CustomerProfileActivity.kt index 27cf1165..8dc9bc6a 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerProfile/CustomerProfileActivity.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerProfile/CustomerProfileActivity.kt @@ -12,13 +12,11 @@ import android.provider.MediaStore import android.view.Menu import android.view.MenuItem import android.widget.ImageView -import com.github.therajanmaurya.sweeterror.SweetUIErrorHandler import kotlinx.android.synthetic.main.activity_customer_profile.* import org.mifos.mobile.cn.R import org.mifos.mobile.cn.ui.base.MifosBaseActivity import org.mifos.mobile.cn.ui.utils.* import java.io.ByteArrayOutputStream -import java.util.jar.Manifest class CustomerProfileActivity: MifosBaseActivity(),CustomerProfileContract.View { @@ -43,7 +41,7 @@ class CustomerProfileActivity: MifosBaseActivity(),CustomerProfileContract.View override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == R.id.menu_customer_profile_share) { - checkCameraPermission() + checkWriteExternalStoragePermission() return true } else return super.onOptionsItemSelected(item) @@ -79,7 +77,7 @@ class CustomerProfileActivity: MifosBaseActivity(),CustomerProfileContract.View return returnedBitmap } - override fun checkCameraPermission() { + override fun checkWriteExternalStoragePermission() { if (CheckSelfPermissionAndRequest.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)){ shareImage() @@ -98,6 +96,10 @@ class CustomerProfileActivity: MifosBaseActivity(),CustomerProfileContract.View resources.getString( R.string.dialog_message_write_permission_for_share_never_ask_again), ConstantKeys.PERMISSIONS_WRITE_EXTERNAL_STORAGE_STATUS) + if (CheckSelfPermissionAndRequest.checkSelfPermission(this, + android.Manifest.permission.WRITE_EXTERNAL_STORAGE)){ + shareImage() + } } override fun loadCustomerPortrait() { @@ -110,7 +112,7 @@ class CustomerProfileActivity: MifosBaseActivity(),CustomerProfileContract.View override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { when (requestCode) { - ConstantKeys.PERMISSIONS_REQUEST_CAMERA -> { + ConstantKeys.PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE -> { if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { shareImage() } else { diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerProfile/CustomerProfileContract.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerProfile/CustomerProfileContract.kt index e316a643..9765ea71 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerProfile/CustomerProfileContract.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/customerProfile/CustomerProfileContract.kt @@ -5,7 +5,7 @@ import org.mifos.mobile.cn.ui.base.MvpView interface CustomerProfileContract { interface View: MvpView{ - fun checkCameraPermission(); + fun checkWriteExternalStoragePermission(); fun requestPermission(); diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/dashboard/DashboardFragment.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/dashboard/DashboardFragment.kt index 977343ff..536012d2 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/dashboard/DashboardFragment.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/dashboard/DashboardFragment.kt @@ -5,28 +5,34 @@ import android.content.Intent import android.os.Bundle import android.view.* +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import kotlinx.android.synthetic.main.fragment_dashboard.* import org.mifos.mobile.cn.data.models.customer.Customer import org.mifos.mobile.cn.R import org.mifos.mobile.cn.enums.AccountType import org.mifos.mobile.cn.ui.base.MifosBaseActivity import org.mifos.mobile.cn.ui.base.MifosBaseFragment +import org.mifos.mobile.cn.ui.mifos.Main +import org.mifos.mobile.cn.ui.mifos.QRGenerator import org.mifos.mobile.cn.ui.mifos.customerAccounts.CustomerAccountFragment import org.mifos.mobile.cn.ui.mifos.customerDetails.CustomerDetailsActivity +import org.mifos.mobile.cn.ui.mifos.customerProfile.CustomerProfileActivity import org.mifos.mobile.cn.ui.mifos.loanApplication.loanActivity.LoanApplicationActivity import org.mifos.mobile.cn.ui.mifos.recentTransactions.RecentTransactionsFragment import org.mifos.mobile.cn.ui.utils.ConstantKeys -class DashboardFragment : MifosBaseFragment(), View.OnClickListener { - +class DashboardFragment : MifosBaseFragment(), View.OnClickListener, SwipeRefreshLayout.OnRefreshListener { + private lateinit var customerIdentification: String private lateinit var rootView: View private lateinit var customer: Customer + companion object { - fun newInstance(): DashboardFragment { + fun newInstance(identifier: String): DashboardFragment { val fragment = DashboardFragment() val args = Bundle() + args.putString(ConstantKeys.CUSTOMER_IDENTIFIER, identifier) fragment.arguments = args return fragment } @@ -38,6 +44,10 @@ class DashboardFragment : MifosBaseFragment(), View.OnClickListener { rootView = inflater.inflate(R.layout.fragment_dashboard, container, false) setHasOptionsMenu(true) setToolbarTitle(getString(R.string.home)) + if (arguments != null) { + 1 + customerIdentification = arguments!!.getString(ConstantKeys.CUSTOMER_IDENTIFIER) + } return rootView } @@ -47,6 +57,8 @@ class DashboardFragment : MifosBaseFragment(), View.OnClickListener { ll_accounts.setOnClickListener(this) ll_account_overview.setOnClickListener(this) ll_recent_transactions.setOnClickListener(this) + ll_charges.setOnClickListener(this) + swipe_home_container.setOnRefreshListener(this) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -54,6 +66,11 @@ class DashboardFragment : MifosBaseFragment(), View.OnClickListener { super.onCreateOptionsMenu(menu, inflater) } + override fun onResume() { + super.onResume() + this.onCreate(null) + } + override fun onClick(view: View) { when (view.id) { @@ -69,9 +86,21 @@ class DashboardFragment : MifosBaseFragment(), View.OnClickListener { R.id.ll_recent_transactions -> { showRecentTransactions() } + R.id.ll_charges ->{ + test() + } + R.id.ll_surveys ->{ + qrcode() + } } } + private fun qrcode() { + val intent = Intent(activity, QRGenerator::class.java) + intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER, "customer_identifier") + startActivity(intent) + } + private fun applyForLoan() { val intent = Intent(activity, LoanApplicationActivity::class.java) intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER, "customer_identifier") @@ -81,18 +110,30 @@ class DashboardFragment : MifosBaseFragment(), View.OnClickListener { private fun openAccount() { (activity as MifosBaseActivity) .replaceFragment(CustomerAccountFragment.newInstance(AccountType.DEPOSIT), - true, R.id.container) + true, R.id.container) } - private fun showCustomerDetails(){ - val intent = Intent(activity,CustomerDetailsActivity::class.java) - intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER,"customer_identifier") - startActivity(intent) + + private fun showCustomerDetails() { + val intent = Intent(activity, CustomerDetailsActivity::class.java) + intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER, "customer_identifier") + startActivity(intent) } - private fun showRecentTransactions(){ + + private fun showRecentTransactions() { (activity as MifosBaseActivity) .replaceFragment(RecentTransactionsFragment.Companion.newInstance(), - true,R.id.container) + true, R.id.container) + } + private fun test(){ + val intent = Intent(activity, Main::class.java) + intent.putExtra(ConstantKeys.CUSTOMER_IDENTIFIER, "customer_identifier") + startActivity(intent) + } + + override fun onRefresh() { + this.onResume() + swipe_home_container.isRefreshing = false } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/login/LoginActivity.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/login/LoginActivity.kt index 31a84ca1..5427c155 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/login/LoginActivity.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/login/LoginActivity.kt @@ -13,6 +13,7 @@ import org.mifos.mobile.cn.ui.utils.ConstantKeys import org.mifos.mobile.cn.ui.utils.Toaster import javax.inject.Inject import kotlinx.android.synthetic.main.activity_login.* +import org.mifos.mobile.cn.ui.mifos.signup.SignupActivity class LoginActivity : MifosBaseActivity(), LoginContract.View, View.OnClickListener { @@ -27,23 +28,31 @@ class LoginActivity : MifosBaseActivity(), LoginContract.View, View.OnClickListe override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) - setToolbarTitle(getString(R.string.login)) + //setToolbarTitle(getString(R.string.login)) activityComponent.inject(this) loginPresenter.attachView(this) preferencesHelper.clear() //TODO:remove this while implementing API btnLogin.setOnClickListener(this) + create.setOnClickListener(this) } override fun onClick(view: View) { when (view.id) { R.id.btnLogin -> login() + R.id.create -> signup() } } + private fun signup() { + val intent = Intent(this, SignupActivity::class.java) + startActivity(intent) + finish() + } + internal fun login() { val username = etUsername.text.toString().trim() diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/passcode/PasscodeActivity.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/passcode/PasscodeActivity.kt index f04d00e1..023b1318 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/passcode/PasscodeActivity.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/passcode/PasscodeActivity.kt @@ -1,19 +1,47 @@ package org.mifos.mobile.cn.ui.mifos.passcode +import android.Manifest import android.content.DialogInterface import android.content.Intent +import android.os.Bundle import android.view.View import android.widget.Toast import com.mifos.mobile.passcode.MifosPassCodeActivity import com.mifos.mobile.passcode.utils.EncryptionUtil +import com.mifos.mobile.passcode.utils.PasscodePreferencesHelper import org.mifos.mobile.cn.R import org.mifos.mobile.cn.ui.mifos.DashboardActivity import org.mifos.mobile.cn.ui.mifos.login.LoginActivity +import org.mifos.mobile.cn.ui.utils.CheckSelfPermissionAndRequest.checkSelfPermission +import org.mifos.mobile.cn.ui.utils.CheckSelfPermissionAndRequest.requestPermission +import org.mifos.mobile.cn.ui.utils.ConstantKeys import org.mifos.mobile.cn.ui.utils.MaterialDialog import org.mifos.mobile.cn.ui.utils.Toaster class PasscodeActivity : MifosPassCodeActivity(){ + private var currPass = "" + private var updatePassword = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (!checkSelfPermission(this, + Manifest.permission.READ_PHONE_STATE)) { + requestPermission() + } + + } + + private fun requestPermission() { + requestPermission( + this, + Manifest.permission.READ_PHONE_STATE, + ConstantKeys.PERMISSIONS_REQUEST_READ_PHONE_STATE, + resources.getString( + R.string.dialog_message_phone_state_permission_denied_prompt), + resources.getString(R.string.dialog_message_phone_state_permission_never_ask_again), + ConstantKeys.PERMISSIONS_READ_PHONE_STATE_STATUS) + } override fun getLogo(): Int { return R.drawable.mifos_logo_new @@ -49,5 +77,12 @@ class PasscodeActivity : MifosPassCodeActivity(){ override fun getEncryptionType(): Int { return EncryptionUtil.FINERACT_CN } - + override fun onBackPressed() { + super.onBackPressed() + if (updatePassword && !currPass.isEmpty()) { + val helper = PasscodePreferencesHelper(this) + helper.savePassCode(currPass) + } + finish() + } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/recentTransactions/RecentTransactionsFragment.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/recentTransactions/RecentTransactionsFragment.kt index 743a3db2..c0ae1fc3 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/recentTransactions/RecentTransactionsFragment.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/recentTransactions/RecentTransactionsFragment.kt @@ -37,7 +37,6 @@ SwipeRefreshLayout.OnRefreshListener{ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setHasOptionsMenu(true) recentTransactionList = ArrayList() } @@ -62,7 +61,6 @@ SwipeRefreshLayout.OnRefreshListener{ } override fun showUserInterface() { - setToolbarTitle("Recent Transactions") val layoutManager = LinearLayoutManager(activity) layoutManager.orientation = RecyclerView.VERTICAL rvRecentTransactions.layoutManager = layoutManager @@ -86,7 +84,7 @@ SwipeRefreshLayout.OnRefreshListener{ showRecyclerView(false) } - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) inflater?.inflate(R.menu.menu_transactions_search,menu) setUpSearchInterface(menu) diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/settings/SettingsFragment.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/settings/SettingsFragment.kt index 21f01cec..7e6913b1 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/settings/SettingsFragment.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/settings/SettingsFragment.kt @@ -1,13 +1,16 @@ package org.mifos.mobile.cn.ui.mifos.settings -import android.content.Context -import android.net.Uri +import android.content.Intent import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.mifos.mobile.passcode.utils.PasscodePreferencesHelper import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.ui.mifos.passcode.PasscodeActivity +import org.mifos.mobile.cn.ui.utils.ConstantKeys + // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -27,6 +30,7 @@ class SettingsFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_settings, container, false) } diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/signup/SignupActivity.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/signup/SignupActivity.kt new file mode 100644 index 00000000..1ff853f8 --- /dev/null +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/mifos/signup/SignupActivity.kt @@ -0,0 +1,15 @@ +package org.mifos.mobile.cn.ui.mifos.signup + +import android.os.Bundle +import android.view.View +import kotlinx.android.synthetic.main.activity_login.* +import org.mifos.mobile.cn.R +import org.mifos.mobile.cn.ui.base.MifosBaseActivity +import org.mifos.mobile.cn.ui.mifos.login.LoginContract + +class SignupActivity: MifosBaseActivity(){ + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_signup) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/utils/ConstantKeys.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/utils/ConstantKeys.kt index 64fabae7..c77dc580 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/utils/ConstantKeys.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/utils/ConstantKeys.kt @@ -10,7 +10,13 @@ object ConstantKeys { const val REQUEST_PERMISSION_SETTING = 254 const val PERMISSION_REQUEST_ALL = 4 const val PERMISSION_REQUEST_CAMERA = 5 - + const val PASSCODE = "Passcode" + const val CURR_PASSWORD = "currentPassword" + const val ERROR_OCCURRED = "Error occurred" + const val FAILED_TO_WRITE_DATA_TO_QR = "Failed to write data to qr" + const val PERMISSIONS_READ_PHONE_STATE_STATUS = "read_phone_status" + const val PERMISSIONS_REQUEST_READ_PHONE_STATE = 2 + const val UPDATE_PASSWORD_KEY = "updatePassword" const val PERMISSIONS_WRITE_EXTERNAL_STORAGE_STATUS = "write_status" const val PERMISSION_READ_EXTERNAL_STORAGE_STATUS = "read_status" const val PERMISSIONS_CAMERA_STATUS = "camera_status" diff --git a/app/src/main/kotlin/org/mifos/mobile/cn/ui/utils/Utils.kt b/app/src/main/kotlin/org/mifos/mobile/cn/ui/utils/Utils.kt index 8802c272..5e4fa9a0 100644 --- a/app/src/main/kotlin/org/mifos/mobile/cn/ui/utils/Utils.kt +++ b/app/src/main/kotlin/org/mifos/mobile/cn/ui/utils/Utils.kt @@ -1,6 +1,7 @@ package org.mifos.mobile.cn.ui.utils import android.content.Context +import android.content.res.Resources import android.graphics.PorterDuff import android.view.Menu import androidx.core.content.ContextCompat @@ -15,6 +16,13 @@ import java.util.* class Utils { companion object { + fun dpToPx(dp: Int): Int { + return ((dp * Resources.getSystem().displayMetrics.density).toInt()); + } + + fun pxToDp(px: Int, context: Context): Int { + return ((px / Resources.getSystem().displayMetrics.density).toInt()); + } fun getPrecision(aDouble: Double?): String { return String.format(Locale.ENGLISH, "%.2f", aDouble) } diff --git a/app/src/main/res/drawable-v24/arrow.png b/app/src/main/res/drawable-v24/arrow.png new file mode 100644 index 00000000..015dbf8c Binary files /dev/null and b/app/src/main/res/drawable-v24/arrow.png differ diff --git a/app/src/main/res/drawable-v24/circle_arrow.png b/app/src/main/res/drawable-v24/circle_arrow.png new file mode 100644 index 00000000..a04090a9 Binary files /dev/null and b/app/src/main/res/drawable-v24/circle_arrow.png differ diff --git a/app/src/main/res/drawable-v24/drawable_editable_image_hint.xml b/app/src/main/res/drawable-v24/drawable_editable_image_hint.xml new file mode 100644 index 00000000..9790605f --- /dev/null +++ b/app/src/main/res/drawable-v24/drawable_editable_image_hint.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_cam.png b/app/src/main/res/drawable-v24/ic_cam.png new file mode 100644 index 00000000..22a721d6 Binary files /dev/null and b/app/src/main/res/drawable-v24/ic_cam.png differ diff --git a/app/src/main/res/drawable-v24/ic_camera.xml b/app/src/main/res/drawable-v24/ic_camera.xml new file mode 100644 index 00000000..7b9b5cd4 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_camera.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable-v24/ic_error_state.xml b/app/src/main/res/drawable-v24/ic_error_state.xml new file mode 100644 index 00000000..c01af649 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_error_state.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v24/ic_transaction.xml b/app/src/main/res/drawable-v24/ic_transaction.xml new file mode 100644 index 00000000..fad1fe3e --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_transaction.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable-v24/nav_drawer.png b/app/src/main/res/drawable-v24/nav_drawer.png new file mode 100644 index 00000000..fe9a429c Binary files /dev/null and b/app/src/main/res/drawable-v24/nav_drawer.png differ diff --git a/app/src/main/res/drawable-v24/outline_button.png b/app/src/main/res/drawable-v24/outline_button.png new file mode 100644 index 00000000..958105dc Binary files /dev/null and b/app/src/main/res/drawable-v24/outline_button.png differ diff --git a/app/src/main/res/drawable-v24/profile.png b/app/src/main/res/drawable-v24/profile.png new file mode 100644 index 00000000..0dcfeb06 Binary files /dev/null and b/app/src/main/res/drawable-v24/profile.png differ diff --git a/app/src/main/res/drawable-v24/shape_bottom_sheet_dialog_grip.xml b/app/src/main/res/drawable-v24/shape_bottom_sheet_dialog_grip.xml new file mode 100644 index 00000000..792c8361 --- /dev/null +++ b/app/src/main/res/drawable-v24/shape_bottom_sheet_dialog_grip.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/bottomview_selector.xml b/app/src/main/res/drawable/bottomview_selector.xml new file mode 100644 index 00000000..b18401d2 --- /dev/null +++ b/app/src/main/res/drawable/bottomview_selector.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/chip_outline.xml b/app/src/main/res/drawable/chip_outline.xml new file mode 100644 index 00000000..4d772740 --- /dev/null +++ b/app/src/main/res/drawable/chip_outline.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/chip_outline_selected.xml b/app/src/main/res/drawable/chip_outline_selected.xml new file mode 100644 index 00000000..0dae89da --- /dev/null +++ b/app/src/main/res/drawable/chip_outline_selected.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gray_box.xml b/app/src/main/res/drawable/gray_box.xml new file mode 100644 index 00000000..a23f364f --- /dev/null +++ b/app/src/main/res/drawable/gray_box.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/greyborder.xml b/app/src/main/res/drawable/greyborder.xml new file mode 100644 index 00000000..4a9acdf3 --- /dev/null +++ b/app/src/main/res/drawable/greyborder.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_account.xml b/app/src/main/res/drawable/ic_account.xml new file mode 100644 index 00000000..b9d5db60 --- /dev/null +++ b/app/src/main/res/drawable/ic_account.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_add_circle_black_24dp.xml b/app/src/main/res/drawable/ic_add_circle_black_24dp.xml new file mode 100644 index 00000000..be629131 --- /dev/null +++ b/app/src/main/res/drawable/ic_add_circle_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_flash_off.xml b/app/src/main/res/drawable/ic_flash_off.xml new file mode 100644 index 00000000..51b17693 --- /dev/null +++ b/app/src/main/res/drawable/ic_flash_off.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_flash_on.xml b/app/src/main/res/drawable/ic_flash_on.xml new file mode 100644 index 00000000..a3c81cc3 --- /dev/null +++ b/app/src/main/res/drawable/ic_flash_on.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml new file mode 100644 index 00000000..70fb2910 --- /dev/null +++ b/app/src/main/res/drawable/ic_home.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_profile.xml b/app/src/main/res/drawable/ic_profile.xml new file mode 100644 index 00000000..76785806 --- /dev/null +++ b/app/src/main/res/drawable/ic_profile.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_swap_horiz.xml b/app/src/main/res/drawable/ic_swap_horiz.xml new file mode 100644 index 00000000..f9df11df --- /dev/null +++ b/app/src/main/res/drawable/ic_swap_horiz.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/launcher_background.xml b/app/src/main/res/drawable/launcher_background.xml index 03dd5b62..5aca7c3d 100644 --- a/app/src/main/res/drawable/launcher_background.xml +++ b/app/src/main/res/drawable/launcher_background.xml @@ -4,8 +4,7 @@ - diff --git a/app/src/main/res/drawable/login_bg.png b/app/src/main/res/drawable/login_bg.png new file mode 100644 index 00000000..3fbe488e Binary files /dev/null and b/app/src/main/res/drawable/login_bg.png differ diff --git a/app/src/main/res/drawable/mifos_splash_screen_logo.png b/app/src/main/res/drawable/mifos_splash_screen_logo.png new file mode 100644 index 00000000..c1819abf Binary files /dev/null and b/app/src/main/res/drawable/mifos_splash_screen_logo.png differ diff --git a/app/src/main/res/drawable/more_icon.png b/app/src/main/res/drawable/more_icon.png new file mode 100644 index 00000000..82f45f34 Binary files /dev/null and b/app/src/main/res/drawable/more_icon.png differ diff --git a/app/src/main/res/drawable/notification.xml b/app/src/main/res/drawable/notification.xml new file mode 100644 index 00000000..3ebf17e7 --- /dev/null +++ b/app/src/main/res/drawable/notification.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/notification_circle.xml b/app/src/main/res/drawable/notification_circle.xml index f112527e..721e9c80 100644 --- a/app/src/main/res/drawable/notification_circle.xml +++ b/app/src/main/res/drawable/notification_circle.xml @@ -2,7 +2,7 @@ + android:width="35dp" + android:height="35dp" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/pick_contact.png b/app/src/main/res/drawable/pick_contact.png new file mode 100644 index 00000000..0a33393a Binary files /dev/null and b/app/src/main/res/drawable/pick_contact.png differ diff --git a/app/src/main/res/drawable/qrcode.png b/app/src/main/res/drawable/qrcode.png new file mode 100644 index 00000000..c9fef9c3 Binary files /dev/null and b/app/src/main/res/drawable/qrcode.png differ diff --git a/app/src/main/res/drawable/round_gray.xml b/app/src/main/res/drawable/round_gray.xml new file mode 100644 index 00000000..81cf96d7 --- /dev/null +++ b/app/src/main/res/drawable/round_gray.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_shape.xml b/app/src/main/res/drawable/rounded_shape.xml new file mode 100644 index 00000000..1f0b683a --- /dev/null +++ b/app/src/main/res/drawable/rounded_shape.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_tab.xml b/app/src/main/res/drawable/selector_tab.xml new file mode 100644 index 00000000..6cc08e7d --- /dev/null +++ b/app/src/main/res/drawable/selector_tab.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/violet_concave.xml b/app/src/main/res/drawable/violet_concave.xml new file mode 100644 index 00000000..518afafc --- /dev/null +++ b/app/src/main/res/drawable/violet_concave.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/violet_round.xml b/app/src/main/res/drawable/violet_round.xml new file mode 100644 index 00000000..30cca1a2 --- /dev/null +++ b/app/src/main/res/drawable/violet_round.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/white_card.xml b/app/src/main/res/drawable/white_card.xml new file mode 100644 index 00000000..57a1b6f1 --- /dev/null +++ b/app/src/main/res/drawable/white_card.xml @@ -0,0 +1,15 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_amount.xml b/app/src/main/res/layout/activity_amount.xml new file mode 100644 index 00000000..df43bae2 --- /dev/null +++ b/app/src/main/res/layout/activity_amount.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_beneficiaries.xml b/app/src/main/res/layout/activity_beneficiaries.xml new file mode 100644 index 00000000..2be7d0c8 --- /dev/null +++ b/app/src/main/res/layout/activity_beneficiaries.xml @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml new file mode 100644 index 00000000..1377e128 --- /dev/null +++ b/app/src/main/res/layout/activity_home.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_loan_application.xml b/app/src/main/res/layout/activity_loan_application.xml index 20819ace..a59bb838 100644 --- a/app/src/main/res/layout/activity_loan_application.xml +++ b/app/src/main/res/layout/activity_loan_application.xml @@ -1,24 +1,48 @@ + android:layout_width="match_parent" + android:background="@color/violet"> + + - - - + + + + - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 5f9704e1..598de8a6 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -1,98 +1,105 @@ - - + - - - - - - - + android:layout_height="wrap_content" + android:fontFamily="@font/productsans" + android:text="@string/mifos_mobile_cn" + android:textAlignment="center" + android:textColor="@color/white" + android:textSize="@dimen/text_size_35sp" /> + android:layout_marginBottom="@dimen/layout_padding_20dp" + android:backgroundTint="@color/white" + android:textColorHint="@color/white"> - + android:fontFamily="@font/productsans" + android:hint="@string/username" + android:inputType="text" + android:textColor="@color/white" /> + android:layout_height="wrap_content" + android:backgroundTint="@color/white" + android:textColorHint="@color/white" + app:passwordToggleEnabled="true" + app:passwordToggleTint="@color/white"> - + android:fontFamily="@font/productsans" + android:hint="@string/password" + android:inputType="textPassword" + android:textColor="@color/white" + android:textColorHighlight="@color/white" /> -