diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..fb7f4a8
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..058aee2
--- /dev/null
+++ b/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..2370474
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 941ce03..ca65a49 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,11 +3,11 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 28
+ compileSdkVersion 31
defaultConfig {
applicationId "com.divyanshu.androiddraw"
minSdkVersion 21
- targetSdkVersion 28
+ targetSdkVersion 31
versionCode 3
versionName "1.0.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -31,19 +31,19 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
//AndroidX
- implementation 'androidx.appcompat:appcompat:1.0.2'
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
- implementation 'com.google.android.material:material:1.0.0'
+ implementation 'com.google.android.material:material:1.4.0'
//Annotation
- implementation 'com.github.bumptech.glide:glide:4.8.0'
- annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
+ implementation 'com.github.bumptech.glide:glide:4.12.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
//Test
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test:runner:1.2.0'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test:runner:1.4.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e41b6c1..3afd872 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -11,7 +11,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
-
+
diff --git a/build.gradle b/build.gradle
index e0bf29a..61ad121 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,13 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.5.21'
+ ext.compose_version = '1.0.5'
+ ext.kotlin_version = '1.5.31'
repositories {
google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
+ classpath 'com.android.tools.build:gradle:7.0.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
@@ -19,6 +20,7 @@ allprojects {
repositories {
google()
jcenter()
+ maven { url 'https://jitpack.io' }
}
}
diff --git a/draw/build.gradle b/draw/build.gradle
index 45b345f..58bce81 100644
--- a/draw/build.gradle
+++ b/draw/build.gradle
@@ -1,12 +1,10 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 28
+ compileSdkVersion 31
defaultConfig {
minSdkVersion 19
- targetSdkVersion 28
- versionCode 1
- versionName "1.0"
+ targetSdkVersion 31
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -23,17 +21,17 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//AndroidX
- implementation 'androidx.appcompat:appcompat:1.0.2'
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
- implementation 'com.google.android.material:material:1.0.0'
+ implementation 'com.google.android.material:material:1.4.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
//Test
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test:runner:1.2.0'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test:runner:1.4.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
apply plugin: 'kotlin-android'
diff --git a/draw/src/main/AndroidManifest.xml b/draw/src/main/AndroidManifest.xml
index 19e1db7..a8587d2 100644
--- a/draw/src/main/AndroidManifest.xml
+++ b/draw/src/main/AndroidManifest.xml
@@ -5,7 +5,8 @@
+ android:theme="@style/Theme.AppCompat.NoActionBar"
+ android:exported="true"/>
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..0f80bbf 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/jpc/.gitignore b/jpc/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/jpc/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/jpc/README.md b/jpc/README.md
new file mode 100644
index 0000000..10d937d
--- /dev/null
+++ b/jpc/README.md
@@ -0,0 +1,16 @@
+### Purpose:
+
+This is a sample app intended to show a way of using DrawView in Jetpack Compose
+
+### How to run:
+
+Choose the ‘jpc’ instead of ‘app’ to run the program
+
+### Notes from the contributer:
+
+* The app doesn’t load images asynchronously. Implementation of asynchronous loading is recommended
+* The app expects users to allow access to storage. Handling denial is necessary in real app
+* In the ListScreen, saved images are shown in grid. Taps on each item won’t cause anything. Implement onClick functionality if needed
+* Coloring and sizing etc hard-coded
+
+Thanks divyanshub024 for creating such a useful library.
\ No newline at end of file
diff --git a/jpc/build.gradle b/jpc/build.gradle
new file mode 100644
index 0000000..9645b64
--- /dev/null
+++ b/jpc/build.gradle
@@ -0,0 +1,70 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 31
+
+ defaultConfig {
+ applicationId "com.divyanshu.androiddraw.jpc"
+ minSdkVersion 21
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables {
+ useSupportLibrary true
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ useIR = true
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ kotlinCompilerVersion '1.5.21'
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation "androidx.compose.ui:ui:$compose_version"
+ implementation "androidx.compose.material:material:$compose_version"
+ implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0'
+ implementation 'androidx.activity:activity-compose:1.4.0'
+ implementation "androidx.navigation:navigation-compose:2.4.0-beta02"
+
+ implementation 'com.github.divyanshub024:AndroidDraw:v0.1'
+
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
+ debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
+}
\ No newline at end of file
diff --git a/jpc/proguard-rules.pro b/jpc/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/jpc/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/jpc/src/androidTest/java/com/divyanshu/androiddraw/jpc/ExampleInstrumentedTest.kt b/jpc/src/androidTest/java/com/divyanshu/androiddraw/jpc/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..ab81d62
--- /dev/null
+++ b/jpc/src/androidTest/java/com/divyanshu/androiddraw/jpc/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.divyanshu.androiddraw.jpc
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.divyanshu.androiddraw.jpc", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/jpc/src/main/AndroidManifest.xml b/jpc/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..848c0c9
--- /dev/null
+++ b/jpc/src/main/AndroidManifest.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/java/com/divyanshu/androiddraw/jpc/DrawScreen.kt b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/DrawScreen.kt
new file mode 100644
index 0000000..151c2b2
--- /dev/null
+++ b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/DrawScreen.kt
@@ -0,0 +1,386 @@
+package com.divyanshu.androiddraw.jpc
+
+import android.util.Xml
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.animateDp
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+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.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.navigation.NavController
+import org.xmlpull.v1.XmlPullParser
+import com.divyanshu.draw.widget.DrawView
+import java.util.*
+
+@ExperimentalAnimationApi
+@Composable
+fun DrawScreen(
+ navController: NavController
+) {
+ val context = LocalContext.current
+
+ // States in which a particular tool is selected
+ val showDrawTools = remember { mutableStateOf(false) } // Needed for ColorPalette, StrokeWidth, and Opacity
+ val isEraserSelected = remember { mutableStateOf(false) }
+ val isColorPaletteSelected = remember { mutableStateOf(false) }
+ val isStrokeWidthSelected = remember { mutableStateOf(false) }
+ val isOpacitySelected = remember { mutableStateOf(false) }
+ val isUndoSelected = remember { mutableStateOf(false) }
+ val isRedoSelected = remember { mutableStateOf(false) }
+
+ // Currently-selected color, stroke width, and opacity
+ val pathColor = remember { mutableStateOf(Color.Black) }
+ val strokeWidth = remember { mutableStateOf(5f) }
+ val opacity = remember { mutableStateOf(100f) }
+
+ // Used to animate the ToolBar going up and down based on the visibility of DrawTools
+ val transition = updateTransition(showDrawTools.value)
+ val elevation by transition.animateDp { isSelected ->
+ if (isSelected) 40.dp else 0.dp
+ }
+
+ // The color of the eraser. It has to be the same as the background color
+ val backgroundColor = MaterialTheme.colors.background
+
+ // For saving
+ val showSaveDialog = remember { mutableStateOf(false) }
+ val resultBitmap = remember { mutableStateOf(Utils.createEmptyBitmap(1, 1)) }
+
+ Box(
+ modifier = Modifier.fillMaxSize()
+ ) {
+ // Get the DrawView from xml
+ AndroidView(
+ modifier = Modifier.fillMaxSize(),
+ factory = {
+ val parser: XmlPullParser = it.resources.getXml(R.xml.draw_view)
+ try {
+ parser.next()
+ parser.nextTag()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ val attrs = Xml.asAttributeSet(parser)
+ DrawView(it, attrs)
+ }
+ ) { drawView ->
+ drawView.setColor(if (isEraserSelected.value) backgroundColor.toArgb() else pathColor.value.toArgb())
+ drawView.setStrokeWidth(strokeWidth.value)
+ drawView.setAlpha(if (isEraserSelected.value) 100 else opacity.value.toInt())
+
+ if (isUndoSelected.value) {
+ drawView.undo()
+ isUndoSelected.value = false
+ }
+ if (isRedoSelected.value) {
+ drawView.redo()
+ isRedoSelected.value = false
+ }
+ if (showSaveDialog.value) {
+ resultBitmap.value = drawView.getBitmap()
+ }
+ }
+ // Close button
+ IconButton(
+ modifier = Modifier
+ .align(Alignment.TopStart)
+ .size(50.dp)
+ .padding(4.dp),
+ onClick = { navController.navigateUp() }
+ ) {
+ Icon(imageVector = Icons.Default.Close, tint = Color.Gray, contentDescription = "Close")
+ }
+ // Save button
+ IconButton(
+ modifier = Modifier
+ .align(Alignment.TopEnd)
+ .size(50.dp)
+ .padding(4.dp)
+ .clip(CircleShape)
+ .background(Color.Black),
+ onClick = {
+ showSaveDialog.value = true
+ }
+ ) {
+ Icon(imageVector = Icons.Default.Check, tint = Color.White, contentDescription = "Save")
+ }
+ // Toolbar at the bottom
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .align(Alignment.BottomCenter)
+ .padding(0.dp, 0.dp, 0.dp, elevation),
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ IconButton(
+ onClick = {
+ showDrawTools.value = false
+ isEraserSelected.value = !isEraserSelected.value
+ }
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_eraser_black_24dp),
+ tint = if(isEraserSelected.value) Color.Black else Color.Gray,
+ contentDescription = "Eraser"
+ )
+ }
+ IconButton(
+ onClick = {
+ showDrawTools.value = true
+ isStrokeWidthSelected.value = true
+ isColorPaletteSelected.value = false
+ isOpacitySelected.value = false
+ }
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_adjust_black_24dp),
+ tint = Color.Gray,
+ contentDescription = "Stroke Width"
+ )
+ }
+ IconButton(
+ onClick = {
+ showDrawTools.value = true
+ isStrokeWidthSelected.value = false
+ isColorPaletteSelected.value = true
+ isOpacitySelected.value = false
+ }
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_color_lens_black_24dp),
+ tint = Color.Gray,
+ contentDescription = "Color"
+ )
+ }
+ IconButton(
+ onClick = {
+ showDrawTools.value = true
+ isStrokeWidthSelected.value = false
+ isColorPaletteSelected.value = false
+ isOpacitySelected.value = true
+ }
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_opacity_black_24dp),
+ tint = Color.Gray,
+ contentDescription = "Opacity"
+ )
+ }
+ IconButton(
+ onClick = {
+ showDrawTools.value = false
+ isUndoSelected.value = true
+ }
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_undo_black_24dp),
+ tint = Color.Gray,
+ contentDescription = "Undo"
+ )
+ }
+ IconButton(
+ onClick = {
+ showDrawTools.value = false
+ isRedoSelected.value = true
+ }
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_redo_black_24dp),
+ tint = Color.Gray,
+ contentDescription = "Redo"
+ )
+ }
+ }
+ // Box that comes up animated from the bottom. Only for ColorPalette, StrokeWidth, and Opacity
+ AnimatedVisibility(
+ modifier = Modifier.align(Alignment.BottomCenter),
+ visible = showDrawTools.value
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(50.dp)
+ .padding(15.dp, 2.dp)
+ ) {
+ when {
+ isColorPaletteSelected.value -> {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Box(
+ modifier = Modifier
+ .size(if (pathColor.value == Color.Black) 38.dp else 32.dp)
+ .clip(CircleShape)
+ .background(Color.Black)
+ .clickable { pathColor.value = Color.Black }
+ )
+ Box(
+ modifier = Modifier
+ .size(if (pathColor.value == Color.Red) 38.dp else 32.dp)
+ .clip(CircleShape)
+ .background(Color.Red)
+ .clickable { pathColor.value = Color.Red }
+ )
+ Box(
+ modifier = Modifier
+ .size(if (pathColor.value == Color.Yellow) 38.dp else 32.dp)
+ .clip(CircleShape)
+ .background(Color.Yellow)
+ .clickable { pathColor.value = Color.Yellow }
+ )
+ Box(
+ modifier = Modifier
+ .size(if (pathColor.value == Color.Green) 38.dp else 32.dp)
+ .clip(CircleShape)
+ .background(Color.Green)
+ .clickable { pathColor.value = Color.Green }
+ )
+ Box(
+ modifier = Modifier
+ .size(if (pathColor.value == Color.Blue) 38.dp else 32.dp)
+ .clip(CircleShape)
+ .background(Color.Blue)
+ .clickable { pathColor.value = Color.Blue }
+ )
+ Box(
+ modifier = Modifier
+ .size(if (pathColor.value == Color.Gray) 38.dp else 32.dp)
+ .clip(CircleShape)
+ .background(Color.Gray)
+ .clickable { pathColor.value = Color.Gray }
+ )
+ Box(
+ modifier = Modifier
+ .size(if (pathColor.value == Color.Magenta) 38.dp else 32.dp)
+ .clip(CircleShape)
+ .background(Color.Magenta)
+ .clickable { pathColor.value = Color.Magenta }
+ )
+ }
+ }
+ isStrokeWidthSelected.value -> {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Slider(
+ modifier = Modifier
+ .padding(5.dp, 0.dp)
+ .weight(1f),
+ value = strokeWidth.value,
+ onValueChange = { strokeWidth.value = it },
+ valueRange = 0f..100f,
+ steps = 100,
+ colors = SliderDefaults.colors(
+ thumbColor = MaterialTheme.colors.secondary,
+ activeTrackColor = MaterialTheme.colors.secondary
+ )
+ )
+ Box(
+ modifier = Modifier
+ .size(40.dp)
+ .padding(2.dp)
+ ) {
+ Box(
+ modifier = Modifier
+ .size(36.dp * strokeWidth.value / 100f)
+ .clip(CircleShape)
+ .background(pathColor.value)
+ )
+ }
+ }
+ }
+ isOpacitySelected.value -> {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Slider(
+ modifier = Modifier
+ .padding(5.dp, 0.dp)
+ .weight(1f),
+ value = opacity.value,
+ onValueChange = { opacity.value = it },
+ valueRange = 0f..100f,
+ steps = 100,
+ colors = SliderDefaults.colors(
+ thumbColor = MaterialTheme.colors.secondary,
+ activeTrackColor = MaterialTheme.colors.secondary
+ )
+ )
+ Box(
+ modifier = Modifier
+ .size(40.dp)
+ .padding(2.dp)
+ .clip(CircleShape)
+ .background(
+ // alpha has to be within the range: 0f..1f
+ pathColor.value.copy(alpha = opacity.value / 100f)
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (showSaveDialog.value) {
+ val fileName = remember { mutableStateOf(UUID.randomUUID().toString()) }
+ AlertDialog(
+ onDismissRequest = { showSaveDialog.value = false },
+ title = {
+ Text(
+ text = "Save Drawing",
+ fontWeight = FontWeight.Bold
+ )
+ },
+ text = {
+ OutlinedTextField(
+ value = fileName.value,
+ maxLines = 1,
+ onValueChange = { fileName.value = it }
+ )
+ },
+ dismissButton = {
+ OutlinedButton(
+ onClick = { showSaveDialog.value = false }
+ ) {
+ Text(text = "Cancel")
+ }
+ },
+ confirmButton = {
+ OutlinedButton(
+ onClick = {
+ Utils.saveImage(context, resultBitmap.value, fileName.value)
+ navController.navigateUp()
+ }
+ ) {
+ Text(text = "OK")
+ }
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ListScreen.kt b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ListScreen.kt
new file mode 100644
index 0000000..e5d1908
--- /dev/null
+++ b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ListScreen.kt
@@ -0,0 +1,151 @@
+package com.divyanshu.androiddraw.jpc
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.graphics.ImageDecoder
+import android.os.Build
+import android.provider.MediaStore
+import android.util.Log
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.*
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.GridCells
+import androidx.compose.foundation.lazy.LazyVerticalGrid
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat
+import androidx.navigation.NavController
+
+@ExperimentalFoundationApi
+@Composable
+fun ListScreen(
+ navController: NavController
+) {
+ val context = LocalContext.current
+ val scaffoldState = rememberScaffoldState()
+ val gridListState = rememberLazyListState()
+
+ val imagePathList = remember { mutableStateOf(Utils.getImagePathList(context)) }
+
+ val launcher = rememberLauncherForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ Log.d("ListScreen","PERMISSION GRANTED")
+ } else {
+ Log.d("ListScreen","PERMISSION DENIED")
+ }
+ }
+
+ LaunchedEffect(Unit) {
+ when (PackageManager.PERMISSION_GRANTED) {
+ ContextCompat.checkSelfPermission(
+ context, Manifest.permission.WRITE_EXTERNAL_STORAGE
+ ) -> {
+ Log.d("ListScreen","Has WRITE_EXTERNAL_STORAGE permission")
+ }
+ else -> {
+ // Asking for permission
+ launcher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ }
+ }
+
+ imagePathList.value = Utils.getImagePathList(context)
+ }
+
+ Scaffold(
+ floatingActionButton = {
+ FloatingActionButton(
+ onClick = {
+ navController.navigate("draw")
+ },
+ backgroundColor = MaterialTheme.colors.primary
+ ) {
+ Icon(
+ imageVector = Icons.Default.Add,
+ contentDescription = "Add Note"
+ )
+ }
+ },
+ scaffoldState = scaffoldState
+ ) {
+ Column(modifier = Modifier.fillMaxSize()) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(48.dp)
+ .background(Color.Black)
+ ) {
+ Text(
+ modifier = Modifier
+ .align(Alignment.CenterStart)
+ .padding(10.dp, 0.dp),
+ text = "Android Draw",
+ fontWeight = FontWeight.Bold,
+ color = Color.White
+ )
+ }
+ LazyVerticalGrid(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f),
+ cells = GridCells.Fixed(count = 2),
+ state = gridListState,
+ contentPadding = PaddingValues(
+ start = 6.dp,
+ top = 8.dp,
+ end = 6.dp,
+ bottom = 8.dp
+ ),
+ content = {
+ items(imagePathList.value.size) { index ->
+ val imagePath = imagePathList.value[index]
+
+ val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ val source = ImageDecoder.createSource(context.contentResolver, imagePath)
+ ImageDecoder.decodeBitmap(source)
+ }
+ else {
+ MediaStore.Images.Media.getBitmap(context.contentResolver, imagePath)
+ }
+
+ bitmap?.let {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(1.dp),
+ shape = RoundedCornerShape(15.dp),
+ border = BorderStroke(width = 1.dp, color = Color.Gray)
+ ) {
+ Image(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ // todo: do something upon click
+ },
+ bitmap = bitmap.asImageBitmap(),
+ contentDescription = "Image"
+ )
+ }
+ }
+ }
+ }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/jpc/src/main/java/com/divyanshu/androiddraw/jpc/MainActivity.kt b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/MainActivity.kt
new file mode 100644
index 0000000..530ae6b
--- /dev/null
+++ b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/MainActivity.kt
@@ -0,0 +1,43 @@
+package com.divyanshu.androiddraw.jpc
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.core.content.ContextCompat
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import com.divyanshu.androiddraw.jpc.ui.theme.AndroidDrawTheme
+
+@ExperimentalFoundationApi
+@ExperimentalAnimationApi
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ this.window.statusBarColor = ContextCompat.getColor(this,R.color.black)
+
+ AndroidDrawTheme {
+ Surface(color = MaterialTheme.colors.background) {
+ val navController = rememberNavController()
+
+ NavHost(
+ navController = navController,
+ startDestination = "list"
+ ) {
+ composable(route = "list") {
+ ListScreen(navController = navController)
+ }
+ composable(route = "draw") {
+ DrawScreen(navController = navController)
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/jpc/src/main/java/com/divyanshu/androiddraw/jpc/Utils.kt b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/Utils.kt
new file mode 100644
index 0000000..eaf615e
--- /dev/null
+++ b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/Utils.kt
@@ -0,0 +1,98 @@
+package com.divyanshu.androiddraw.jpc
+
+import android.content.ContentUris
+import android.content.ContentValues
+import android.content.Context
+import android.graphics.Bitmap
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import android.provider.MediaStore
+import android.util.Log
+import androidx.core.net.toUri
+import java.util.*
+import java.io.File
+import java.io.FileOutputStream
+
+val imageDir = "${Environment.DIRECTORY_PICTURES}/Android Draw/"
+
+object Utils {
+
+ fun createEmptyBitmap(w: Int, h: Int): Bitmap {
+ val conf = Bitmap.Config.ARGB_8888
+ return Bitmap.createBitmap(w, h, conf)
+ }
+
+ fun saveImage(context: Context, bitmap: Bitmap, fileName: String) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ val resolver = context.contentResolver
+ val contentValues = ContentValues().apply {
+ put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
+ put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
+ put(MediaStore.MediaColumns.RELATIVE_PATH, imageDir)
+ }
+
+ val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
+
+ uri?.let {
+ resolver.openOutputStream(it).use { outputStream ->
+ bitmap.compress(Bitmap.CompressFormat.PNG,100, outputStream)
+ }
+ }
+ }
+ else {
+ val path = Environment.getExternalStoragePublicDirectory(imageDir)
+ Log.e("path",path.toString())
+ val file = File(path, "$fileName.png")
+ path.mkdirs()
+ file.createNewFile()
+ val outputStream = FileOutputStream(file)
+ bitmap.compress(Bitmap.CompressFormat.PNG,100, outputStream)
+ outputStream.flush()
+ outputStream.close()
+ }
+ }
+
+ fun getImagePathList(context: Context): List {
+ val resultList = ArrayList()
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ val externalUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
+ val projection = arrayOf(MediaStore.Images.Media._ID)
+
+ context.contentResolver.query(
+ externalUri,
+ projection,
+ null,
+ null,
+ MediaStore.Images.Media.DATE_TAKEN
+ )?.use { cursor ->
+ val idColumn: Int = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
+
+ while (cursor.moveToNext()) {
+ val uri = ContentUris.withAppendedId(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ cursor.getLong(idColumn)
+ )
+ resultList.add(uri)
+ }
+ }
+ }
+ else {
+ val path = Environment.getExternalStoragePublicDirectory(imageDir)
+ path.mkdirs()
+ val imageList = path.listFiles()
+
+ if (imageList.isNullOrEmpty())
+ return emptyList()
+
+ for (imagePath in imageList) {
+ val uri = imagePath.toUri()
+ resultList.add(uri)
+ }
+ }
+
+ return resultList
+ }
+
+}
\ No newline at end of file
diff --git a/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Color.kt b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Color.kt
new file mode 100644
index 0000000..34c9aaf
--- /dev/null
+++ b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Color.kt
@@ -0,0 +1,8 @@
+package com.divyanshu.androiddraw.jpc.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
\ No newline at end of file
diff --git a/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Shape.kt b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Shape.kt
new file mode 100644
index 0000000..daa1baf
--- /dev/null
+++ b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package com.divyanshu.androiddraw.jpc.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
\ No newline at end of file
diff --git a/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Theme.kt b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Theme.kt
new file mode 100644
index 0000000..eab0507
--- /dev/null
+++ b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Theme.kt
@@ -0,0 +1,48 @@
+package com.divyanshu.androiddraw.jpc.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+
+private val DarkColorPalette = darkColors(
+ primary = Color.Black,
+ primaryVariant = Color.Black,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Color.Black,
+ primaryVariant = Color.Black,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun AndroidDrawTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable() () -> Unit
+) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Type.kt b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Type.kt
new file mode 100644
index 0000000..8b9cfcf
--- /dev/null
+++ b/jpc/src/main/java/com/divyanshu/androiddraw/jpc/ui/theme/Type.kt
@@ -0,0 +1,28 @@
+package com.divyanshu.androiddraw.jpc.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/jpc/src/main/res/drawable-v24/ic_adjust_black_24dp.xml b/jpc/src/main/res/drawable-v24/ic_adjust_black_24dp.xml
new file mode 100644
index 0000000..92e1aa9
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_adjust_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/drawable-v24/ic_close_black_24dp.xml b/jpc/src/main/res/drawable-v24/ic_close_black_24dp.xml
new file mode 100644
index 0000000..ede4b71
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_close_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/jpc/src/main/res/drawable-v24/ic_color_lens_black_24dp.xml b/jpc/src/main/res/drawable-v24/ic_color_lens_black_24dp.xml
new file mode 100644
index 0000000..497dad0
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_color_lens_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/drawable-v24/ic_done_black_24dp.xml b/jpc/src/main/res/drawable-v24/ic_done_black_24dp.xml
new file mode 100644
index 0000000..7affe9b
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_done_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/jpc/src/main/res/drawable-v24/ic_eraser_black_24dp.xml b/jpc/src/main/res/drawable-v24/ic_eraser_black_24dp.xml
new file mode 100644
index 0000000..1283eaa
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_eraser_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/drawable-v24/ic_launcher_foreground.xml b/jpc/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/drawable-v24/ic_opacity_black_24dp.xml b/jpc/src/main/res/drawable-v24/ic_opacity_black_24dp.xml
new file mode 100644
index 0000000..95da153
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_opacity_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/drawable-v24/ic_redo_black_24dp.xml b/jpc/src/main/res/drawable-v24/ic_redo_black_24dp.xml
new file mode 100644
index 0000000..2a4e353
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_redo_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/drawable-v24/ic_undo_black_24dp.xml b/jpc/src/main/res/drawable-v24/ic_undo_black_24dp.xml
new file mode 100644
index 0000000..eb6fa05
--- /dev/null
+++ b/jpc/src/main/res/drawable-v24/ic_undo_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/drawable/ic_launcher_background.xml b/jpc/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/jpc/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jpc/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/jpc/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/jpc/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/jpc/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/jpc/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/mipmap-hdpi/ic_launcher.png b/jpc/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a901e5a
Binary files /dev/null and b/jpc/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/jpc/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/jpc/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..55df7f2
Binary files /dev/null and b/jpc/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/jpc/src/main/res/mipmap-hdpi/ic_launcher_round.png b/jpc/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..a901e5a
Binary files /dev/null and b/jpc/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/jpc/src/main/res/mipmap-mdpi/ic_launcher.png b/jpc/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..7aee16d
Binary files /dev/null and b/jpc/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/jpc/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/jpc/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..5eda1bf
Binary files /dev/null and b/jpc/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/jpc/src/main/res/mipmap-mdpi/ic_launcher_round.png b/jpc/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..7aee16d
Binary files /dev/null and b/jpc/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/jpc/src/main/res/mipmap-xhdpi/ic_launcher.png b/jpc/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..d270f29
Binary files /dev/null and b/jpc/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/jpc/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/jpc/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..f752758
Binary files /dev/null and b/jpc/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/jpc/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/jpc/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d270f29
Binary files /dev/null and b/jpc/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/jpc/src/main/res/mipmap-xxhdpi/ic_launcher.png b/jpc/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..772635e
Binary files /dev/null and b/jpc/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/jpc/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/jpc/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..8912ff5
Binary files /dev/null and b/jpc/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/jpc/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/jpc/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..772635e
Binary files /dev/null and b/jpc/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..63b4fad
Binary files /dev/null and b/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..2053e92
Binary files /dev/null and b/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..63b4fad
Binary files /dev/null and b/jpc/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/jpc/src/main/res/values-night/themes.xml b/jpc/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..b6431a8
--- /dev/null
+++ b/jpc/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/values/colors.xml b/jpc/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/jpc/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/jpc/src/main/res/values/ic_launcher_background.xml b/jpc/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..beab31f
--- /dev/null
+++ b/jpc/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #000000
+
\ No newline at end of file
diff --git a/jpc/src/main/res/values/strings.xml b/jpc/src/main/res/values/strings.xml
new file mode 100644
index 0000000..51519de
--- /dev/null
+++ b/jpc/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ jpc
+
\ No newline at end of file
diff --git a/jpc/src/main/res/values/themes.xml b/jpc/src/main/res/values/themes.xml
new file mode 100644
index 0000000..e9aeb83
--- /dev/null
+++ b/jpc/src/main/res/values/themes.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jpc/src/main/res/xml/draw_view.xml b/jpc/src/main/res/xml/draw_view.xml
new file mode 100644
index 0000000..580db6e
--- /dev/null
+++ b/jpc/src/main/res/xml/draw_view.xml
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/jpc/src/test/java/com/divyanshu/androiddraw/jpc/ExampleUnitTest.kt b/jpc/src/test/java/com/divyanshu/androiddraw/jpc/ExampleUnitTest.kt
new file mode 100644
index 0000000..7856bb6
--- /dev/null
+++ b/jpc/src/test/java/com/divyanshu/androiddraw/jpc/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.divyanshu.androiddraw.jpc
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index f02c602..5daaf14 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,2 @@
include ':app', ':draw'
+include ':jpc'