Initial commit

This commit is contained in:
mehul4795
2021-03-27 10:55:34 +05:30
commit 6b23bf1daa
254 changed files with 12178 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="aculix.core">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
</manifest>
@@ -0,0 +1,17 @@
package aculix.core.base
import aculix.core.extensions.toast
import android.os.Bundle
import androidx.fragment.app.Fragment
/**
* Base Fragment which is extended by other fragments
* in the app
*/
open class BaseFragment : Fragment() {
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
}
}
@@ -0,0 +1,20 @@
package aculix.core.base
import aculix.core.R
import android.app.Dialog
import android.os.Bundle
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
/**
* BottomSheetDialogFragment that uses a custom
* theme which sets a rounded background to the dialog
* and doesn't dim the navigation bar
*/
open class RoundedBottomSheetDialogFragment : BottomSheetDialogFragment() {
override fun getTheme(): Int = R.style.BottomSheetDialogTheme
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = BottomSheetDialog(requireContext(), theme)
}
@@ -0,0 +1,37 @@
package aculix.core.extensions
import android.widget.ProgressBar
import androidx.annotation.StringRes
import com.google.android.material.button.MaterialButton
/**
* Button enabling/disabling modifiers
*/
fun MaterialButton.disableButton() {
isEnabled = false
}
fun MaterialButton.enableButton() {
isEnabled = true
}
/**
* Disables the button and hides the text. Shows the progressbar inside
* the button
*/
fun MaterialButton.showProgressBarInButton(progressBar: ProgressBar) {
text = ""
disableButton()
progressBar.makeVisible()
}
/**
* Enables the button and shows the button text. Hides the progressbar
* from the button
*/
fun MaterialButton.hideProgressBarFromButton(progressBar: ProgressBar, @StringRes buttonText: Int) {
progressBar.makeInvisible()
text = resources.getString(buttonText)
enableButton()
}
@@ -0,0 +1,11 @@
package aculix.core.extensions
import com.google.android.material.chip.Chip
fun Chip.check() {
isChecked = true
}
fun Chip.unCheck() {
isChecked = false
}
@@ -0,0 +1,137 @@
package aculix.core.extensions
import aculix.core.R
import android.app.Activity
import android.app.DownloadManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.util.DisplayMetrics
import android.widget.Toast
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.ContextCompat
import saschpe.android.customtabs.CustomTabsHelper
import saschpe.android.customtabs.WebViewFallback
/**
* Relaunches the current activity
*/
fun Context.relaunchActivity(context: Activity, intent: Intent) {
context.finish()
startActivity(intent)
}
/**
* Shows the toast message
*/
fun Context.toast(message: String, length: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, length).show()
}
/**
* Opens the passed URL in the Chrome Custom Tabs
*/
fun Context.openUrl(url: String, @ColorRes toolbarColor: Int) {
val customTabsIntent = CustomTabsIntent.Builder()
.addDefaultShareMenuItem()
.setToolbarColor(ContextCompat.getColor(this, toolbarColor))
.setShowTitle(true)
.build()
// This is optional but recommended
CustomTabsHelper.addKeepAliveExtra(this, customTabsIntent.intent)
CustomTabsHelper.openCustomTab(
this,
customTabsIntent,
Uri.parse(url),
WebViewFallback() // Opens in system browser if Chrome isn't installed on device
)
}
fun Context.openAppInGooglePlay(appPackageName: String) {
try {
// Try to open in the Google Play app
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appPackageName")))
} catch (exception: android.content.ActivityNotFoundException) {
// Google Play app is not installed. Open URL in the browser.
openUrl("https://play.google.com/store/apps/details?id=$appPackageName", R.color.customTabToolbarColor)
}
}
fun Context.copyTextToClipboard(textToCopy: String, textCopiedMessage: String) {
val clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipData = ClipData.newPlainText("Copied Text", textToCopy)
clipboardManager.setPrimaryClip(clipData)
toast(textCopiedMessage)
}
@Suppress("DEPRECATION")
fun Context.isInternetAvailable(): Boolean {
var result = false
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.run {
cm.getNetworkCapabilities(cm.activeNetwork)?.run {
result = when {
hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
else -> false
}
}
}
} else {
cm.run {
cm.activeNetworkInfo?.run {
if (type == ConnectivityManager.TYPE_WIFI) {
result = true
} else if (type == ConnectivityManager.TYPE_MOBILE) {
result = true
}
}
}
}
return result
}
/**
* Starts an Intent for sharing text
*/
fun Context.startShareTextIntent(shareTitle: String, shareText: String) {
val shareIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, shareText)
type = "text/plain"
}
startActivity(Intent.createChooser(shareIntent, shareTitle))
}
fun Context.startEmailIntent(toEmail: String, emailSubject: String?) {
// Open Email app with subject and to field pre-filled
val emailIntent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("mailto:$toEmail") // only email apps should handle this
putExtra(Intent.EXTRA_SUBJECT, emailSubject) // Email subject
}
// Check if an Email app is installed on the device
if (emailIntent.resolveActivity(packageManager) != null) {
startActivity(emailIntent)
} else {
toast(getString(R.string.text_no_email_app_found))
}
}
@@ -0,0 +1,25 @@
package aculix.core.extensions
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
/**
* Adds TextWatcher to the EditText
*/
fun EditText.onTextChanged(listener: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
listener(s.toString())
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// We only need the value of EditText after the user stops typing
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// We only need the value of EditText after the user stops typing
}
})
}
@@ -0,0 +1,71 @@
package aculix.core.extensions
import aculix.core.helper.BaseViewModelFactory
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.webkit.URLUtil
import android.widget.ImageView
import androidx.core.app.ActivityCompat
import androidx.core.content.FileProvider
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
/**
* Checks if the passed permissions are granted or not
*
* @param permissions List of permissions to be checked
*/
fun Fragment.hasPermissions(context: Context?, vararg permissions: String): Boolean {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null) {
for (permission in permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false
}
}
}
return true
}
@Suppress("DEPRECATION")
fun Fragment.isInternetAvailable(context: Context): Boolean {
var result = false
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.run {
cm.getNetworkCapabilities(cm.activeNetwork)?.run {
result = when {
hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
else -> false
}
}
}
} else {
cm.run {
cm.activeNetworkInfo?.run {
if (type == ConnectivityManager.TYPE_WIFI) {
result = true
} else if (type == ConnectivityManager.TYPE_MOBILE) {
result = true
}
}
}
}
return result
}
@@ -0,0 +1,2 @@
package aculix.core.extensions
@@ -0,0 +1,32 @@
package aculix.core.extensions
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
/**
* Converts a Vector drawable to a Bitmap
*
* @param tintColor Tint color to be applied to the Vector drawable
*
*@return Converted Bitmap
*/
fun Int.toBitmap(context: Context, @ColorRes tintColor: Int? = null): Bitmap? {
// retrieve the actual drawable
val drawable = ContextCompat.getDrawable(context, this) ?: return null
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
val bm = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
// add the tint if it exists
tintColor?.let {
DrawableCompat.setTint(drawable, ContextCompat.getColor(context, it))
}
// draw it onto the bitmap
val canvas = Canvas(bm)
drawable.draw(canvas)
return bm
}
@@ -0,0 +1,19 @@
package aculix.core.extensions
import android.annotation.SuppressLint
import kotlin.math.ln
import kotlin.math.pow
/**
* Formats the number in thousands, millions, billions etc.
*/
@SuppressLint("DefaultLocale")
fun Long.getFormattedNumberInString(): String {
if (this < 1000) return this.toString()
val exp = (ln(this.toDouble()) / ln(1000.0)).toInt()
return java.lang.String.format(
"%.1f %c",
this / 1000.0.pow(exp.toDouble()),
"KMBTPE"[exp - 1]
)
}
@@ -0,0 +1,15 @@
package aculix.core.extensions
/**
* Creates a 64 bit hash string from the given charsequences
* @hide
*/
fun CharSequence.to64BitHash(): Long {
var result = -0x340d631b7bdddcdbL
val len = this.length
for (i in 0 until len) {
result = result xor this[i].toLong()
result *= 0x100000001b3L
}
return result
}
@@ -0,0 +1,76 @@
package aculix.core.extensions
import android.content.Context
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.annotation.StringRes
import com.google.android.material.snackbar.Snackbar
/**
* Visibility modifiers and check functions
*/
fun View.isVisibile(): Boolean = this.visibility == View.VISIBLE
fun View.isGone(): Boolean = this.visibility == View.GONE
fun View.isInvisible(): Boolean = this.visibility == View.INVISIBLE
fun View.makeVisible() {
this.visibility = View.VISIBLE
}
fun View.makeGone() {
this.visibility = View.GONE
}
fun View.makeInvisible() {
this.visibility = View.INVISIBLE
}
/**
* Hides the soft input keyboard from the screen
*/
fun View.dismissKeyboard(context: Context?) {
val inputMethodManager =
context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
}
/**
* Shows the soft keyboard on the screen
*/
fun View.showKeyboard(context: Context?) {
val inputMethodManager =
context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(this, InputMethodManager.SHOW_FORCED)
}
/**
* Extension functions to show Snackbar along with the action
*/
inline fun View.showSnackbar(
@StringRes messageRes: Int, length: Int = Snackbar.LENGTH_LONG,
f: Snackbar.() -> Unit
) {
showSnackbar(resources.getString(messageRes), length, f)
}
inline fun View.showSnackbar(
message: String,
length: Int = Snackbar.LENGTH_LONG,
f: Snackbar.() -> Unit
) {
val snack = Snackbar.make(this, message, length)
snack.f()
snack.show()
}
fun Snackbar.action(@StringRes actionRes: Int, color: Int? = null, listener: (View) -> Unit) {
action(view.resources.getString(actionRes), color, listener)
}
fun Snackbar.action(action: String, color: Int? = null, listener: (View) -> Unit) {
setAction(action, listener)
color?.let { setActionTextColor(color) }
}
@@ -0,0 +1,10 @@
package aculix.core.extensions
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
// Inflates the layout in RecyclerView Adapter
fun ViewGroup.inflate(layoutRes: Int): View {
return LayoutInflater.from(context).inflate(layoutRes,this, false);
}
@@ -0,0 +1,14 @@
package aculix.core.helper
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
/**
* ViewModel factory to create ViewModels and also allows to pass
* values to the ViewModel constructor
*/
class BaseViewModelFactory<T>(val creator: () -> T) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return creator() as T
}
}
@@ -0,0 +1,14 @@
package aculix.core.helper
/**
* An app wide wrapper class that is returned by the Livedata as a result
* and can be used with when expression in an activity or fragment
*/
sealed class ResultWrapper {
object Loading : ResultWrapper()
data class Error(val errorMessage: String) : ResultWrapper()
data class Success<T>(val data: T) : ResultWrapper()
}
@@ -0,0 +1,34 @@
package aculix.core.helper
import aculix.core.R
import androidx.recyclerview.widget.RecyclerView
import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat
class SimpleDividerItemDecoration(context: Context, private val paddingLeft: Int = 16, private val paddingRight: Int = 16) : RecyclerView.ItemDecoration() {
private val mDivider: Drawable? = ContextCompat.getDrawable(context, R.drawable.bg_line_divider)
private val density = context.resources.displayMetrics.density
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val left = Math.round(density * paddingLeft)
val right = parent.width - Math.round(density * paddingRight)
val childCount = parent.childCount
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
val params = child.layoutParams as RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin
val bottom = top + mDivider!!.intrinsicHeight
mDivider.setBounds(left, top, right, bottom)
mDivider.draw(c)
}
}
}
@@ -0,0 +1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="1417.32"
android:viewportHeight="1417.32">
<path
android:pathData="M0,0h1417.32v1417.32h-1417.32z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="4.07"
android:startX="1226.85"
android:endY="1508.85"
android:endX="120.16"
android:type="linear">
<item android:offset="0" android:color="#FFE27C3A"/>
<item android:offset="0.24" android:color="#FFE57735"/>
<item android:offset="0.54" android:color="#FFEC6926"/>
<item android:offset="0.86" android:color="#FFF8530D"/>
<item android:offset="1" android:color="#FFFF4600"/>
</gradient>
</aapt:attr>
</path>
</vector>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="@dimen/bottom_sheet_corner_radius"
android:topRightRadius="@dimen/bottom_sheet_corner_radius" />
<solid android:color="?attr/colorSurface" />
</shape>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="1dp"
android:height="1dp" />
<solid android:color="#eeeeee" />
</shape>
@@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="2115.403"
android:viewportHeight="2115.403">
<group android:translateX="349.0415"
android:translateY="349.0415">
<path
android:pathData="M1006.08,989.14H356.68a133,133 0,0 1,-107 -212l96.12,-130.13A474.78,474.78 0,0 1,727.72 454.37h278.36a22.21,22.21 0,0 1,0 44.41H727.72A430.38,430.38 0,0 0,381.57 673.43L285.45,803.56c-20.28,27.46 -23.21,62 -7.83,92.51s44.93,48.67 79.06,48.67h649.4a22.2,22.2 0,0 1,0 44.4Z"
android:fillColor="#fff"/>
<path
android:pathData="M388.13,735.9l53.27,-69.74s108,-127.08 247.33,-127.08h22.32c14,0 18.19,19 5.54,24.85 -57.72,26.79 -132.5,77.93 -145,166A31.26,31.26 0,0 1,542.15 757l-139.89,6A16.87,16.87 0,0 1,388.13 735.9Z"
android:fillColor="#fff"/>
<path
android:pathData="M1071.9,587.2H856.52a22.21,22.21 0,1 1,0 -44.41H1071.9a22.21,22.21 0,0 1,0 44.41Z"
android:fillColor="#fff"/>
<path
android:pathData="M923.82,691.71H782.39a22.21,22.21 0,0 1,0 -44.41H923.82a22.21,22.21 0,1 1,0 44.41Z"
android:fillColor="#fff"/>
<path
android:pathData="M1171.52,691.71H1006.08a22.21,22.21 0,1 1,0 -44.41h165.44a22.21,22.21 0,0 1,0 44.41Z"
android:fillColor="#fff"/>
<path
android:pathData="M1133.82,796.22H832.28a22.21,22.21 0,0 1,0 -44.41h301.54a22.21,22.21 0,1 1,0 44.41Z"
android:fillColor="#fff"/>
<path
android:pathData="M902.29,900.73h-70a22.21,22.21 0,0 1,0 -44.41h70a22.21,22.21 0,0 1,0 44.41Z"
android:fillColor="#fff"/>
<path
android:pathData="M1133.82,900.73H983.05a22.21,22.21 0,0 1,0 -44.41h150.77a22.21,22.21 0,1 1,0 44.41Z"
android:fillColor="#fff"/>
</group>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.9,13.98l2.1,2.53 3.1,-3.99c0.2,-0.26 0.6,-0.26 0.8,0.01l3.51,4.68c0.25,0.33 0.01,0.8 -0.4,0.8H6.02c-0.42,0 -0.65,-0.48 -0.39,-0.81L8.12,14c0.19,-0.26 0.57,-0.27 0.78,-0.02z" />
</vector>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:inset="16dp">
<layer-list>
<item android:drawable="@drawable/ic_place_holder" />
</layer-list>
</inset>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="customTabToolbarColor">#212121</color>
</resources>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- BottomSheet -->
<dimen name="bottom_sheet_corner_radius">16dp</dimen>
<dimen name="bottom_sheet_top_padding">16dp</dimen>
</resources>
+4
View File
@@ -0,0 +1,4 @@
<resources>
<string name="app_name">Core</string>
<string name="text_no_email_app_found">No Email application installed on the device</string>
</resources>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- BottomSheet -->
<style name="BottomSheet" parent="@style/Widget.MaterialComponents.BottomSheet.Modal">
<item name="android:background">@drawable/bg_bottom_sheet</item>
</style>
<style name="BaseBottomSheetDialog" parent="@style/Theme.MaterialComponents.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="bottomSheetStyle">@style/BottomSheet</item>
</style>
<style name="BottomSheetDialogTheme" parent="BaseBottomSheetDialog" >
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>