From 24a556bc138aeb4299ec7a5bd74b81bf26a7a6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Mar=C3=ADa?= Date: Tue, 24 Aug 2021 19:45:48 +0200 Subject: [PATCH] Include new components, extensions and utils --- app/build.gradle | 4 +- .../compposecomponents/MainActivity.kt | 23 +++ jchucomponentscompose/build.gradle | 17 ++- .../src/main/AndroidManifest.xml | 6 +- .../extensions/context/ContextExensions.kt | 1 - .../core/extensions/ints/IntExtensions.kt | 1 + .../notifications/NotificationExtensions.kt | 121 ++++++++++++++++ .../extensions/retrofit/RequestResponses.kt | 5 +- .../extensions/strings/StringExtensions.kt | 35 ++++- .../core/extensions/uri/UriExtensions.kt | 30 ++++ .../ui/animations/ListAnimations.kt | 121 ++++++++++++++++ .../ui/cards/{StoryCard.kt => DebutCard.kt} | 29 +++- .../ui/cards/InfoCard.kt | 31 +++- .../ui/cards/PostCardTop.kt | 16 +- .../ui/cards/SectionCard.kt | 32 +++- .../ui/cards/StoriesCard.kt | 137 ++++++++++++++++++ .../jchucomponentscompose/ui/chips/Tags.kt | 121 ++++++++++++++++ .../ui/chips/YoutubeChip.kt | 79 ++++++++++ .../ui/images/DoubleTapAnimation.kt | 17 ++- .../ui/images/NetworkImage.kt | 13 +- .../ui/loaders/CircularLoading.kt | 26 +++- .../ui/loaders/PulseLoading.kt | 52 +++++-- .../ui/sheets/BottomSheetWithClose.kt | 24 ++- .../ui/systemui/SystemBarsColors.kt | 12 ++ .../jchucomponentscompose/ui/tags/Tags.kt | 95 ------------ .../ui/textfields/CountTextField.kt | 107 ++++++++++++++ .../jchucomponentscompose/ui/time/Time.kt | 11 +- .../utils/GraphicUtils.kt | 42 ++++++ .../utils/broadcast/CustomTabsCopyReceiver.kt | 13 +- .../utils/broadcast/ShareBroadcastReceiver.kt | 8 + .../firebase/FirebaseAnalyticsManager.kt | 58 ++++++++ .../utils/mediaplayer/MediaPlayerHolder.kt | 34 +++-- .../utils/mediaplayer/PlaybackInfoListener.kt | 2 +- .../utils/network/NetworkUtils.kt | 50 +++++++ .../utils/validators/CreditCardValidator.kt | 81 +++++++++++ .../utils/validators/Validators.kt | 52 +++++++ 36 files changed, 1326 insertions(+), 180 deletions(-) create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/notifications/NotificationExtensions.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/uri/UriExtensions.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/animations/ListAnimations.kt rename jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/{StoryCard.kt => DebutCard.kt} (84%) create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoriesCard.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/Tags.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/YoutubeChip.kt delete mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/tags/Tags.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/textfields/CountTextField.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/GraphicUtils.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/firebase/FirebaseAnalyticsManager.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/network/NetworkUtils.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/validators/CreditCardValidator.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/validators/Validators.kt diff --git a/app/build.gradle b/app/build.gradle index 0ac3ce8c..a8a77bf5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdk 30 + compileSdk 31 defaultConfig { applicationId "com.jeluchu.compposecomponents" @@ -58,7 +58,7 @@ dependencies { implementation 'androidx.compose.runtime:runtime:1.0.1' implementation 'androidx.compose.runtime:runtime-livedata:1.0.1' implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02' - implementation 'androidx.navigation:navigation-compose:2.4.0-alpha06' + implementation 'androidx.navigation:navigation-compose:2.4.0-alpha07' implementation 'androidx.activity:activity-compose:1.3.1' } \ No newline at end of file diff --git a/app/src/main/java/com/jeluchu/compposecomponents/MainActivity.kt b/app/src/main/java/com/jeluchu/compposecomponents/MainActivity.kt index d37c5330..8f5b332d 100644 --- a/app/src/main/java/com/jeluchu/compposecomponents/MainActivity.kt +++ b/app/src/main/java/com/jeluchu/compposecomponents/MainActivity.kt @@ -7,6 +7,8 @@ import androidx.activity.compose.setContent import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Button import androidx.compose.runtime.Composable @@ -15,7 +17,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp +import com.jeluchu.jchucomponentscompose.core.extensions.context.shortToast +import com.jeluchu.jchucomponentscompose.ui.animations.animateItem +import com.jeluchu.jchucomponentscompose.ui.cards.StoryCard import com.jeluchu.jchucomponentscompose.ui.images.DoubleTapAnimation import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage @@ -37,9 +43,26 @@ class MainActivity : ComponentActivity() { @Composable fun PhotoSelector() { + + val context = LocalContext.current + + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { Column(horizontalAlignment = Alignment.CenterHorizontally) { + LazyRow { + items(numbers) { item -> + StoryCard( + modifier = Modifier.animateItem(), + title = item.toString(), + iconMainUrl = "https://i.picsum.photos/id/1003/1181/1772.jpg?hmac=oN9fHMXiqe9Zq2RM6XT-RVZkojgPnECWwyEF1RvvTZk", + circleImage = R.drawable.ic_btnfavourite, + navigateToScreen = { context.shortToast("Clicked!") } + ) + } + } + if (imageUriState.value != null) { NetworkImage( diff --git a/jchucomponentscompose/build.gradle b/jchucomponentscompose/build.gradle index 25c4c031..ada6d276 100644 --- a/jchucomponentscompose/build.gradle +++ b/jchucomponentscompose/build.gradle @@ -6,12 +6,12 @@ plugins { android { - compileSdkVersion 30 + compileSdkVersion 31 buildToolsVersion "30.0.3" defaultConfig { minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 31 consumerProguardFiles "consumer-rules.pro" } @@ -56,11 +56,14 @@ dependencies { implementation 'androidx.compose.runtime:runtime:1.0.1' implementation 'androidx.compose.runtime:runtime-livedata:1.0.1' implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02' - implementation 'androidx.navigation:navigation-compose:2.4.0-alpha06' + implementation 'androidx.navigation:navigation-compose:2.4.0-alpha07' + + // FIREBASE LIBRARY ---------------------------------------------------------------------------- + implementation("com.google.firebase:firebase-analytics-ktx:19.0.1") // ACCOMPANIST GOOGLE LIBRARY ------------------------------------------------------------------ - implementation 'com.google.accompanist:accompanist-systemuicontroller:0.16.1' - implementation 'com.google.accompanist:accompanist-navigation-animation:0.16.1' + implementation 'com.google.accompanist:accompanist-systemuicontroller:0.17.0' + implementation 'com.google.accompanist:accompanist-navigation-animation:0.17.0' // ANDROIDX LIBRARY ---------------------------------------------------------------------------- implementation 'androidx.core:core-ktx:1.6.0' @@ -69,7 +72,7 @@ dependencies { // GOOGLE LIBRARY ------------------------------------------------------------------------------ implementation 'com.google.android.material:material:1.4.0' - implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.google.code.gson:gson:2.8.8' // SQUAREUP LIBRARY ---------------------------------------------------------------------------- implementation 'com.squareup.retrofit2:retrofit:2.9.0' @@ -88,7 +91,7 @@ afterEvaluate { from components.release groupId = "com.jeluchu" artifactId = "jchucomponentscompose" - version = "0.0.6" + version = "0.1.0" } } } diff --git a/jchucomponentscompose/src/main/AndroidManifest.xml b/jchucomponentscompose/src/main/AndroidManifest.xml index e35e8344..cc64049f 100644 --- a/jchucomponentscompose/src/main/AndroidManifest.xml +++ b/jchucomponentscompose/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ - + + + \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensions.kt index fff53527..329cb112 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensions.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensions.kt @@ -1,7 +1,6 @@ package com.jeluchu.jchucomponentscompose.core.extensions.context import android.annotation.SuppressLint -import android.app.Activity import android.app.NotificationManager import android.app.PendingIntent import android.content.ClipData diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/ints/IntExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/ints/IntExtensions.kt index f9de1f92..9183603c 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/ints/IntExtensions.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/ints/IntExtensions.kt @@ -6,6 +6,7 @@ import java.text.DecimalFormat fun Int.Companion.empty() = 0 fun Int?.orEmpty() = this ?: Int.empty() +fun Int.isNotEmpty() = this != Int.empty() fun Long.bytesToMeg(): String = (this / (1024L * 1024L)).toString() fun Int.fixedDecimalsTime(): String { diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/notifications/NotificationExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/notifications/NotificationExtensions.kt new file mode 100644 index 00000000..6a894a3f --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/notifications/NotificationExtensions.kt @@ -0,0 +1,121 @@ +package com.jeluchu.jchucomponentscompose.core.extensions.notifications + +import android.annotation.SuppressLint +import android.app.* +import android.content.Context +import android.os.Build +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat + +/** + * NotificationExtensions.kt + * is a class that contain all extension functions + * to help us easy to create and update notification + * from anywhere that has context object. + */ + +/** + * Create notification group + */ +fun Context.createNotificationChannelGroup(@StringRes groupId: Int, @StringRes groupName: Int) { + val id = this.getString(groupId) + val name = this.getString(groupName) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val notificationManager = this.getSystemService(NotificationManager::class.java) + notificationManager?.createNotificationChannelGroup(NotificationChannelGroup(id, name)) + } +} + +/** + * Get notification channel + * @param channelId + * @return [NotificationChannel] + */ +fun Context.getNotificationChannel(@StringRes channelId: Int): NotificationChannel? { + return NotificationManagerCompat.from(this).getNotificationChannel(this.getString(channelId)) +} + +/** + * Get notification channel group + * @param groupId + * @return [NotificationChannelGroup] + */ +fun Context.getNotificationChannelGroup(@StringRes groupId: Int): NotificationChannelGroup? { + return NotificationManagerCompat.from(this).getNotificationChannelGroup(this.getString(groupId)) +} + +/** + * Create notification channel + */ +fun Context.createNotificationChannel( + @StringRes channelId: Int, + @StringRes channelName: Int, + @StringRes channelDescription: Int, + @SuppressLint("InlinedApi") importance: Int = NotificationManager.IMPORTANCE_DEFAULT +) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val id = this.getString(channelId) + val name = this.getString(channelName) + val descriptionText = this.getString(channelDescription) + val channel = NotificationChannel(id, name, importance).also { + it.description = descriptionText + if (importance >= NotificationManager.IMPORTANCE_HIGH) { + it.enableVibration(true) + it.enableLights(true) + } else if (importance < NotificationManager.IMPORTANCE_DEFAULT) { + it.enableVibration(false) + it.enableLights(false) + it.setSound(null, null) + } + } + val notificationManager = this.getSystemService(NotificationManager::class.java) + notificationManager?.createNotificationChannel(channel) + } +} + +/** + * Build a notification + */ +fun Context.getNotificationBuilder( + @StringRes channelId: Int, + title: Int, + text: Int, + @DrawableRes icon: Int, + @StringRes groupId: Int, + importance: Int = NotificationCompat.PRIORITY_DEFAULT, + pendingIntent: PendingIntent? = null +): NotificationCompat.Builder = + NotificationCompat.Builder(this, this.getString(channelId)) + .setSmallIcon(icon) + .setContentTitle(this.getString(title)) + .setContentText(this.getString(text)) + .setPriority(importance) + .setContentIntent(pendingIntent) + .setGroup(this.getString(groupId)) + +/** + * Update notification + */ +fun Context.notifyNotification(notificationId: Int, notification: Notification) = + NotificationManagerCompat.from(this).notify(notificationId, notification) + +/** + * Cancel notification base on notification id + * + * @param notificationId id to cancel + * + * Note: + * - if [notificationId] is empty or default, this method + * will cancel all notifications + */ +fun Context.cancelNotification(vararg notificationId: Int = intArrayOf()) { + if (notificationId.isEmpty()) { + NotificationManagerCompat.from(this).cancelAll() + return + } + notificationId.forEach { + NotificationManagerCompat.from(this).cancel(it) + } +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/retrofit/RequestResponses.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/retrofit/RequestResponses.kt index 412b2494..09bda04d 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/retrofit/RequestResponses.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/retrofit/RequestResponses.kt @@ -1,7 +1,7 @@ package com.jeluchu.jchucomponentscompose.core.extensions.retrofit -import com.jeluchu.jchucomponentscompose.core.functional.Either import com.jeluchu.jchucomponentscompose.core.exception.Failure +import com.jeluchu.jchucomponentscompose.core.functional.Either import retrofit2.Call fun request( @@ -14,13 +14,12 @@ fun request( when (response.isSuccessful) { true -> Either.Right(transform((response.body() ?: default))) false -> { - when (response.code()) { StatusCode.InternalServerError.code -> Either.Left(Failure.ServerError) StatusCode.BadGateway.code -> Either.Left(Failure.ServerError) + StatusCode.NotFound.code -> Either.Left(Failure.ServerError) else -> Either.Left(Failure.ServerError) } - } } } catch (exception: Throwable) { diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/strings/StringExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/strings/StringExtensions.kt index 307e6ffb..2559c011 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/strings/StringExtensions.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/strings/StringExtensions.kt @@ -9,6 +9,7 @@ import android.util.Base64 import androidx.compose.ui.graphics.Color import org.intellij.lang.annotations.RegExp import java.io.File +import java.io.FileOutputStream import java.io.IOException import java.net.HttpURLConnection import java.net.URL @@ -18,6 +19,28 @@ import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern +fun String.getLastBitFromUrl(): String = replaceFirst(".*/([^/?]+).*".toRegex(), "$1") + +fun String.saveImage(destinationFile: File) { + try { + Thread { + val url = URL(this) + val inputStream = url.openStream() + val os = FileOutputStream(destinationFile) + val b = ByteArray(2048) + var length: Int + while (inputStream.read(b).also { length = it } != -1) { + os.write(b, 0, length) + } + inputStream?.close() + os.close() + }.start() + + } catch (e: Exception) { + e.printStackTrace() + } +} + /** ---- COMPOSE FUNCTIONS --------------------------------------------------------------------- **/ fun String.getColor() = Color(android.graphics.Color.parseColor(this)) @@ -71,12 +94,6 @@ fun String?.compareDate(): Boolean { /** ---- CHECKER ------------------------------------------------------------------------------- **/ -val String.isPhone: Boolean get() = matches("^1([34578])\\d{9}\$".toRegex()) -val String.isEmail: Boolean get() = matches("^(\\w)+(\\.\\w+)*@(\\w)+((\\.\\w+)+)\$".toRegex()) -val String.isNumeric: Boolean get() = matches("^[0-9]+$".toRegex()) -val String.isAlphanumeric get() = matches("^[a-zA-Z0-9]*$".toRegex()) -val String.isAlphabetic get() = matches("^[a-zA-Z]*$".toRegex()) -fun String.isLocal() = !isEmptyString() && (startsWith("http://") || startsWith("https://")) fun CharSequence.isEmptyString(): Boolean = this.isEmpty() || this.toString().equals("null", true) fun CharSequence.isDigitOnly(): Boolean = (0 until length).any { Character.isDigit(this[it]) } @@ -100,11 +117,13 @@ fun String.atLeastOneNumber(): Boolean = !matches(Regex(".*\\d.*")) fun String.startsWithNonNumber(): Boolean = Character.isDigit(this[0]) fun String.noSpecialCharacter(): Boolean = !matches(Regex("[A-Za-z0-9]+")) fun String.atLeastOneSpecialCharacter(): Boolean = matches(Regex("[A-Za-z0-9]+")) -fun Any.readResourceText(resource: String): String? = this.javaClass.classLoader?.getResource(resource)?.readText() +fun Any.readResourceText(resource: String): String? = + this.javaClass.classLoader?.getResource(resource)?.readText() inline val String.isIp: Boolean get() { - val p = Pattern.compile("([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}") + val p = + Pattern.compile("([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}") val m = p.matcher(this) return m.find() } diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/uri/UriExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/uri/UriExtensions.kt new file mode 100644 index 00000000..886e20bc --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/uri/UriExtensions.kt @@ -0,0 +1,30 @@ +package com.jeluchu.jchucomponentscompose.core.extensions.uri + +import android.content.ContentResolver +import android.database.Cursor +import android.net.Uri +import android.provider.OpenableColumns +import java.io.IOException + +fun Uri.getFileName(contentResolver: ContentResolver): String { + var result: String? = null + if (scheme == "content") { + val cursor: Cursor? = contentResolver.query(this, null, null, null, null) + try { + if (cursor != null && cursor.moveToFirst()) { + result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)) + } + } catch (e: IOException) { + e.printStackTrace() + cursor?.close() + } + } + if (result == null) { + result = path + val cut = result!!.lastIndexOf('/') + if (cut != -1) { + result = result.substring(cut + 1) + } + } + return result +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/animations/ListAnimations.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/animations/ListAnimations.kt new file mode 100644 index 00000000..92821691 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/animations/ListAnimations.kt @@ -0,0 +1,121 @@ +package com.jeluchu.jchucomponentscompose.ui.animations + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.graphicsLayer + +/** + * + * Author: @Jeluchu + * + * This is an extension of animations for list item modifiers + * + * @param animationType in this parameter you have to pass a type of animation, + * by default this value is set to Default, you have the types of animations at [Animations] + * + */ + +fun Modifier.animateItem(animationType: Animations = Animations.Default): Modifier = + composed { + when (animationType) { + Animations.Fade -> { + val animatedProgress = remember { Animatable(initialValue = 0f) } + LaunchedEffect(Unit) { + animatedProgress.animateTo( + targetValue = 1f, + animationSpec = tween(600) + ) + } + alpha(animatedProgress.value) + } + Animations.Scale -> { + val animatedProgress = remember { Animatable(initialValue = 0.8f) } + LaunchedEffect(Unit) { + animatedProgress.animateTo( + targetValue = 1f, + animationSpec = tween(300, easing = LinearEasing) + ) + } + graphicsLayer(scaleY = animatedProgress.value, scaleX = animatedProgress.value) + } + Animations.Slide -> { + val animatedProgress = remember { Animatable(initialValue = 300f) } + LaunchedEffect(Unit) { + animatedProgress.animateTo( + targetValue = 0f, + animationSpec = tween(300, easing = FastOutSlowInEasing) + ) + } + graphicsLayer(translationX = animatedProgress.value) + } + Animations.FadeAndSlide -> { + val animatedProgress = remember { Animatable(initialValue = -300f) } + val opacityProgress = remember { Animatable(initialValue = 0f) } + LaunchedEffect(Unit) { + animatedProgress.animateTo( + targetValue = 0f, + animationSpec = tween(300, easing = LinearEasing) + ) + opacityProgress.animateTo( + targetValue = 1f, + animationSpec = tween(600) + ) + } + graphicsLayer(translationX = animatedProgress.value) + alpha(opacityProgress.value) + } + Animations.SlideUp -> { + val animatedProgress = remember { Animatable(initialValue = 300f) } + val opacityProgress = remember { Animatable(initialValue = 0f) } + LaunchedEffect(Unit) { + animatedProgress.animateTo( + targetValue = 0f, + animationSpec = tween(300, easing = LinearEasing) + ) + opacityProgress.animateTo( + targetValue = 1f, + animationSpec = tween(600) + ) + } + graphicsLayer(translationY = animatedProgress.value) + alpha(opacityProgress.value) + } + Animations.RotateX -> { + val animatedProgress = remember { Animatable(initialValue = 0f) } + LaunchedEffect(Unit) { + animatedProgress.animateTo( + targetValue = 360f, + animationSpec = tween(400, easing = FastOutSlowInEasing) + ) + } + graphicsLayer(rotationX = animatedProgress.value) + } + Animations.Default -> { + val animatedProgress = remember { Animatable(initialValue = 0.8f) } + LaunchedEffect(Unit) { + animatedProgress.animateTo( + targetValue = 1f, + animationSpec = tween(300) + ) + } + alpha(animatedProgress.value) + } + } + } + +enum class Animations { + Fade, + Scale, + Slide, + FadeAndSlide, + SlideUp, + RotateX, + Default +} diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoryCard.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/DebutCard.kt similarity index 84% rename from jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoryCard.kt rename to jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/DebutCard.kt index 9b9c9d8b..0c5560fc 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoryCard.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/DebutCard.kt @@ -20,9 +20,27 @@ import androidx.compose.ui.unit.sp import androidx.constraintlayout.compose.ConstraintLayout import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage +/** + * + * Author: @Jeluchu + * + * Component displaying + * information on a custom-designed card + * + * @param modifier modifier that will be used to change the color, size... + * @param title text to be displayed on the card + * @param image link of the image you want to be displayed on Card + * @param iconDebut link of the icon you want to be displayed on Card + * @param isDebut status to display a banner with information at the top + * @param bgDebut background color of the flag state with information + * @param navigateToScreen action to be performed after pressing + * + */ + @Composable -fun StoryCard( - name: String, +fun DebutCard( + modifier: Modifier, + title: String, image: String, iconDebut: String, isDebut: Boolean = false, @@ -34,7 +52,7 @@ fun StoryCard( Card( shape = RoundedCornerShape(12.dp), - modifier = Modifier + modifier = modifier .width(130.dp) .height(190.dp) .padding(4.dp), @@ -99,7 +117,6 @@ fun StoryCard( } - } } @@ -109,7 +126,7 @@ fun StoryCard( } Text( - text = name, + text = title, fontSize = 12.sp, color = Color.Black, modifier = Modifier.padding(7.dp), @@ -118,4 +135,4 @@ fun StoryCard( } -} +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/InfoCard.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/InfoCard.kt index fcd1cf24..8325146e 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/InfoCard.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/InfoCard.kt @@ -15,18 +15,34 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.jeluchu.jchucomponentscompose.core.extensions.ints.isNotEmpty import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage +/** + * + * Author: @Jeluchu + * + * Component displaying + * information on a custom-designed card + * + * @param modifier modifier that will be used to change the color, size... + * @param title text to be displayed on the card + * @param textColor color of text that appears on Card + * @param iconResource drawable id of the resource you want to be displayed as an icon on Card + * @param iconTintColor color of the icon (only if [iconResource] is being used) displayed on Card + * @param iconImage link of the image you want to be displayed on Card + * + */ + @Composable fun InfoCard( + modifier: Modifier, title: String, - isRemoteImage: Boolean = true, + textColor: Color = Color.Gray, iconResource: Int = 0, iconTintColor: Color = Color.White, - iconImage: String? = String.empty(), - modifier: Modifier, - textColor: Color = Color.Gray + iconImage: String = String.empty() ) { Box( @@ -40,13 +56,13 @@ fun InfoCard( Column { - if (isRemoteImage) { + if (iconImage.isNotEmpty()) { NetworkImage( contentScale = ContentScale.Fit, modifier = Modifier.size(34.dp), - url = iconImage ?: String.empty() + url = iconImage ) - } else { + } else if (iconResource.isNotEmpty()) { Icon( modifier = Modifier.size(30.dp), painter = painterResource(id = iconResource), @@ -55,7 +71,6 @@ fun InfoCard( ) } - } Column { diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/PostCardTop.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/PostCardTop.kt index fb329b86..e9989fd6 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/PostCardTop.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/PostCardTop.kt @@ -10,12 +10,26 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage +/** + * + * Author: @Jeluchu + * + * Component displaying + * information on a custom-designed card + * + * @param modifier modifier that will be used to change the color, size... + * @param title text to be displayed on the card + * @param image link of the image you want to be displayed on Card + * @param description text to be displayed on the card + * + */ + @Composable fun PostCardTop( + modifier: Modifier = Modifier, title: String, image: String, description: String, - modifier: Modifier = Modifier ) { val typography = MaterialTheme.typography diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/SectionCard.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/SectionCard.kt index bef45fee..ba94ff7f 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/SectionCard.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/SectionCard.kt @@ -20,21 +20,38 @@ import androidx.constraintlayout.compose.ConstraintLayout import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage +/** + * + * Author: @Jeluchu + * + * Component displaying + * information on a custom-designed card + * + * @param modifier modifier that will be used to change the color, size... + * @param title text to be displayed on the card + * @param textColor color of text that appears on Card + * @param localImage drawable id of the resource you want to be displayed as an icon on Card + * @param remoteImage link of the image you want to be displayed on Card + * @param backgroundCard color of the background Card + * @param navigateToScreen action to be performed after pressing + * + */ + @Composable fun SectionCard( - name: String, + modifier: Modifier, + title: String, + textColor: Color, localImage: Int = 0, remoteImage: String = String.empty(), backgroundCard: Color, - textColor: Color, - modifier: Modifier, - navigateToDetailScreen: () -> Unit + navigateToScreen: () -> Unit ) { Card( modifier = modifier .height(200.dp) - .clickable { navigateToDetailScreen() }, + .clickable { navigateToScreen() }, backgroundColor = backgroundCard, shape = RoundedCornerShape(16.dp), elevation = 0.dp @@ -75,8 +92,9 @@ fun SectionCard( } Text( - text = name, - modifier = Modifier.padding(12.dp, 6.dp, 12.dp, 6.dp) + text = title, + modifier = Modifier + .padding(12.dp, 6.dp, 12.dp, 6.dp) .constrainAs(sectionName) { bottom.linkTo(parent.bottom, margin = 20.dp) linkTo(parent.start, parent.end) diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoriesCard.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoriesCard.kt new file mode 100644 index 00000000..6b57febb --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoriesCard.kt @@ -0,0 +1,137 @@ +package com.jeluchu.jchucomponentscompose.ui.cards + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.constraintlayout.compose.ConstraintLayout +import com.jeluchu.jchucomponentscompose.core.extensions.ints.isNotEmpty +import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty +import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage + +/** + * + * Author: @Jeluchu + * + * This component displays the same design as Facebook stories + * + * @param modifier modifier that will be used to added animations (for example) + * @param title text to be displayed on the chip + * @param circleImage link of the image you want to be displayed as an icon on top storie + * @param iconMainUrl link of the image you want to be displayed as an image + * @param iconMainResource drawable id of the resource you want to be displayed as an image + * @param navigateToScreen action to be performed after pressing + * + */ + +@Composable +fun StoryCard( + modifier: Modifier = Modifier, + title: String, + circleImage: Int, + iconMainUrl: String = String.empty(), + iconMainResource: Int = 0, + navigateToScreen: () -> Unit +) { + + Card( + shape = RoundedCornerShape(12.dp), + modifier = modifier + .width(130.dp) + .height(190.dp) + .padding(4.dp), + backgroundColor = Color.Gray + ) { + + ConstraintLayout( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + .clickable { navigateToScreen() } + ) { + + val (profileImg, storyImg, name) = createRefs() + + if (iconMainUrl.isNotEmpty()) { + + NetworkImage( + url = iconMainUrl, + modifier = Modifier + .size(23.dp) + .padding(start = 7.dp) + .constrainAs(storyImg) { + linkTo(parent.start, parent.end) + linkTo(parent.top, parent.bottom) + }, + contentScale = ContentScale.Fit, + ) + + } else if (iconMainResource.isNotEmpty()) { + Icon( + modifier = Modifier + .size(23.dp) + .padding(start = 7.dp), + painter = painterResource(id = iconMainResource), + contentDescription = null + ) + } + + Text( + text = title, + fontSize = 13.sp, + color = Color.White, + modifier = Modifier + .constrainAs(name) { + start.linkTo(parent.start) + bottom.linkTo(parent.bottom) + } + .padding(8.dp), + style = TextStyle(shadow = Shadow(color = Color.Red)) + ) + + Card( + shape = CircleShape, + modifier = Modifier + .constrainAs(profileImg) { + top.linkTo(parent.top) + start.linkTo(parent.start) + } + .padding(8.dp) + .width(40.dp) + .height(40.dp) + .border( + width = 2.dp, + color = Color.Blue, + shape = CircleShape + ) + .padding(4.dp) + ) { + + Image( + painter = painterResource(circleImage), + contentDescription = "", + contentScale = ContentScale.Crop, + ) + + } + + } + + + } + +} diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/Tags.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/Tags.kt new file mode 100644 index 00000000..aa60c64c --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/Tags.kt @@ -0,0 +1,121 @@ +package com.jeluchu.jchucomponentscompose.ui.chips + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.jeluchu.jchucomponentscompose.core.extensions.ints.isNotEmpty +import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty +import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage + +/** + * + * Author: @Jeluchu + * + * This component is similar to the Chips, + * in which you can display a text or a text and an icon + * + * @sample ChipTagViewPreview + * + * @param modifier modifier that will be used to change the color, size... + * @param title text to be displayed on the chip + * @param textColor color of the text to be displayed inside the chip + * @param isIconShow in case you want to show an icon or not (by default it is deactivated) + * @param iconUrl link of the image you want to be displayed as an icon on the chip + * @param iconResource drawable id of the resource you want to be displayed as an icon on the chip + * @param iconTintColor color of the icon (only if [iconResource] is being used) displayed on the chip + * @param modifierIcon custom modifier for the displayed icon (currently there is a default padding) + * + */ + +@Composable +fun ChipTagView( + modifier: Modifier, + title: String, + textColor: Color, + isIconShow: Boolean = false, + iconUrl: String = String.empty(), + iconResource: Int = 0, + iconTintColor: Color = Color.White, + modifierIcon: Modifier = Modifier.padding(8.dp, 6.dp, 12.dp, 6.dp), +) { + Box( + modifier = modifier + ) { + + Row( + modifier = Modifier.wrapContentWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + + if (isIconShow) { + + Column { + + if (iconUrl.isNotEmpty()) { + NetworkImage( + modifier = Modifier + .size(23.dp) + .padding(start = 7.dp), + contentScale = ContentScale.Fit, + url = iconUrl + ) + } else if (iconResource.isNotEmpty()) { + Icon( + modifier = Modifier + .size(23.dp) + .padding(start = 7.dp), + painter = painterResource(id = iconResource), + tint = iconTintColor, + contentDescription = null + ) + } + + } + + } + + Column { + Text( + text = title, + modifier = if (isIconShow) modifierIcon else Modifier.padding( + 12.dp, + 6.dp, + 12.dp, + 6.dp + ), + fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.caption, + color = textColor + ) + } + + } + + } +} + +@Preview +@Composable +fun ChipTagViewPreview() { + ChipTagView( + modifier = Modifier + .wrapContentSize() + .clip(RoundedCornerShape(10.dp)) + .background(Color.Blue.copy(.2f)), + title = "Name", + textColor = Color.White + ) +} diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/YoutubeChip.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/YoutubeChip.kt new file mode 100644 index 00000000..93fe2e6c --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/YoutubeChip.kt @@ -0,0 +1,79 @@ +package com.jeluchu.jchucomponentscompose.ui.chips + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +/** + * + * Author: @Jeluchu + * + * This component is similar to the chips in the YouTube app + * + * @sample YoutubeChipPreview + * + * @param modifier modifier that will be used to change the color, size... + * @param selected status (selected or not) + * @param text text to be displayed inside the chip + * + */ + +@Composable +fun YoutubeChip( + modifier: Modifier = Modifier, + selected: Boolean, + text: String, +) { + Surface( + color = when { + selected -> MaterialTheme.colors.onSurface.copy( + alpha = if (MaterialTheme.colors.isLight) 0.7f else 1f + ) + else -> MaterialTheme.colors.onSurface.copy( + alpha = if (MaterialTheme.colors.isLight) 0.04f else 0.07f + ) + }, + contentColor = when { + selected -> MaterialTheme.colors.surface + else -> MaterialTheme.colors.onSurface + }, + shape = CircleShape, + border = BorderStroke( + width = 1.dp, + color = when { + selected -> MaterialTheme.colors.surface + else -> if (MaterialTheme.colors.isLight) Color.LightGray else Color.DarkGray + } + ), + modifier = modifier + ) { + Text( + text = text, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.body2, + modifier = Modifier.padding( + vertical = 8.dp, + horizontal = 12.dp, + ) + ) + } +} + +@Preview +@Composable +fun YoutubeChipPreview() { + YoutubeChip( + modifier = Modifier.padding(horizontal = 4.dp), + selected = true, + text = "Name" + ) +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/DoubleTapAnimation.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/DoubleTapAnimation.kt index f079b954..93d57487 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/DoubleTapAnimation.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/DoubleTapAnimation.kt @@ -20,6 +20,21 @@ import androidx.compose.ui.unit.dp import com.jeluchu.jchucomponentscompose.core.extensions.ints.empty import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty +/** + * + * Author: @Jeluchu + * + * This component performs the same function as the animation + * when you double click on an image to "like" it on Instagram + * + * @param imageRemote link of the remote image to be displayed + * @param imageResources drawable resource of the remote image to be displayed + * @param iconResource drawable of the icon you want to display when pressing + * @param size maximum animation size + * @param onDoubleTap action to be performed after the double-tap animation is performed + * + */ + @Composable fun DoubleTapAnimation( imageRemote: String = String.empty(), @@ -68,7 +83,6 @@ fun DoubleTapAnimation( }) } - if (isLike) { Icon( painterResource(id = iconResource), @@ -82,5 +96,6 @@ fun DoubleTapAnimation( isLike = false } } + } } diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/NetworkImage.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/NetworkImage.kt index d2c57fe6..a8819c8c 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/NetworkImage.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/NetworkImage.kt @@ -5,7 +5,18 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import coil.compose.rememberImagePainter -import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty + +/** + * + * Author: @Jeluchu + * + * This component is used to upload images via network + * + * @param url link to image requiring internet (based on Coil) + * @param modifier custom modifier for the displayed icon (currently there is a default padding) + * @param contentScale type of scale for the image + * + */ @Composable fun NetworkImage( diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/CircularLoading.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/CircularLoading.kt index 88dfd9d7..0e268e4a 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/CircularLoading.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/CircularLoading.kt @@ -5,16 +5,29 @@ import androidx.compose.material.CircularProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview import androidx.constraintlayout.compose.ConstraintLayout +/** + * + * Author: @Jeluchu + * + * This is a component for displaying a circular loading progress + * + * @sample CircularLoadingPreview + * + * @param isShow the status of whether or not to display + * @param colorLoading color of the circular progress bar + * + */ + @Composable fun CircularLoading( isShow: Boolean, - modifier: Modifier = Modifier.fillMaxSize(), colorLoading: Color = Color.Black ) { - ConstraintLayout(modifier = modifier) { + ConstraintLayout(modifier = Modifier.fillMaxSize()) { val (progress) = createRefs() @@ -32,4 +45,13 @@ fun CircularLoading( } +} + +@Preview +@Composable +fun CircularLoadingPreview() { + CircularLoading( + isShow = true, + colorLoading = Color.Black + ) } \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/PulseLoading.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/PulseLoading.kt index bbbc74cf..a18b6d7a 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/PulseLoading.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/PulseLoading.kt @@ -12,16 +12,33 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +/** + * + * Author: @Jeluchu + * + * This component is a loading with pulse animation + * + * @sample PulseLoadingPreview + * + * @param durationMillis animation duration + * @param maxPulseSize maximum size of circular animation + * @param minPulseSize minimum size of circular animation + * @param pulseColor color of the circular animation + * @param centreColor color of the main circle + * + */ + @Composable fun PulseLoading( - durationMillis:Int = 1000, - maxPulseSize:Float = 300f, - minPulseSize:Float = 50f, - pulseColor: Color = Color(234,240,246), - centreColor:Color = Color(66,133,244) -){ + durationMillis: Int = 1000, + maxPulseSize: Float = 300f, + minPulseSize: Float = 50f, + pulseColor: Color = Color(234, 240, 246), + centreColor: Color = Color(66, 133, 244) +) { val infiniteTransition = rememberInfiniteTransition() val size by infiniteTransition.animateFloat( initialValue = minPulseSize, @@ -39,17 +56,28 @@ fun PulseLoading( repeatMode = RepeatMode.Restart ) ) - Box(contentAlignment = Alignment.Center,modifier = Modifier.fillMaxSize()) { + Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { Card( shape = CircleShape, - modifier = Modifier.size(size.dp).align(Alignment.Center).alpha(alpha), + modifier = Modifier + .size(size.dp) + .align(Alignment.Center) + .alpha(alpha), backgroundColor = pulseColor, elevation = 0.dp ) {} - Card(modifier = Modifier - .size(minPulseSize.dp) - .align(Alignment.Center), + Card( + modifier = Modifier + .size(minPulseSize.dp) + .align(Alignment.Center), shape = CircleShape, - backgroundColor = centreColor){} + backgroundColor = centreColor + ) {} } } + +@Preview +@Composable +fun PulseLoadingPreview() { + PulseLoading() +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/sheets/BottomSheetWithClose.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/sheets/BottomSheetWithClose.kt index 042f44f9..e8a4adf5 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/sheets/BottomSheetWithClose.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/sheets/BottomSheetWithClose.kt @@ -14,27 +14,35 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +/** + * + * Author: @Jeluchu + * + * This component displays a BottomSheet with an icon to close it + * + * @param content change the color of the system notification bar + * @param modifier modifier that will be used to change the color, size... + * @param onClosePressed action that will be performed when the close button is pressed + * @param closeButtonColor color of the close button + * + */ + @Composable fun BottomSheetWithCloseDialog( - onClosePressed: () -> Unit, + content: @Composable () -> Unit, modifier: Modifier = Modifier, + onClosePressed: () -> Unit, closeButtonColor: Color = Color.Gray, - content: @Composable () -> Unit ) { Box(modifier.fillMaxWidth()) { content() - IconButton( onClick = onClosePressed, modifier = Modifier .align(Alignment.TopEnd) .padding(16.dp) .size(29.dp) - - ) { - Icon(Icons.Filled.Close, tint = closeButtonColor, contentDescription = null) - } - + ) { Icon(Icons.Filled.Close, tint = closeButtonColor, contentDescription = null) } } } \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/systemui/SystemBarsColors.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/systemui/SystemBarsColors.kt index ede812e7..ca739a89 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/systemui/SystemBarsColors.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/systemui/SystemBarsColors.kt @@ -6,6 +6,18 @@ import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color import com.google.accompanist.systemuicontroller.rememberSystemUiController +/** + * + * Author: @Jeluchu + * + * This component makes adjustments + * to the system interface of the cell phone + * + * @param systemBarsColor change the color of the system navigation bar + * @param statusBarColor change the color of the system notification bar + * + */ + @Composable fun SystemStatusBarColors( systemBarsColor: Color, diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/tags/Tags.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/tags/Tags.kt deleted file mode 100644 index 508b7631..00000000 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/tags/Tags.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.jeluchu.jchucomponentscompose.ui.tags - -import androidx.compose.foundation.layout.* -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty -import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage - -@Composable -fun Tag( - modifier: Modifier, - name: String, - textColor: Color, - isIconShow: Boolean = false, - iconUrl: String = String.empty() -) { - ChipTagView( - modifier = modifier, - name = name, - textColor = textColor, - isIconShow = isIconShow, - iconUrl = iconUrl - ) -} - - -@Composable -fun ChipTagView( - modifier: Modifier, - name: String, - textColor: Color, - isIconShow: Boolean = false, - modifierTextIcon: Modifier = Modifier.padding(8.dp, 6.dp, 12.dp, 6.dp), - iconUrl: String = String.empty() -) { - Box( - modifier = modifier - ) { - - Row( - modifier = Modifier.wrapContentWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - - if (isIconShow && iconUrl.isNotEmpty()) { - - Column { - NetworkImage( - modifier = Modifier - .size(23.dp) - .padding(start = 7.dp), - contentScale = ContentScale.Fit, - url = iconUrl - ) - } - - } - - Column { - Text( - text = name, - modifier = if (isIconShow) modifierTextIcon else Modifier.padding( - 12.dp, - 6.dp, - 12.dp, - 6.dp - ), - fontWeight = FontWeight.Bold, - style = MaterialTheme.typography.caption, - color = textColor - ) - } - - } - - } -} - -@Preview -@Composable -fun TagPreviewLight() { - Tag( - Modifier, - name = "Male", - textColor = Color.White - ) -} diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/textfields/CountTextField.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/textfields/CountTextField.kt new file mode 100644 index 00000000..fcb1c6a6 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/textfields/CountTextField.kt @@ -0,0 +1,107 @@ +package com.jeluchu.jchucomponentscompose.ui.textfields + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Close +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty + +/** + * + * Author: @Jeluchu + * + * This component is based on EditText in which you can check + * how many characters you have typed and what is the maximum + * + * @sample CountTextFieldPreview + * + * @param title title to be displayed at the top of the EditText / TextField + * @param maxLength maximum number of characters the user can type + * @param backgroundColor the lambda to be invoked when this icon is pressed + * @param disabledLabelColor color to be displayed when Edit Text / Text Field is disabled + * @param counterTextColor color of the counter (text) that appears below the Edit Text / Text Field + * + */ + +@Composable +fun CountTextField( + title: String, + maxLength: Int, + backgroundColor: Color, + disabledLabelColor: Color, + counterTextColor: Color +) { + + Column { + var textState by remember { mutableStateOf(String.empty()) } + + Text( + text = title, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 4.dp), + textAlign = TextAlign.Start, + color = counterTextColor + ) + + TextField( + modifier = Modifier.fillMaxWidth(), + value = textState, + colors = TextFieldDefaults.textFieldColors( + backgroundColor = backgroundColor, + cursorColor = Color.Black, + disabledLabelColor = disabledLabelColor, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ), + onValueChange = { + if (it.length <= maxLength) textState = it + }, + shape = RoundedCornerShape(8.dp), + singleLine = true, + trailingIcon = { + if (textState.isNotEmpty()) { + IconButton(onClick = { textState = "" }) { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = null + ) + } + } + } + ) + Text( + text = "${textState.length} / $maxLength", + modifier = Modifier + .fillMaxWidth() + .padding(top = 4.dp), + textAlign = TextAlign.End, + color = counterTextColor + ) + } + +} + + +@Preview +@Composable +fun CountTextFieldPreview() { + + CountTextField( + title = "Name", + maxLength = 110, + backgroundColor = Color(0xffd8e6ff), + disabledLabelColor = Color(0xffd8e6ff), + counterTextColor = Color(0xff76a9ff) + ) + +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/time/Time.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/time/Time.kt index c9e36097..f5d32295 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/time/Time.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/time/Time.kt @@ -5,6 +5,15 @@ import com.jeluchu.jchucomponentscompose.core.extensions.ints.fixedDecimalsTime import kotlinx.coroutines.delay import java.util.* +/** + * + * Author: @Jeluchu + * + * This component only returns the current time (it is a kind of clock) + * which is updated every minute. + * + */ + @Composable fun minutesLeft(): String { @@ -34,4 +43,4 @@ data class Time( var hours: String, var minutes: String, var zoneDay: String -) \ No newline at end of file +) diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/GraphicUtils.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/GraphicUtils.kt new file mode 100644 index 00000000..68d87de8 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/GraphicUtils.kt @@ -0,0 +1,42 @@ +package com.jeluchu.jchucomponentscompose.utils + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.view.View +import androidx.appcompat.widget.LinearLayoutCompat + +/** + * + * Author: @Jeluchu + * + * Class to generate Bitmaps based on a view element. + * You can include a Composable inside an AndroidView + * to create it in a view and transform it to an image. + * + */ + +class GraphicUtils { + + fun createBitmapFromView(view: View, width: Int, height: Int): Bitmap { + view.layoutParams = LinearLayoutCompat.LayoutParams( + LinearLayoutCompat.LayoutParams.WRAP_CONTENT, + LinearLayoutCompat.LayoutParams.WRAP_CONTENT + ) + + view.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY) + ) + + view.layout(0, 0, width, height) + + val canvas = Canvas() + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + + canvas.setBitmap(bitmap) + view.draw(canvas) + + return bitmap + } + +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/broadcast/CustomTabsCopyReceiver.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/broadcast/CustomTabsCopyReceiver.kt index 32b4dc10..567b4863 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/broadcast/CustomTabsCopyReceiver.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/broadcast/CustomTabsCopyReceiver.kt @@ -1,9 +1,20 @@ package com.jeluchu.jchucomponentscompose.utils.broadcast -import android.content.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent import android.widget.Toast import com.jeluchu.jchucomponentscompose.core.extensions.context.addToClipboard +/** + * + * Author: @Jeluchu + * + * This class is used to show inside + * a CustomTab the option of copying the url + * + */ + class CustomTabsCopyReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent) { val url = intent.dataString diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/broadcast/ShareBroadcastReceiver.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/broadcast/ShareBroadcastReceiver.kt index 7538db6c..8a383e4b 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/broadcast/ShareBroadcastReceiver.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/broadcast/ShareBroadcastReceiver.kt @@ -4,6 +4,14 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +/** + * + * Author: @Jeluchu + * + * This class is used to show inside + * a CustomTab the option of share the url + * + */ class ShareBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val url = intent.dataString diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/firebase/FirebaseAnalyticsManager.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/firebase/FirebaseAnalyticsManager.kt new file mode 100644 index 00000000..ffb97a7f --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/firebase/FirebaseAnalyticsManager.kt @@ -0,0 +1,58 @@ +package com.jeluchu.jchucomponentscompose.utils.firebase + +import android.content.Context +import android.os.Bundle +import android.os.Parcelable +import android.util.Log +import com.google.firebase.analytics.FirebaseAnalytics +import java.io.Serializable + +/** + * + * Author: @Jeluchu + * + * This class is used to send custom analytics to Firebase + * + * Send an event + * [logEvent] @param event name + * [logEvent] @param properties for event + * + * Send user property + * [setUserProperty] @param key of property + * [setUserProperty] @param value of property + * + * Set `user id` for firebase analytic + * [setUserId] @param userId [please follow the firebase document rule] + * + */ +class FirebaseAnalyticsManager(private val context: Context) { + + private val mFirebaseAnalytics: FirebaseAnalytics + get() { + return FirebaseAnalytics.getInstance(context) + } + + fun logEvent(event: String, properties: Map) { + val bundle = Bundle().apply { + properties.forEach { + when (val value: Any = it.value) { + is Int -> putInt(it.key, value) + is Long -> putLong(it.key, value) + is String -> putString(it.key, value) + is Parcelable -> putParcelable(it.key, value) + is Serializable -> putSerializable(it.key, value) + else -> Log.e("error::", "unknown key: $it.key and value: $value") + } + } + } + + mFirebaseAnalytics.logEvent(event, bundle) + } + + fun setUserProperty(key: String, value: String) = + mFirebaseAnalytics.setUserProperty(key, value) + + fun setUserId(userId: String) = + mFirebaseAnalytics.setUserId(userId) + +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/mediaplayer/MediaPlayerHolder.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/mediaplayer/MediaPlayerHolder.kt index d227317b..d299cd82 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/mediaplayer/MediaPlayerHolder.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/mediaplayer/MediaPlayerHolder.kt @@ -3,13 +3,29 @@ package com.jeluchu.jchucomponentscompose.utils.mediaplayer import android.content.Context import android.media.MediaPlayer import android.net.Uri -import com.jeluchu.inook.core.utils.mediaplayer.PlaybackInfoListener import com.jeluchu.jchucomponentscompose.core.extensions.ints.milliSecondsToTimer import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit +/** + * + * Author: @Jeluchu + * + * This class is used to play music with MediaPlayer + * + * Once the [MediaPlayer] is released, it can't be used again, and another one has to be + * created. In the onStop() method of the Activity the [MediaPlayer] is + * released. Then in the onStart() of the Activity a new [MediaPlayer] + * object has to be created. That's why this method is private, and called by load(int) and + * not the constructor. + * + * References [PlayerAdapter] and [PlaybackInfoListener] + * + */ + + class MediaPlayerHolder(context: Context) : PlayerAdapter { private val mContext: Context = context.applicationContext private var mMediaPlayer: MediaPlayer? = null @@ -18,18 +34,11 @@ class MediaPlayerHolder(context: Context) : PlayerAdapter { private var mExecutor: ScheduledExecutorService? = null private var mSeekbarPositionUpdateTask: Runnable? = null - /** - * Once the [MediaPlayer] is released, it can't be used again, and another one has to be - * created. In the onStop() method of the [MainActivity] the [MediaPlayer] is - * released. Then in the onStart() of the [MainActivity] a new [MediaPlayer] - * object has to be created. That's why this method is private, and called by load(int) and - * not the constructor. - */ private fun initializeMediaPlayer() { if (mMediaPlayer == null) { mMediaPlayer = MediaPlayer() mMediaPlayer!!.setOnCompletionListener { - stopUpdatingCallbackWithPosition(true) + stopUpdatingCallbackWithPosition() if (mPlaybackInfoListener != null) { mPlaybackInfoListener!!.onStateChanged(PlaybackInfoListener.State.COMPLETED) mPlaybackInfoListener!!.onPlaybackCompleted() @@ -110,7 +119,7 @@ class MediaPlayerHolder(context: Context) : PlayerAdapter { if (mPlaybackInfoListener != null) { mPlaybackInfoListener!!.onStateChanged(PlaybackInfoListener.State.RESET) } - stopUpdatingCallbackWithPosition(true) + stopUpdatingCallbackWithPosition() } } @@ -144,13 +153,12 @@ class MediaPlayerHolder(context: Context) : PlayerAdapter { ) } - // Reports media playback position to mPlaybackProgressCallback. - private fun stopUpdatingCallbackWithPosition(resetUIPlaybackPosition: Boolean) { + private fun stopUpdatingCallbackWithPosition() { if (mExecutor != null) { mExecutor!!.shutdownNow() mExecutor = null mSeekbarPositionUpdateTask = null - if (resetUIPlaybackPosition && mPlaybackInfoListener != null) { + if (mPlaybackInfoListener != null) { mPlaybackInfoListener!!.onPositionChanged(0) } } diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/mediaplayer/PlaybackInfoListener.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/mediaplayer/PlaybackInfoListener.kt index 20b3354e..01b1479c 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/mediaplayer/PlaybackInfoListener.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/mediaplayer/PlaybackInfoListener.kt @@ -1,4 +1,4 @@ -package com.jeluchu.inook.core.utils.mediaplayer +package com.jeluchu.jchucomponentscompose.utils.mediaplayer abstract class PlaybackInfoListener { diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/network/NetworkUtils.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/network/NetworkUtils.kt new file mode 100644 index 00000000..44fc6d87 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/network/NetworkUtils.kt @@ -0,0 +1,50 @@ +package com.jeluchu.jchucomponentscompose.utils.network + +import com.jeluchu.jchucomponentscompose.utils.network.NetworkUtils.saveResponseBodyToFile +import okhttp3.ResponseBody +import java.io.File +import java.io.FileOutputStream + +/** + * + * Author: @Jeluchu + * + * Save response body to specific file with progressing callback + * [saveResponseBodyToFile] @param filePath Path of file that store the response body + * [saveResponseBodyToFile] @param responseBody Response body from network call + * [saveResponseBodyToFile] @param progress Progressing percent callback + * + */ + +object NetworkUtils { + + fun saveResponseBodyToFile( + filePath: String, + responseBody: ResponseBody, + progress: (percent: Long) -> Unit + ) { + responseBody.let { body -> + File(filePath).let { file -> + val outputStream = FileOutputStream(file) + body.source().also { + val buffer = ByteArray(4096) + var totalBytesRead = 0L + while (true) { + val byteRead = it.read(buffer) + if (byteRead < 0) { + break + } + outputStream.write(buffer, 0, byteRead) + // Downloading progress in percent + totalBytesRead += byteRead + val percent = (totalBytesRead * 100) / body.contentLength() + progress(percent) + } + } + outputStream.flush() + outputStream.close() + } + } + } + +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/validators/CreditCardValidator.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/validators/CreditCardValidator.kt new file mode 100644 index 00000000..993f39d6 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/validators/CreditCardValidator.kt @@ -0,0 +1,81 @@ +package com.jeluchu.jchucomponentscompose.utils.validators + +/** + * + * Author: @Jeluchu + * + * Class to validate credit card number + * + */ + +class CreditCardValidator private constructor() { + + enum class CardType { + VISA { + override fun brand() = "VISA CARD" + override fun pattern(): String = "4[0-9]{12}(?:[0-9]{3})?" + }, + MASTER_CARD { + override fun brand() = "MASTER CARD" + override fun pattern(): String = "5[1-5][0-9]{14}" + }, + AMERICAN_EXPRESS { + override fun brand() = "AMERICAN EXPRESS" + override fun pattern(): String = "3[47][0-9]{13}" + }, + DINNER_CLUB { + override fun brand() = "DINNER CLUB" + override fun pattern(): String = "3(?:0[0-5]|[68][0-9])?[0-9]{11}" + }, + DISCOVER { + override fun brand() = "DISCOVER" + override fun pattern(): String = "6(?:011|5[0-9]{2})[0-9]{12}" + }, + JCB { + override fun brand() = "JCB" + override fun pattern(): String = "(?:2131|1800|35[0-9]{3})[0-9]{11}" + }, + UNKNOWN { + override fun brand() = "UNKNOWN" + override fun pattern(): String = "" + }; + + abstract fun brand(): String + abstract fun pattern(): String + + companion object { + fun patternList() = listOf( + VISA, + MASTER_CARD, + AMERICAN_EXPRESS, + DINNER_CLUB, + DISCOVER, + JCB + ) + } + } + + companion object { + + /** + * Get credit card type + * @param card Card number (include [-] and [space] bot not any special characters) + * @return [CardType] + */ + private fun getCardType(card: String): CardType { + val cardNumber = + card.replace(" ".toRegex(), "") + .replace("-".toRegex(), "") + return CardType.patternList() + .firstOrNull { cardNumber.matches(it.pattern().toRegex()) } ?: CardType.UNKNOWN + } + + /** + * Check credit card number is valid + * @param card card number (include [-] and [space] bot not any special characters) + * @return [Boolean] (credit card is valid) + */ + fun isValidCard(card: String): Boolean = getCardType(card) != CardType.UNKNOWN + + } +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/validators/Validators.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/validators/Validators.kt new file mode 100644 index 00000000..ad70dd67 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/validators/Validators.kt @@ -0,0 +1,52 @@ +package com.jeluchu.jchucomponentscompose.utils.validators + +import android.annotation.SuppressLint +import android.text.TextUtils +import androidx.core.util.PatternsCompat +import com.jeluchu.jchucomponentscompose.core.extensions.strings.isEmptyString + +/** + * + * Author: @Jeluchu + * + * Class to validate texts, for example you can validate + * if it is a phone, an e-mail, an IP or a valid url + * + */ + +@SuppressLint("RestrictedApi") +object Validators { + + val String.isValidEmail: Boolean + get() = + !TextUtils.isEmpty(this) && PatternsCompat.AUTOLINK_EMAIL_ADDRESS.matcher(this) + .matches() + + val String.isValidUrl: Boolean + get() = + !TextUtils.isEmpty(this) && PatternsCompat.AUTOLINK_WEB_URL.matcher(this).matches() + + val String.isValidIpAddress: Boolean + get() = + !TextUtils.isEmpty(this) && PatternsCompat.IP_ADDRESS.matcher(this).matches() + + val String.isPhone: Boolean + get() = + matches("^1([34578])\\d{9}\$".toRegex()) + + val String.isNumeric: Boolean + get() = + matches("^[0-9]+$".toRegex()) + + val String.isAlphanumeric + get() = + matches("^[a-zA-Z0-9]*$".toRegex()) + + val String.isAlphabetic + get() = + matches("^[a-zA-Z]*$".toRegex()) + + fun String.isLocal() = + !isEmptyString() && (startsWith("http://") || startsWith("https://")) + +} \ No newline at end of file