diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index be86d4b..dcfd735 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -26,7 +26,7 @@ If applicable, add screenshots to help explain your problem. **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - - Library version [e.g. 0.4.2] + - Library version [e.g. 0.5.0] **Additional context** Add any other context about the problem here. diff --git a/README.md b/README.md index 668e036..a7164ae 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Also add the SquircleView dependency to your app build.gradle ```groovy dependencies { - implementation "app.juky:squircleview:0.4.2" + implementation "app.juky:squircleview:0.5.0" } ``` @@ -322,6 +322,11 @@ Check out the [CONTRIBUTING.md](CONTRIBUTING.md) file to know more ## Changelog +- V0.5.0 (8 april 2022) + - Updated Gradle, Kotlin and dependencies + - Added Jetpack Compose Path components and paths + - Fixed SquircleImageView crash #15 + - Fixed issue where clickable and focusable were set to true by default - V0.4.2 (4 august 2021) - Fixed issue where the text appearance of the button would override any custom styling set for the following attributes: android:fontFamily, android:textStyle, android:textAllCaps, android:textSize, android:letterSpacing diff --git a/build.gradle b/build.gradle index e7bdad5..0404ce2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.5.21" + ext.kotlin_version = "1.6.10" repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' + classpath 'com.android.tools.build:gradle:7.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cc6f63b..4637033 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri Jul 30 21:49:10 CEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/library/build.gradle b/library/build.gradle index f3223be..3833810 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -35,14 +35,12 @@ if (pgpKeyContent != null) { } android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdkVersion 32 + buildToolsVersion '32.0.0' defaultConfig { minSdkVersion 23 - targetSdkVersion 30 - versionCode 1 - versionName "0.4.2" + targetSdkVersion 32 project.archivesBaseName = "SquircleView" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -59,10 +57,11 @@ android { dependencies { // Default implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - implementation 'androidx.core:core-ktx:1.6.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'com.google.android.material:material:1.5.0' + implementation("androidx.compose.material:material:1.1.1") // Testing testImplementation 'junit:junit:4.13.2' diff --git a/library/src/main/java/app/juky/squircleview/data/SquircleCore.kt b/library/src/main/java/app/juky/squircleview/data/SquircleCore.kt index 66d0ff4..2d7fe71 100644 --- a/library/src/main/java/app/juky/squircleview/data/SquircleCore.kt +++ b/library/src/main/java/app/juky/squircleview/data/SquircleCore.kt @@ -135,8 +135,9 @@ class SquircleCore(context: Context, attrs: AttributeSet?, view: View) { */ private fun loadDefaultStyle(context: Context, view: View, attrs: AttributeSet?) { if (view is SquircleButton || view is SquircleImageView || view is SquircleConstraintLayout) { - view.isClickable = true - view.isFocusable = true + val isButton = view is SquircleButton + view.isClickable = attrs?.getAttributeBooleanValue(android.R.attr.clickable, isButton) ?: isButton + view.isFocusable = attrs?.getAttributeBooleanValue(android.R.attr.focusable, isButton) ?: isButton // Set ripple if enabled if (rippleEnabled && view.hasOnClickListeners()) { diff --git a/library/src/main/java/app/juky/squircleview/utils/SquircleComposeShape.kt b/library/src/main/java/app/juky/squircleview/utils/SquircleComposeShape.kt new file mode 100644 index 0000000..ed06b4c --- /dev/null +++ b/library/src/main/java/app/juky/squircleview/utils/SquircleComposeShape.kt @@ -0,0 +1,116 @@ +package app.juky.squircleview.utils + + +import android.graphics.RectF +import androidx.annotation.IntRange +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection +import app.juky.squircleview.data.Constants.DEFAULT_CORNER_SMOOTHING +import app.juky.squircleview.utils.SquirclePath.getRadiusByHeightOrWidth + +object SquircleComposeShape { + fun getSquirclePath( + size: Size, + @IntRange(from = 0, to = DEFAULT_CORNER_SMOOTHING) + cornerSmoothing: Int = DEFAULT_CORNER_SMOOTHING.toInt() + ): Path { + return getSquirclePath( + rect = RectF(0f, 0f, size.width, size.height), + width = size.width.toInt(), + height = size.height.toInt(), + cornerSmoothing = cornerSmoothing + ) + } + + fun getSquirclePath( + rect: RectF, + width: Int, + height: Int, + @IntRange(from = 0, to = DEFAULT_CORNER_SMOOTHING) + cornerSmoothing: Int = DEFAULT_CORNER_SMOOTHING.toInt() + ): Path { + val radius = getRadiusByHeightOrWidth(height, width, cornerSmoothing) + + val startX = rect.left + val endX = rect.right + val startY = rect.top + val endY = rect.bottom + + val path = Path() + + path.moveTo(x = startX, y = (height / 2).toFloat()) + + // Top left corner + path.cubicTo( + x1 = startX, + y1 = startY, + x2 = startX, + y2 = startY, + x3 = startX + radius, + y3 = startY + ) + + // Top line + path.lineTo(x = endX - radius, y = startY) + + // Top right corner + path.cubicTo( + x1 = endX, + y1 = startY, + x2 = endX, + y2 = startY, + x3 = endX, + y3 = startY + radius, + ) + + // Right line + path.lineTo(x = endX, y = endY - radius) + + // Bottom right corner + path.cubicTo( + x1 = endX, + y1 = endY, + x2 = endX, + y2 = endY, + x3 = endX - radius, + y3 = endY, + ) + + // Bottom line + path.lineTo(x = startX + radius, y = endY) + + // Bottom left corner + path.cubicTo( + x1 = startX, + y1 = endY, + x2 = startX, + y2 = endY, + x3 = startX, + y3 = endY - radius, + ) + + path.lineTo(x = startX, y = (height / 2).toFloat()) + + return path + } + + class SquircleComposeShape( + @IntRange(from = 0, to = DEFAULT_CORNER_SMOOTHING) + val cornerSmoothing: Int = DEFAULT_CORNER_SMOOTHING.toInt() + ) : Shape { + override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline { + return Outline.Generic( + getSquirclePath( + rect = RectF(0f, 0f, size.width, size.height), + width = size.width.toInt(), + height = size.height.toInt(), + cornerSmoothing = cornerSmoothing + ) + ) + } + } +} \ No newline at end of file diff --git a/library/src/main/java/app/juky/squircleview/utils/SquirclePath.kt b/library/src/main/java/app/juky/squircleview/utils/SquirclePath.kt index 486df22..0ec6be2 100644 --- a/library/src/main/java/app/juky/squircleview/utils/SquirclePath.kt +++ b/library/src/main/java/app/juky/squircleview/utils/SquirclePath.kt @@ -8,7 +8,7 @@ import app.juky.squircleview.data.Constants.MAX_DEFAULT_CORNER_SMOOTHING import kotlin.math.min object SquirclePath { - internal fun getRadiusByHeightOrWidth( + fun getRadiusByHeightOrWidth( height: Int, width: Int, @IntRange(from = 0, to = DEFAULT_CORNER_SMOOTHING) diff --git a/library/src/main/java/app/juky/squircleview/views/SquircleImageView.kt b/library/src/main/java/app/juky/squircleview/views/SquircleImageView.kt index 3ee26ea..9ae17de 100644 --- a/library/src/main/java/app/juky/squircleview/views/SquircleImageView.kt +++ b/library/src/main/java/app/juky/squircleview/views/SquircleImageView.kt @@ -71,6 +71,7 @@ class SquircleImageView(context: Context, attrs: AttributeSet?) : AppCompatImage // Support image library view loading override fun setImageDrawable(drawable: Drawable?) { drawable ?: return + style ?: return style.setBackgroundImage(drawable) } diff --git a/sample/build.gradle b/sample/build.gradle index 032eab0..9476d42 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -5,13 +5,13 @@ plugins { } android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdkVersion 32 + buildToolsVersion '32.0.0' defaultConfig { applicationId "app.juky.squircleview.sample" minSdkVersion 23 - targetSdkVersion 30 + targetSdkVersion 32 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -34,31 +34,39 @@ android { } packagingOptions { - exclude 'META-INF/core.kotlin_module' + resources { + excludes += ['META-INF/core.kotlin_module'] + } + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.1.1" } buildFeatures { viewBinding true + compose true } } dependencies { // Default libraries implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" - implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'com.google.android.material:material:1.4.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + 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("androidx.compose.material:material:1.1.1") // Import the library implementation project(':library') // Test the library using the Maven central / local dependency - //implementation 'app.juky:squircleview:0.4.2' + //implementation 'app.juky:squircleview:0.5.0' // Image loading - implementation 'com.github.bumptech.glide:glide:4.12.0' - kapt 'com.github.bumptech.glide:compiler:4.12.0' + implementation 'com.github.bumptech.glide:glide:4.13.1' + kapt 'com.github.bumptech.glide:compiler:4.13.1' // Testing testImplementation 'junit:junit:4.13.2' diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index b675803..09d5b26 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.SquircleView"> - + diff --git a/sample/src/main/java/app/juky/squircleview/sample/MainActivity.kt b/sample/src/main/java/app/juky/squircleview/sample/MainActivity.kt index 333fae2..e843d4e 100644 --- a/sample/src/main/java/app/juky/squircleview/sample/MainActivity.kt +++ b/sample/src/main/java/app/juky/squircleview/sample/MainActivity.kt @@ -6,8 +6,25 @@ import android.os.Handler import android.os.Looper import android.widget.SeekBar import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material.Text +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import app.juky.squircleview.sample.databinding.ActivityMainBinding +import app.juky.squircleview.utils.SquircleComposeShape.SquircleComposeShape +import app.juky.squircleview.utils.SquircleComposeShape.getSquirclePath import app.juky.squircleview.utils.SquircleShape import app.juky.squircleview.views.SquircleImageView import com.bumptech.glide.Glide @@ -58,6 +75,39 @@ class MainActivity : AppCompatActivity() { this.color = ContextCompat.getColor(this@MainActivity, R.color.teal_700) } } + binding.constraintLayoutWithShapeDrawableCompose.setContent { + val borderGradient = Brush.linearGradient( + colors = listOf( + colorResource(id = R.color.teal_200), + colorResource(id = R.color.teal_700) + ) + ) + + Column( + modifier = Modifier + .fillMaxWidth() + .height(150.dp) + .graphicsLayer( + shadowElevation = 8f, + shape = SquircleComposeShape(cornerSmoothing = 80), + clip = true + ) + .background(color = colorResource(id = R.color.purple_200)) + .drawBehind { + drawPath( + path = getSquirclePath(size, cornerSmoothing = 80), + brush = borderGradient, + style = Stroke( + width = 3.dp.toPx() + ) + ) + }, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(stringResource(R.string.app_name)) + } + } binding.normalButton.setOnClickListener { // Just demonstrating a ripple will only work with a click listener diff --git a/sample/src/main/res/drawable/first_image.jpg b/sample/src/main/res/drawable/first_image.jpg index 4861f6b..4458cef 100644 Binary files a/sample/src/main/res/drawable/first_image.jpg and b/sample/src/main/res/drawable/first_image.jpg differ diff --git a/sample/src/main/res/drawable/second_image.jpg b/sample/src/main/res/drawable/second_image.jpg index 8fd476f..65eec71 100644 Binary files a/sample/src/main/res/drawable/second_image.jpg and b/sample/src/main/res/drawable/second_image.jpg differ diff --git a/sample/src/main/res/drawable/third_image.jpg b/sample/src/main/res/drawable/third_image.jpg index c49ab43..25ea468 100644 Binary files a/sample/src/main/res/drawable/third_image.jpg and b/sample/src/main/res/drawable/third_image.jpg differ diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index bb9142f..bd843a6 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -304,5 +304,14 @@ + + \ No newline at end of file