diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..5fb62c5
--- /dev/null
+++ b/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index d7bfda1..56be5ec 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -11,10 +11,10 @@
+
-
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 2a4d5b5..f0873a2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,29 @@
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 65579f4..f1244ea 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -7,7 +7,7 @@ android {
compileSdk 32
defaultConfig {
- applicationId "org.mifos.mobile.ui"
+ applicationId "org.mifos.mobile.ui.app"
minSdk 22
targetSdk 32
versionCode 1
@@ -23,22 +23,30 @@ android {
}
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
+ jvmTarget = '11'
+ }
+ buildFeatures{
+ viewBinding = true
+ }
+
+ lintOptions{
+ abortOnError false
}
}
dependencies {
+ implementation libraries.androidx_core
+ implementation libraries.app_compat
+ implementation libraries.material
+ implementation libraries.constraint_layout
+ implementation libraries.navigation_component
+
+ testImplementation libraries.junit
+ androidTestImplementation libraries.android_tests
- implementation 'androidx.core:core-ktx:1.7.0'
- implementation 'androidx.appcompat:appcompat:1.4.1'
- implementation 'com.google.android.material:material:1.5.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation project(path: ':uihouse')
- testImplementation 'junit:junit:4.13.2'
- androidTestImplementation 'androidx.test.ext:junit:1.1.3'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a4614e7..f14cfaa 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+ package="org.mifos.mobile.ui.app">
SampleCardBinding.inflate(LayoutInflater.from(parent.context), parent, false) },
+ itemOnClick = {
+ val dialog = MaterialAlertDialogBuilder(requireContext())
+ .setTitle("Sample Dialog")
+ .setIcon(R.drawable.ic_launcher_foreground)
+ .setMessage("This is some random long dialog message. Just to show you how it looks")
+ .setPositiveButton("OK"){ dialogInterface, _ -> dialogInterface.dismiss() }
+ .setNegativeButton("Cancel"){ dialogInterface, _ -> dialogInterface.dismiss() }
+ .show()
+ },
+ itemOnLongClick = lambda@{
+ val snackBar = Snackbar.make(binding.root, "Item Long Clicked position $it", Snackbar.LENGTH_SHORT)
+ snackBar.setAction("OK"){ snackBar.dismiss() }
+ snackBar.show()
+ return@lambda true
+ }
+ )
+
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mifos/mobile/ui/app/SecondFragment.kt b/app/src/main/java/org/mifos/mobile/ui/app/SecondFragment.kt
new file mode 100644
index 0000000..0df8459
--- /dev/null
+++ b/app/src/main/java/org/mifos/mobile/ui/app/SecondFragment.kt
@@ -0,0 +1,32 @@
+package org.mifos.mobile.ui.app
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.google.android.material.textfield.MaterialAutoCompleteTextView
+import org.mifos.mobile.ui.app.R
+import org.mifos.mobile.ui.app.databinding.FragmentSecondBinding
+
+class SecondFragment : Fragment() {
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val binding = FragmentSecondBinding.inflate(inflater, container, false)
+
+ binding.topAppBar.setOnMenuItemClickListener {
+ if(it.itemId == R.id.editTheme)
+ requireContext().switchNightMode()
+ true
+ }
+
+ (binding.dropDown.editText as? MaterialAutoCompleteTextView)?.setSimpleItems(
+ arrayOf("Item 1", "Item 2", "Item 3", "Item 4")
+ )
+
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mifos/mobile/ui/app/ThirdFragment.kt b/app/src/main/java/org/mifos/mobile/ui/app/ThirdFragment.kt
new file mode 100644
index 0000000..d9cb077
--- /dev/null
+++ b/app/src/main/java/org/mifos/mobile/ui/app/ThirdFragment.kt
@@ -0,0 +1,61 @@
+package org.mifos.mobile.ui.app
+
+import android.app.DatePickerDialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.DatePicker
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import com.google.android.material.datepicker.CalendarConstraints
+import com.google.android.material.datepicker.DateValidatorPointForward
+import com.google.android.material.datepicker.MaterialDatePicker
+import org.mifos.mobile.ui.app.databinding.FragmentThirdBinding
+import java.time.LocalDate
+import java.time.Year
+import java.util.*
+
+class ThirdFragment : Fragment() {
+
+ var previouslySelectedDate: Long? = null
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val binding = FragmentThirdBinding.inflate(inflater, container, false)
+
+ binding.apply {
+ profileImage.setOnImageChangeButtonListener{
+ Toast.makeText(requireContext(), "Image change button clicked", Toast.LENGTH_SHORT).show()
+ }
+
+ dateDialogTrigger.setOnClickListener {
+ val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
+ calendar.timeInMillis = MaterialDatePicker.todayInUtcMilliseconds()
+ val startConst = calendar.timeInMillis
+ calendar[Calendar.DAY_OF_MONTH] = calendar[Calendar.DAY_OF_MONTH] + 10
+ val endConst = calendar.timeInMillis
+
+ val constraints: CalendarConstraints = CalendarConstraints.Builder()
+ .setOpenAt(previouslySelectedDate ?: MaterialDatePicker.todayInUtcMilliseconds())
+ .setValidator(DateValidatorPointForward.now())
+ .build()
+
+ val dialog = MaterialDatePicker
+ .Builder
+ .datePicker()
+ .setCalendarConstraints(constraints)
+ .setTitleText("Select payment date")
+ .setSelection(previouslySelectedDate ?: MaterialDatePicker.todayInUtcMilliseconds())
+ .build()
+ dialog.addOnPositiveButtonClickListener { previouslySelectedDate = it }
+ dialog.show(parentFragmentManager, "DATE_PICKER")
+ }
+ }
+
+ return binding.root
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mifos/mobile/ui/app/util/GenericRecyclerViewAdapter.kt b/app/src/main/java/org/mifos/mobile/ui/app/util/GenericRecyclerViewAdapter.kt
new file mode 100644
index 0000000..4c14865
--- /dev/null
+++ b/app/src/main/java/org/mifos/mobile/ui/app/util/GenericRecyclerViewAdapter.kt
@@ -0,0 +1,51 @@
+package org.mifos.mobile.ui.app.util
+
+import android.annotation.SuppressLint
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+
+
+class GenericRecyclerViewAdapter(
+ items: List,
+ itemComparator:(oldItem: T, newItem: T) -> Boolean = { a,b -> a == b},
+ private val bindingCreator: (parent: ViewGroup) -> VBType,
+ private val bindItemWithHolder: (item: T, binding: VBType) -> Unit = {_,_ ->},
+ private val itemOnClick: (item: T) -> Unit = {},
+ private val itemOnLongClick: (item: T) -> Boolean = {false}
+) : ListAdapter(diffCallBackBy(itemComparator)) {
+
+ init {
+ submitList(items)
+ }
+
+ class GenericViewHolder(val binding: ViewBinding) : RecyclerView.ViewHolder(binding.root)
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenericViewHolder {
+ val binding = bindingCreator(parent)
+ val viewHolder = GenericViewHolder(binding)
+ binding.root.setOnClickListener { itemOnClick(getItem(viewHolder.adapterPosition)) }
+ binding.root.setOnLongClickListener { itemOnLongClick(getItem(viewHolder.adapterPosition)) }
+ return viewHolder
+ }
+
+
+ @Suppress("UNCHECKED_CAST")
+ override fun onBindViewHolder(viewHolder: GenericViewHolder, position: Int)
+ = bindItemWithHolder(getItem(position) as T, viewHolder.binding as VBType)
+
+
+}
+
+
+fun diffCallBackBy(comparator: (oldItem: T, newItem: T) -> Boolean) = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: T, newItem: T)
+ = comparator(oldItem, newItem)
+
+ @SuppressLint("DiffUtilEquals")
+ override fun areContentsTheSame(oldItem: T, newItem: T)
+ = (oldItem == newItem)
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_account_balance_wallet_24.xml b/app/src/main/res/drawable/ic_baseline_account_balance_wallet_24.xml
new file mode 100644
index 0000000..ebed1ce
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_account_balance_wallet_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_account_circle_24.xml b/app/src/main/res/drawable/ic_baseline_account_circle_24.xml
new file mode 100644
index 0000000..93ce250
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_account_circle_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_dark_mode_24.xml b/app/src/main/res/drawable/ic_baseline_dark_mode_24.xml
new file mode 100644
index 0000000..3b52248
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_dark_mode_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_home_24.xml b/app/src/main/res/drawable/ic_baseline_home_24.xml
new file mode 100644
index 0000000..5a870f5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_home_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
index 07d5da9..bb45125 100644
--- a/app/src/main/res/drawable/ic_launcher_background.xml
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -5,166 +5,6 @@
android:viewportWidth="108"
android:viewportHeight="108">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/sample_user_image.xml b/app/src/main/res/drawable/sample_user_image.xml
new file mode 100644
index 0000000..6ead84f
--- /dev/null
+++ b/app/src/main/res/drawable/sample_user_image.xml
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 4fc2444..566e040 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,13 +6,23 @@
android:layout_height="match_parent"
tools:context=".MainActivity">
-
+
+
+ app:menu="@menu/bottom_nav_destinations"/>
\ No newline at end of file
diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml
new file mode 100644
index 0000000..590408d
--- /dev/null
+++ b/app/src/main/res/layout/drawer_header.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_main_screen.xml b/app/src/main/res/layout/fragment_main_screen.xml
new file mode 100644
index 0000000..439d650
--- /dev/null
+++ b/app/src/main/res/layout/fragment_main_screen.xml
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_second.xml b/app/src/main/res/layout/fragment_second.xml
new file mode 100644
index 0000000..e47e82d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_second.xml
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_third.xml b/app/src/main/res/layout/fragment_third.xml
new file mode 100644
index 0000000..3d46b35
--- /dev/null
+++ b/app/src/main/res/layout/fragment_third.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/sample_card.xml b/app/src/main/res/layout/sample_card.xml
new file mode 100644
index 0000000..bb67956
--- /dev/null
+++ b/app/src/main/res/layout/sample_card.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/app_bar_actions.xml b/app/src/main/res/menu/app_bar_actions.xml
new file mode 100644
index 0000000..4925a51
--- /dev/null
+++ b/app/src/main/res/menu/app_bar_actions.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/bottom_nav_destinations.xml b/app/src/main/res/menu/bottom_nav_destinations.xml
new file mode 100644
index 0000000..ff9cadc
--- /dev/null
+++ b/app/src/main/res/menu/bottom_nav_destinations.xml
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/main_bottom_nav_graph.xml b/app/src/main/res/navigation/main_bottom_nav_graph.xml
new file mode 100644
index 0000000..fa51777
--- /dev/null
+++ b/app/src/main/res/navigation/main_bottom_nav_graph.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index 5e4cfff..0000000
--- a/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
deleted file mode 100644
index f8c6127..0000000
--- a/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- #FFBB86FC
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
-
\ 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 2681861..8cd7d4c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,9 @@
- mifos-ui-library
+ Mifos UI Library
+
+ Fragment 1
+ Fragment 2
+ Fragment 3
+
+ Hello blank fragment
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 3b49687..fbdcfca 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,16 +1,4 @@
-
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 0ccd671..20efaaf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,10 +1,48 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id 'com.android.application' version '7.1.2' apply false
- id 'com.android.library' version '7.1.2' apply false
- id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+ id 'com.android.application' version '7.2.0' apply false
+ id 'com.android.library' version '7.2.0' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
}
task clean(type: Delete) {
delete rootProject.buildDir
-}
\ No newline at end of file
+}
+
+
+
+ext.versions = [
+ ui_lib: "1.0.0-alpha1",
+ androidx_core: "1.8.0",
+ app_compat: "1.4.2",
+ material: "1.6.1",
+ constraint_layout: "2.1.4",
+ android_lint_tool: "30.2.0",
+ navigation_component: "2.4.2",
+ country_code_picker: "2.4.7",
+ lib_phone_number: "8.12.50"
+]
+
+ext.libraries = [
+ androidx_core: "androidx.core:core-ktx:$versions.androidx_core",
+ app_compat: "androidx.appcompat:appcompat:$versions.app_compat",
+ material: "com.google.android.material:material:$versions.material",
+ constraint_layout: "androidx.constraintlayout:constraintlayout:$versions.constraint_layout",
+
+ junit: 'junit:junit:4.13.2',
+ android_tests: [
+ 'androidx.test.ext:junit:1.1.3',
+ 'androidx.test.espresso:espresso-core:3.4.0'
+ ],
+
+ android_lint_tool: [
+ "com.android.tools.lint:lint-api:$versions.android_lint_tool",
+ "com.android.tools.lint:lint-checks:$versions.android_lint_tool"
+ ],
+ navigation_component: [
+ "androidx.navigation:navigation-fragment-ktx:$versions.navigation_component",
+ "androidx.navigation:navigation-ui-ktx:$versions.navigation_component"
+ ],
+ country_code_picker: "com.hbb20:ccp:$versions.country_code_picker",
+ lib_phone_number: "io.michaelrocks:libphonenumber-android:$versions.lib_phone_number"
+]
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index cd0519b..3d754b3 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,6 +15,9 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
+# automatically migrates existing third-party libraries to use
+# AndroidX dependencies by rewriting their binaries
+android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3d8d1b2..dd7009b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Mar 16 19:55:13 IST 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/jitpack.yml b/jitpack.yml
new file mode 100644
index 0000000..46c8529
--- /dev/null
+++ b/jitpack.yml
@@ -0,0 +1,2 @@
+jdk:
+ - openjdk11
\ No newline at end of file
diff --git a/lint/.gitignore b/lint/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/lint/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/lint/build.gradle b/lint/build.gradle
new file mode 100644
index 0000000..ba22071
--- /dev/null
+++ b/lint/build.gradle
@@ -0,0 +1,29 @@
+plugins {
+ id 'java-library'
+ id 'kotlin'
+ id 'maven-publish'
+}
+
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ from components.java
+ }
+ }
+ }
+}
+dependencies{
+ compileOnly libraries.android_lint_tool
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+}
+
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+}
\ No newline at end of file
diff --git a/lint/gradle.properties b/lint/gradle.properties
new file mode 100644
index 0000000..0d6aa7b
--- /dev/null
+++ b/lint/gradle.properties
@@ -0,0 +1 @@
+kotlin.stdlib.default.dependency=false
diff --git a/lint/src/main/java/org/mifos/mobile/ui/lint/Common.kt b/lint/src/main/java/org/mifos/mobile/ui/lint/Common.kt
new file mode 100644
index 0000000..ef04482
--- /dev/null
+++ b/lint/src/main/java/org/mifos/mobile/ui/lint/Common.kt
@@ -0,0 +1,22 @@
+@file:Suppress("UnstableApiUsage")
+
+package org.mifos.mobile.ui.lint
+
+import com.android.tools.lint.detector.api.*
+
+val libraryLintCategory = Category.create("UI library lint issues", 7)
+
+fun createIssue(
+ id: String,
+ briefDescription: String,
+ explanation: String,
+ implementation: Implementation
+) = Issue.create(
+ id = id,
+ briefDescription = briefDescription,
+ explanation = explanation,
+ category = libraryLintCategory,
+ priority = 7,
+ severity = Severity.FATAL,
+ implementation = implementation
+)
\ No newline at end of file
diff --git a/lint/src/main/java/org/mifos/mobile/ui/lint/LintRegistry.kt b/lint/src/main/java/org/mifos/mobile/ui/lint/LintRegistry.kt
new file mode 100644
index 0000000..ac5adba
--- /dev/null
+++ b/lint/src/main/java/org/mifos/mobile/ui/lint/LintRegistry.kt
@@ -0,0 +1,27 @@
+package org.mifos.mobile.ui.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.android.tools.lint.detector.api.Issue
+import org.mifos.mobile.ui.lint.NonLibraryStyleDetector.Companion.nonLibraryStyleIssue
+import org.mifos.mobile.ui.lint.SizeTokenDetector.Companion.sizeTokenIssue
+import org.mifos.mobile.ui.lint.SpacingTokenDetector.Companion.spacingTokenIssue
+import org.mifos.mobile.ui.lint.StyleAttributeUsageIssueDetector.Companion.styleAttributeUsageIssue
+
+
+@Suppress("UnstableApiUsage")
+class LintRegistry : IssueRegistry() {
+
+ override val api: Int = CURRENT_API
+
+ override val issues: List
+ get() = listOf(
+ styleAttributeUsageIssue,
+ spacingTokenIssue,
+ sizeTokenIssue,
+ nonLibraryStyleIssue
+ )
+ override val vendor: Vendor
+ get() = Vendor(vendorName = "Mifos", identifier = "mifos ui library")
+}
\ No newline at end of file
diff --git a/lint/src/main/java/org/mifos/mobile/ui/lint/NonLibraryStyleDetector.kt b/lint/src/main/java/org/mifos/mobile/ui/lint/NonLibraryStyleDetector.kt
new file mode 100644
index 0000000..9f2789b
--- /dev/null
+++ b/lint/src/main/java/org/mifos/mobile/ui/lint/NonLibraryStyleDetector.kt
@@ -0,0 +1,55 @@
+package org.mifos.mobile.ui.lint
+
+import com.android.SdkConstants
+import com.android.resources.ResourceFolderType
+import com.android.tools.lint.detector.api.*
+import org.w3c.dom.Attr
+
+@Suppress("UnstableApiUsage")
+class NonLibraryStyleDetector : LayoutDetector() {
+ companion object {
+ private const val MESSAGE =
+ "Styles should only be applied from the ui library"
+
+ val nonLibraryStyleIssue = createIssue(
+ id = "NonLibraryStyleIssue",
+ briefDescription = "Prohibits usage of styles" +
+ " other ui library styles",
+ explanation = "Styles outside of " +
+ "UI library should not be used.",
+ implementation = Implementation(
+ NonLibraryStyleDetector::class.java,
+ Scope.RESOURCE_FILE_SCOPE
+ )
+ )
+ }
+
+ override fun appliesTo(folderType: ResourceFolderType) = ResourceFolderType.LAYOUT == folderType
+
+ override fun getApplicableElements(): MutableList = XmlScannerConstants.ALL
+
+ override fun getApplicableAttributes() = listOf(
+ SdkConstants.ATTR_STYLE
+ )
+
+ override fun visitAttribute(context: XmlContext, attribute: Attr) {
+ val allowedPrefix = listOf(
+ "@style/Mifos.DesignSystem.TextStyle.",
+ "@style/Mifos.DesignSystem.Components."
+ )
+ attribute.nodeValue.let { attrValue ->
+ if (attrValue != SdkConstants.VALUE_WRAP_CONTENT
+ && attrValue != SdkConstants.VALUE_MATCH_PARENT
+ && attrValue != SdkConstants.VALUE_ZERO_DP
+ && allowedPrefix.none { attrValue.startsWith(it) }
+ ) {
+ context.report(
+ nonLibraryStyleIssue,
+ attribute,
+ context.getLocation(attribute),
+ MESSAGE
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/lint/src/main/java/org/mifos/mobile/ui/lint/SizeTokenDetector.kt b/lint/src/main/java/org/mifos/mobile/ui/lint/SizeTokenDetector.kt
new file mode 100644
index 0000000..325a05d
--- /dev/null
+++ b/lint/src/main/java/org/mifos/mobile/ui/lint/SizeTokenDetector.kt
@@ -0,0 +1,56 @@
+package org.mifos.mobile.ui.lint
+
+import com.android.SdkConstants
+import com.android.resources.ResourceFolderType
+import com.android.tools.lint.detector.api.*
+import org.w3c.dom.Attr
+
+
+@Suppress("UnstableApiUsage")
+class SizeTokenDetector : LayoutDetector() {
+ companion object {
+ private const val MESSAGE =
+ "Hardcoded size values should not be used. Replace this with Ui library size token i.e. Mifos.DesignSystem.Size.*"
+
+ val sizeTokenIssue = createIssue(
+ id = "SizeTokenIssue",
+ briefDescription = "Prohibits usage of size value" +
+ " other than size tokens.",
+ explanation = "Size outside of " +
+ "UI library size tokens should not be used.",
+ implementation = Implementation(
+ SizeTokenDetector::class.java,
+ Scope.RESOURCE_FILE_SCOPE
+ )
+ )
+ }
+
+ override fun appliesTo(folderType: ResourceFolderType) = ResourceFolderType.LAYOUT == folderType
+
+ override fun getApplicableElements(): MutableList = XmlScannerConstants.ALL
+
+ override fun getApplicableAttributes() = listOf(
+ SdkConstants.ATTR_LAYOUT_WIDTH,
+ SdkConstants.ATTR_LAYOUT_HEIGHT,
+ SdkConstants.ATTR_MIN_WIDTH,
+ SdkConstants.ATTR_MIN_HEIGHT,
+ SdkConstants.ATTR_LAYOUT_MIN_HEIGHT,
+ SdkConstants.ATTR_LAYOUT_MIN_WIDTH,
+ SdkConstants.ATTR_MAX_WIDTH,
+ SdkConstants.ATTR_MAX_HEIGHT,
+ SdkConstants.ATTR_LAYOUT_MAX_HEIGHT,
+ SdkConstants.ATTR_LAYOUT_MAX_WIDTH
+ )
+
+ override fun visitAttribute(context: XmlContext, attribute: Attr) {
+ val allowedPrefix = "@dimen/Mifos.DesignSystem.Size."
+ attribute.nodeValue.let { attrValue ->
+ if(attrValue != SdkConstants.VALUE_WRAP_CONTENT
+ && attrValue != SdkConstants.VALUE_MATCH_PARENT
+ && attrValue != SdkConstants.VALUE_ZERO_DP
+ && !attrValue.startsWith(allowedPrefix)){
+ context.report(sizeTokenIssue, attribute, context.getLocation(attribute), MESSAGE)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/lint/src/main/java/org/mifos/mobile/ui/lint/SpacingTokenDetector.kt b/lint/src/main/java/org/mifos/mobile/ui/lint/SpacingTokenDetector.kt
new file mode 100644
index 0000000..dad1634
--- /dev/null
+++ b/lint/src/main/java/org/mifos/mobile/ui/lint/SpacingTokenDetector.kt
@@ -0,0 +1,60 @@
+package org.mifos.mobile.ui.lint
+
+import com.android.SdkConstants
+import com.android.resources.ResourceFolderType
+import com.android.tools.lint.detector.api.*
+import org.w3c.dom.Attr
+
+@Suppress("UnstableApiUsage")
+class SpacingTokenDetector : LayoutDetector() {
+ companion object {
+ private const val MESSAGE =
+ "Hardcoded spacings should not be used. Replace this with Ui library spacing token i.e. Mifos.DesignSystem.Spacing.*"
+
+ val spacingTokenIssue = createIssue(
+ id = "SpacingTokenIssue",
+ briefDescription = "Prohibits usage of spacing (i.e.,margins and paddings)" +
+ " other than spacing tokens.",
+ explanation = "Spacings (i.e.,margins and paddings) outside of " +
+ "UI library spacing tokens should not be used.",
+ implementation = Implementation(
+ SpacingTokenDetector::class.java,
+ Scope.RESOURCE_FILE_SCOPE
+ )
+ )
+ }
+
+ override fun appliesTo(folderType: ResourceFolderType) = ResourceFolderType.LAYOUT == folderType
+
+ override fun getApplicableElements(): MutableList = XmlScannerConstants.ALL
+
+ override fun getApplicableAttributes() = listOf(
+ //margins
+ SdkConstants.ATTR_LAYOUT_MARGIN,
+ SdkConstants.ATTR_LAYOUT_MARGIN_LEFT,
+ SdkConstants.ATTR_LAYOUT_MARGIN_RIGHT,
+ SdkConstants.ATTR_LAYOUT_MARGIN_START,
+ SdkConstants.ATTR_LAYOUT_MARGIN_END,
+ SdkConstants.ATTR_LAYOUT_MARGIN_TOP,
+ SdkConstants.ATTR_LAYOUT_MARGIN_BOTTOM,
+ SdkConstants.ATTR_LAYOUT_MARGIN_HORIZONTAL,
+ SdkConstants.ATTR_LAYOUT_MARGIN_VERTICAL,
+ //padding
+ SdkConstants.ATTR_PADDING,
+ SdkConstants.ATTR_PADDING_BOTTOM,
+ SdkConstants.ATTR_PADDING_TOP,
+ SdkConstants.ATTR_PADDING_RIGHT,
+ SdkConstants.ATTR_PADDING_LEFT,
+ SdkConstants.ATTR_PADDING_START,
+ SdkConstants.ATTR_PADDING_END,
+ SdkConstants.ATTR_PADDING_HORIZONTAL,
+ SdkConstants.ATTR_PADDING_VERTICAL
+ )
+
+ override fun visitAttribute(context: XmlContext, attribute: Attr) {
+ val allowedPrefix = "@dimen/Mifos.DesignSystem.Spacing."
+ if(attribute.nodeValue != SdkConstants.VALUE_ZERO_DP && !attribute.nodeValue.startsWith(allowedPrefix)){
+ context.report(spacingTokenIssue, attribute, context.getLocation(attribute), MESSAGE)
+ }
+ }
+}
\ No newline at end of file
diff --git a/lint/src/main/java/org/mifos/mobile/ui/lint/StyleAttributeUsageIssueDetector.kt b/lint/src/main/java/org/mifos/mobile/ui/lint/StyleAttributeUsageIssueDetector.kt
new file mode 100644
index 0000000..95773c8
--- /dev/null
+++ b/lint/src/main/java/org/mifos/mobile/ui/lint/StyleAttributeUsageIssueDetector.kt
@@ -0,0 +1,74 @@
+package org.mifos.mobile.ui.lint
+
+import com.android.SdkConstants
+import com.android.resources.ResourceFolderType
+import com.android.tools.lint.detector.api.*
+import org.w3c.dom.Attr
+
+@Suppress("UnstableApiUsage")
+class StyleAttributeUsageIssueDetector : ResourceXmlDetector() {
+ companion object {
+ const val MESSAGE = "Style attribute usage in layout detected"
+ val styleAttributeUsageIssue = createIssue(
+ id = "StyleAttributeUsageIssue",
+ briefDescription = "Usage of style attribute. Use theme styles instead",
+ explanation = """
+ For consistency of UI, changing Views' style attributes in layout files is discouraged.
+ Click here to learn more: https://sample.doc.link.com
+ """.trimIndent(),
+ implementation = Implementation(
+ StyleAttributeUsageIssueDetector::class.java,
+ Scope.ALL_RESOURCES_SCOPE
+ )
+ )
+ }
+
+ override fun appliesTo(folderType: ResourceFolderType) = ResourceFolderType.LAYOUT == folderType
+
+ override fun getApplicableElements(): MutableList = XmlScannerConstants.ALL
+
+ override fun getApplicableAttributes() = listOf(
+ //Text attributes
+ SdkConstants.ATTR_TEXT_SIZE,
+ SdkConstants.ATTR_TEXT_COLOR,
+ SdkConstants.ATTR_TEXT_STYLE,
+ //Color attributes
+ SdkConstants.ATTR_TINT,
+ SdkConstants.ATTR_BACKGROUND_TINT,
+ SdkConstants.ATTR_ICON_TINT,
+ SdkConstants.ATTR_BUTTON_TINT,
+ SdkConstants.ATTR_DRAWABLE_TINT,
+ SdkConstants.ATTR_FOREGROUND_TINT,
+ SdkConstants.ATTR_PROGRESS_TINT,
+ SdkConstants.ATTR_INDETERMINATE_TINT,
+ SdkConstants.ATTR_END_ICON_TINT,
+ SdkConstants.ATTR_START_ICON_TINT,
+ SdkConstants.ATTR_TAB_ICON_TINT,
+ SdkConstants.ATTR_TAB_ICON_TINT,
+ SdkConstants.ATTR_CHECK_MARK_TINT,
+ SdkConstants.ATTR_SECONDARY_PROGRESS_TINT,
+ SdkConstants.ATTR_PASSWORD_TOGGLE_TINT,
+ SdkConstants.ATTR_CHIP_ICON_TINT,
+ SdkConstants.ATTR_CLOSE_ICON,
+ SdkConstants.ATTR_ERROR_ICON_TINT,
+ SdkConstants.ATTR_ERROR_TEXT_COLOR,
+ //card attributes
+ SdkConstants.ATTR_ELEVATION,
+ SdkConstants.ATTR_CARD_ELEVATION,
+ SdkConstants.ATTR_CORNER_RADIUS,
+ SdkConstants.ATTR_CARD_CORNER_RADIUS,
+ //other attributes
+ SdkConstants.ATTR_FONT,
+ SdkConstants.ATTR_FONT_STYLE,
+ SdkConstants.ATTR_FONT_FAMILY,
+ SdkConstants.ATTR_TEXT_STYLE,
+ )
+
+
+ override fun visitAttribute(context: XmlContext, attribute: Attr) {
+ val incident = Incident(context, styleAttributeUsageIssue)
+ .message(MESSAGE)
+ .at(attribute)
+ context.report(incident)
+ }
+}
\ No newline at end of file
diff --git a/lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry b/lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
new file mode 100644
index 0000000..e239f9c
--- /dev/null
+++ b/lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
@@ -0,0 +1 @@
+org.mifos.mobile.ui.lint.LintRegistry
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index cdf4bbe..a75e7ba 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -15,3 +15,4 @@ dependencyResolutionManagement {
rootProject.name = "mifos-ui-library"
include ':app'
include ':uihouse'
+include 'lint'
\ No newline at end of file
diff --git a/uihouse/build.gradle b/uihouse/build.gradle
index 7f9fbb0..fdf8c68 100644
--- a/uihouse/build.gradle
+++ b/uihouse/build.gradle
@@ -1,9 +1,21 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
+ id 'maven-publish'
+}
+
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ from components.release
+ }
+ }
+ }
}
android {
+
compileSdk 32
defaultConfig {
@@ -21,20 +33,22 @@ android {
}
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
+ jvmTarget = '11'
}
}
dependencies {
- implementation 'androidx.core:core-ktx:1.7.0'
- implementation 'androidx.appcompat:appcompat:1.4.1'
- implementation 'com.google.android.material:material:1.5.0'
- testImplementation 'junit:junit:4.13.2'
- androidTestImplementation 'androidx.test.ext:junit:1.1.3'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation libraries.androidx_core
+ implementation libraries.app_compat
+ implementation libraries.material
+ implementation libraries.country_code_picker
+ implementation libraries.lib_phone_number
+
+ testImplementation libraries.junit
+ androidTestImplementation libraries.android_tests
}
\ No newline at end of file
diff --git a/uihouse/src/main/java/org/mifos/mobile/ui/ColorUtils.kt b/uihouse/src/main/java/org/mifos/mobile/ui/ColorUtils.kt
new file mode 100644
index 0000000..c7681c6
--- /dev/null
+++ b/uihouse/src/main/java/org/mifos/mobile/ui/ColorUtils.kt
@@ -0,0 +1,23 @@
+package org.mifos.mobile.ui
+
+import android.app.Activity
+import android.content.Context
+import android.util.TypedValue
+import androidx.annotation.AttrRes
+import androidx.annotation.ColorInt
+import androidx.core.graphics.ColorUtils
+import androidx.core.view.WindowCompat
+
+fun Activity.setStatusBarColor(@ColorInt color: Int){
+ if(!window.decorView.isInEditMode){
+ window.statusBarColor = color
+ WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = ColorUtils.calculateLuminance(color) > 0.5
+ }
+}
+
+@ColorInt
+fun Context.getThemeAttributeColor(@AttrRes colorAttribute: Int): Int{
+ val typedValue = TypedValue()
+ theme.resolveAttribute(colorAttribute, typedValue, true)
+ return typedValue.data
+}
\ No newline at end of file
diff --git a/uihouse/src/main/java/org/mifos/mobile/ui/DimenUtils.kt b/uihouse/src/main/java/org/mifos/mobile/ui/DimenUtils.kt
new file mode 100644
index 0000000..c0248a5
--- /dev/null
+++ b/uihouse/src/main/java/org/mifos/mobile/ui/DimenUtils.kt
@@ -0,0 +1,13 @@
+package org.mifos.mobile.ui
+
+import android.content.res.Resources
+import android.util.TypedValue
+
+/**
+ * Given value in dp is converted in actual pixel value
+ */
+val Number.dp get() = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ this.toFloat(),
+ Resources.getSystem().displayMetrics
+)
diff --git a/uihouse/src/main/java/org/mifos/mobile/ui/GridSpacingItemDecoration.kt b/uihouse/src/main/java/org/mifos/mobile/ui/GridSpacingItemDecoration.kt
new file mode 100644
index 0000000..1421a14
--- /dev/null
+++ b/uihouse/src/main/java/org/mifos/mobile/ui/GridSpacingItemDecoration.kt
@@ -0,0 +1,39 @@
+package org.mifos.mobile.ui
+
+import android.graphics.Rect
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+class GridSpacingItemDecoration(
+ private val spanCount: Int,
+ private val spacing: Int,
+ private val includeEdge: Boolean
+) : RecyclerView.ItemDecoration() {
+
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ val position: Int = parent.getChildAdapterPosition(view) // item position
+ val column = position % spanCount // item column
+ if (includeEdge) {
+ outRect.left =
+ spacing - column * spacing / spanCount // spacing - column * ((1f / spanCount) * spacing)
+ outRect.right =
+ (column + 1) * spacing / spanCount // (column + 1) * ((1f / spanCount) * spacing)
+ if (position < spanCount) { // top edge
+ outRect.top = spacing
+ }
+ outRect.bottom = spacing // item bottom
+ } else {
+ outRect.left = column * spacing / spanCount // column * ((1f / spanCount) * spacing)
+ outRect.right =
+ spacing - (column + 1) * spacing / spanCount // spacing - (column + 1) * ((1f / spanCount) * spacing)
+ if (position >= spanCount) {
+ outRect.top = spacing // item top
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uihouse/src/main/java/org/mifos/mobile/ui/ProfileImageView.kt b/uihouse/src/main/java/org/mifos/mobile/ui/ProfileImageView.kt
new file mode 100644
index 0000000..20fba0e
--- /dev/null
+++ b/uihouse/src/main/java/org/mifos/mobile/ui/ProfileImageView.kt
@@ -0,0 +1,137 @@
+package org.mifos.mobile.ui
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.TypedArray
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.FrameLayout
+import androidx.annotation.*
+import androidx.appcompat.widget.AppCompatImageView
+import androidx.core.content.ContextCompat
+import androidx.core.view.setPadding
+import com.google.android.material.card.MaterialCardView
+import com.google.android.material.imageview.ShapeableImageView
+import com.google.android.material.shape.ShapeAppearanceModel
+import org.mifos.mobile.uihouse.R
+
+class ProfileImageView @JvmOverloads constructor(
+ context: Context,
+ AttributeSet: AttributeSet? = null,
+ defStyleAttr: Int = R.attr.profileImageViewStyle
+) : FrameLayout(context, AttributeSet, defStyleAttr) {
+
+ private val userImage by lazy { findViewById(R.id.user_image_drawable) }
+ private val userImageChangeButton by lazy { findViewById(R.id.image_change_button) }
+ private val userImageChangeButtonImage by lazy { findViewById(R.id.image_change_button_image) }
+
+ init {
+ LayoutInflater.from(getContext()).inflate(R.layout.profile_image, this, true)
+ val attributeArray: TypedArray = context.theme.obtainStyledAttributes(
+ AttributeSet,
+ R.styleable.profileImageStyleable, defStyleAttr, 0
+ )
+ attributeArray.run {
+ val userImageRes = getResourceIdOrNull(R.styleable.profileImageStyleable_userImage)
+ val strokeColor = getColorOrNull(R.styleable.profileImageStyleable_strokeColor)
+ val strokeWidth = getDimensionOrNull(R.styleable.profileImageStyleable_strokeWidth)
+ val shapeAppearance = getResourceIdOrNull(R.styleable.profileImageStyleable_shapeAppearance)
+ val imageChangeButtonSize = getDimensionOrNull(R.styleable.profileImageStyleable_imageChangeButtonSize)
+ val imageChangeButtonDrawable = getResourceIdOrNull(R.styleable.profileImageStyleable_imageChangeButtonSrc)
+ val imageChangeButtonBottomEndMargin = getDimensionOrNull(R.styleable.profileImageStyleable_imageChangeButtonBottomEndPadding)
+ val imageChangeButtonInnerPadding = getDimensionOrNull(R.styleable.profileImageStyleable_imageChangeButtonInnerPadding)
+
+ userImageRes?.let { setUserImage(it) }
+ strokeColor?.let { setStrokeColorInt(it) }
+ strokeWidth?.let { setStrokeWidth(it) }
+ shapeAppearance?.let { setShapeAppearance(it) }
+ imageChangeButtonSize?.toInt()?.let { setImageChangeButtonSize(it) }
+ imageChangeButtonDrawable?.let { setImageChangeButtonDrawable(it) }
+ imageChangeButtonBottomEndMargin?.toInt()?.let { setImageChangeButtonBottomEndMargin(it) }
+ imageChangeButtonInnerPadding?.toInt()?.let { setImageChangeButtonInnerPadding(it) }
+
+ recycle()
+ }
+
+ }
+
+
+
+
+ fun setStrokeColorRes(@ColorRes resId: Int){
+ userImage.strokeColor = ColorStateList.valueOf(ContextCompat.getColor(context, resId))
+ }
+ fun setStrokeColorInt(@ColorInt strokeColor: Int){
+ userImage.strokeColor = ColorStateList.valueOf(strokeColor)
+ }
+ fun setStrokeWidth(@Dimension strokeWidth: Float){
+ userImage.strokeWidth = strokeWidth
+ userImage.setPadding(strokeWidth.toInt())
+ }
+ fun setShapeAppearance(@StyleRes shapeAppearanceRes: Int){
+ userImage.shapeAppearanceModel = ShapeAppearanceModel
+ .builder(context, com.google.android.material.R.style.ShapeAppearance_Material3_SmallComponent, shapeAppearanceRes)
+ .build()
+ }
+
+ fun setImageChangeButtonSize(@Dimension size: Int){
+ userImageChangeButton.layoutParams.let { oldLayoutParams ->
+ oldLayoutParams.height = size
+ oldLayoutParams.width = size
+ }
+ }
+
+ fun setImageChangeButtonBottomEndMargin(@Dimension margin: Int){
+ (userImageChangeButton.layoutParams as FrameLayout.LayoutParams).apply {
+ setMargins(0,0,0,margin)
+ marginEnd = margin
+ }
+ }
+
+ fun setImageChangeButtonInnerPadding(@Dimension padding: Int){
+ userImageChangeButton.setContentPadding(padding, padding, padding, padding)
+ }
+
+ fun setImageChangeButtonDrawable(@DrawableRes drawable: Int){
+ userImageChangeButtonImage.setImageResource(drawable)
+ }
+
+
+ fun setUserImage(@DrawableRes resId: Int){
+ userImage.setImageResource(resId)
+ }
+
+ fun setUserImage(drawable: Drawable?){
+ userImage.setImageDrawable(drawable)
+ }
+
+ fun setOnImageChangeButtonListener(listener: () -> Unit){
+ userImageChangeButton.setOnClickListener{
+ listener()
+ }
+ }
+}
+
+@ColorInt
+fun TypedArray.getColorOrNull(@StyleableRes res: Int): Int? {
+ return if(this.hasValue(res))
+ this.getColor(res, 0)
+ else
+ null
+}
+
+fun TypedArray.getDimensionOrNull(@StyleableRes res: Int): Float? {
+ return if(this.hasValue(res))
+ this.getDimension(res, 0f)
+ else
+ null
+}
+
+@AnyRes
+fun TypedArray.getResourceIdOrNull(@StyleableRes res: Int): Int? {
+ return if(this.hasValue(res))
+ this.getResourceId(res, 0)
+ else
+ null
+}
\ No newline at end of file
diff --git a/uihouse/src/main/res/color/link_color.xml b/uihouse/src/main/res/color/link_color.xml
new file mode 100644
index 0000000..e1ce048
--- /dev/null
+++ b/uihouse/src/main/res/color/link_color.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/color/link_color_state_list.xml b/uihouse/src/main/res/color/link_color_state_list.xml
new file mode 100644
index 0000000..e1ce048
--- /dev/null
+++ b/uihouse/src/main/res/color/link_color_state_list.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/drawable/bottom_sheet_grip.xml b/uihouse/src/main/res/drawable/bottom_sheet_grip.xml
new file mode 100644
index 0000000..cf8c949
--- /dev/null
+++ b/uihouse/src/main/res/drawable/bottom_sheet_grip.xml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/uihouse/src/main/res/drawable/ic_baseline_photo_camera_24.xml b/uihouse/src/main/res/drawable/ic_baseline_photo_camera_24.xml
new file mode 100644
index 0000000..4a59a05
--- /dev/null
+++ b/uihouse/src/main/res/drawable/ic_baseline_photo_camera_24.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/uihouse/src/main/res/layout/profile_image.xml b/uihouse/src/main/res/layout/profile_image.xml
new file mode 100644
index 0000000..cd1b148
--- /dev/null
+++ b/uihouse/src/main/res/layout/profile_image.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/uihouse/src/main/res/values-night/theme.xml b/uihouse/src/main/res/values-night/theme.xml
new file mode 100644
index 0000000..d6461b0
--- /dev/null
+++ b/uihouse/src/main/res/values-night/theme.xml
@@ -0,0 +1,40 @@
+
+
+
+
diff --git a/uihouse/src/main/res/values/attrs.xml b/uihouse/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..8d512f6
--- /dev/null
+++ b/uihouse/src/main/res/values/attrs.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uihouse/src/main/res/values/bottom_sheet_attributes.xml b/uihouse/src/main/res/values/bottom_sheet_attributes.xml
new file mode 100644
index 0000000..520e106
--- /dev/null
+++ b/uihouse/src/main/res/values/bottom_sheet_attributes.xml
@@ -0,0 +1,34 @@
+
+
+ 10dp
+ 175dp
+
+
+
+
+
+
+
+
+
+
+ - @drawable/bottom_sheet_grip
+
+ 16dp
+
+
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/values/button_attributes.xml b/uihouse/src/main/res/values/button_attributes.xml
new file mode 100644
index 0000000..1a8898c
--- /dev/null
+++ b/uihouse/src/main/res/values/button_attributes.xml
@@ -0,0 +1,20 @@
+
+
+ 60dp
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/values/card_attributes.xml b/uihouse/src/main/res/values/card_attributes.xml
new file mode 100644
index 0000000..ae489f1
--- /dev/null
+++ b/uihouse/src/main/res/values/card_attributes.xml
@@ -0,0 +1,33 @@
+
+
+ 8dp
+ 14dp
+ 20dp
+
+
+
+
+
+
+
+
+
+ 4dp
+ 16dp
+
+ 16dp
+ 8dp
+
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/values/color.xml b/uihouse/src/main/res/values/color.xml
new file mode 100644
index 0000000..0479ac9
--- /dev/null
+++ b/uihouse/src/main/res/values/color.xml
@@ -0,0 +1,64 @@
+
+
+
+ #325CA8
+ #FFFFFF
+ #D8E2FF
+ #001A42
+ #575E71
+ #FFFFFF
+ #DBE2F9
+ #141B2C
+ #715573
+ #FFFFFF
+ #FCD7FB
+ #29132D
+ #BA1A1A
+ #FFDAD6
+ #FFFFFF
+ #410002
+ #FEFBFF
+ #1B1B1F
+ #FEFBFF
+ #1B1B1F
+ #E1E2EC
+ #44474F
+ #75777F
+ #F2F0F4
+ #303034
+ #AEC6FF
+ #000000
+ #325CA8
+ #325CA8
+ #AEC6FF
+ #002E6A
+ #12448F
+ #D8E2FF
+ #BFC6DC
+ #293041
+ #3F4759
+ #DBE2F9
+ #DFBCDF
+ #402843
+ #583E5A
+ #FCD7FB
+ #FFB4AB
+ #93000A
+ #690005
+ #FFDAD6
+ #1B1B1F
+ #E3E2E6
+ #1B1B1F
+ #E3E2E6
+ #44474F
+ #C5C6D0
+ #8E9099
+ #1B1B1F
+ #E3E2E6
+ #325CA8
+ #000000
+ #AEC6FF
+ #AEC6FF
+ #243b67
+ #1C9E43
+
diff --git a/uihouse/src/main/res/values/dimen.xml b/uihouse/src/main/res/values/dimen.xml
new file mode 100644
index 0000000..53df51c
--- /dev/null
+++ b/uihouse/src/main/res/values/dimen.xml
@@ -0,0 +1,38 @@
+
+
+
+ 16dp
+
+ 16dp
+
+
+ 8dp
+ 16dp
+ 20dp
+
+
+ 48dp
+
+ 24dp
+
+ 4dp
+
+
+ 8dp
+
+ 16dp
+
+ 100dp
+
+ 5dp
+
+
+ 120dp
+ 160dp
+
+ 300dp
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/values/image_attributes.xml b/uihouse/src/main/res/values/image_attributes.xml
new file mode 100644
index 0000000..ba3bb6f
--- /dev/null
+++ b/uihouse/src/main/res/values/image_attributes.xml
@@ -0,0 +1,36 @@
+
+
+ 24dp
+ 100dp
+ 100dp
+ 56dp
+ 39dp
+ 15dp
+ 85dp
+ 45dp
+ 64dp
+
+ 56dp
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/values/opacity.xml b/uihouse/src/main/res/values/opacity.xml
new file mode 100644
index 0000000..5b1abdc
--- /dev/null
+++ b/uihouse/src/main/res/values/opacity.xml
@@ -0,0 +1,5 @@
+
+
+ - 0.7
+ - 0.4
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/values/styles.xml b/uihouse/src/main/res/values/styles.xml
new file mode 100644
index 0000000..170be1a
--- /dev/null
+++ b/uihouse/src/main/res/values/styles.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uihouse/src/main/res/values/theme.xml b/uihouse/src/main/res/values/theme.xml
new file mode 100644
index 0000000..a83ee8f
--- /dev/null
+++ b/uihouse/src/main/res/values/theme.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
diff --git a/uihouse/src/main/res/values/typography.xml b/uihouse/src/main/res/values/typography.xml
new file mode 100644
index 0000000..dd71936
--- /dev/null
+++ b/uihouse/src/main/res/values/typography.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file