Skip to content

Commit 9ac41e4

Browse files
author
Vinod Baste
committed
intial push
1 parent b2e6870 commit 9ac41e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1322
-0
lines changed

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea/caches
5+
/.idea/libraries
6+
/.idea/modules.xml
7+
/.idea/workspace.xml
8+
/.idea/navEditor.xml
9+
/.idea/assetWizardSettings.xml
10+
.DS_Store
11+
/build
12+
/captures
13+
.externalNativeBuild
14+
.cxx
15+
local.properties

.idea/.gitignore

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/compiler.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/gradle.xml

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ImageCompressor/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

ImageCompressor/build.gradle

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
plugins {
2+
id 'com.android.library'
3+
id 'org.jetbrains.kotlin.android'
4+
}
5+
6+
android {
7+
compileSdk 31
8+
9+
defaultConfig {
10+
minSdk 21
11+
targetSdk 31
12+
13+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14+
consumerProguardFiles "consumer-rules.pro"
15+
}
16+
17+
buildTypes {
18+
release {
19+
minifyEnabled false
20+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21+
}
22+
}
23+
compileOptions {
24+
sourceCompatibility JavaVersion.VERSION_1_8
25+
targetCompatibility JavaVersion.VERSION_1_8
26+
}
27+
kotlinOptions {
28+
jvmTarget = '1.8'
29+
}
30+
}
31+
32+
dependencies {
33+
34+
implementation 'androidx.core:core-ktx:1.7.0'
35+
implementation 'androidx.appcompat:appcompat:1.4.2'
36+
implementation 'com.google.android.material:material:1.6.1'
37+
testImplementation 'junit:junit:4.13.2'
38+
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
39+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
40+
}

ImageCompressor/consumer-rules.pro

Whitespace-only changes.

ImageCompressor/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.android.imagecompressor
2+
3+
import androidx.test.platform.app.InstrumentationRegistry
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("com.android.imagecompressor.test", appContext.packageName)
23+
}
24+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.android.imagecompressor">
4+
5+
</manifest>
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
@file:Suppress("DEPRECATION")
2+
3+
package com.android.imagecompressor
4+
5+
import android.content.Context
6+
import android.graphics.*
7+
import android.location.Location
8+
import android.media.ExifInterface
9+
import android.net.Uri
10+
import android.os.Build
11+
import android.os.Environment
12+
import android.provider.MediaStore
13+
import android.util.Log
14+
import java.io.*
15+
import java.nio.file.Files
16+
import kotlin.math.roundToInt
17+
18+
object ImageCompressUtils {
19+
20+
//compress the image
21+
@JvmOverloads
22+
fun compressImage(
23+
context: Context,
24+
imagePath: String?,
25+
imageName: String?,
26+
imageQuality: Int = 50
27+
): String {
28+
29+
var filePath = ""
30+
try {
31+
var scaledBitmap: Bitmap? = null
32+
val options = BitmapFactory.Options()
33+
// by setting this field as true, the actual bitmap pixels are not loaded in the memory.
34+
// Just the bounds are loaded. If you try the use the bitmap here, you will get null.
35+
options.inJustDecodeBounds = true
36+
var actualHeight = options.outHeight
37+
var actualWidth = options.outWidth
38+
39+
val imageFile = File(imagePath!!)
40+
val fileContent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
41+
Files.readAllBytes(imageFile.toPath())
42+
} else {
43+
File(imagePath).readBytes()
44+
}
45+
var bmp = BitmapFactory.decodeByteArray(fileContent, 0, fileContent.size, options)
46+
47+
//max Height and width values of the compressed image is taken as 1024x912
48+
val maxHeight = 1024.0f
49+
val maxWidth = 912.0f
50+
var imgRatio = actualWidth / actualHeight.toFloat()
51+
val maxRatio = maxWidth / maxHeight
52+
53+
//width and height values are set maintaining the aspect ratio of the image
54+
if (actualHeight > maxHeight || actualWidth > maxWidth) {
55+
when {
56+
imgRatio < maxRatio -> {
57+
imgRatio = maxHeight / actualHeight
58+
actualWidth = (imgRatio * actualWidth).toInt()
59+
actualHeight = maxHeight.toInt()
60+
}
61+
imgRatio > maxRatio -> {
62+
imgRatio = maxWidth / actualWidth
63+
actualHeight = (imgRatio * actualHeight).toInt()
64+
actualWidth = maxWidth.toInt()
65+
}
66+
else -> {
67+
actualHeight = maxHeight.toInt()
68+
actualWidth = maxWidth.toInt()
69+
}
70+
}
71+
}
72+
73+
//setting inSampleSize value allows to load a scaled down version of the original image
74+
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight)
75+
//inJustDecodeBounds set to false to load the actual bitmap
76+
options.inJustDecodeBounds = false
77+
//this removes the redundant quality of the image
78+
options.inPurgeable = true
79+
//this options allow android to claim the bitmap memory if it runs low on memory
80+
options.inInputShareable = true
81+
82+
options.inTempStorage = ByteArray(16 * 1024)
83+
try {
84+
//load the bitmap from its path
85+
bmp = BitmapFactory.decodeFile(imagePath, options)
86+
} catch (exception: OutOfMemoryError) {
87+
exception.printStackTrace()
88+
}
89+
try {
90+
scaledBitmap =
91+
Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888)
92+
} catch (exception: OutOfMemoryError) {
93+
exception.printStackTrace()
94+
}
95+
val ratioX = actualWidth / options.outWidth.toFloat()
96+
val ratioY = actualHeight / options.outHeight.toFloat()
97+
val middleX = actualWidth / 2.0f
98+
val middleY = actualHeight / 2.0f
99+
val scaleMatrix = Matrix()
100+
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY)
101+
val canvas = Canvas(scaledBitmap!!)
102+
canvas.setMatrix(scaleMatrix)
103+
canvas.drawBitmap(
104+
bmp,
105+
middleX - bmp.width / 2,
106+
middleY - bmp.height / 2,
107+
Paint(Paint.FILTER_BITMAP_FLAG)
108+
)
109+
110+
//check the rotation of the image and display it properly
111+
val exif: ExifInterface
112+
try {
113+
exif = ExifInterface(imagePath.toString())
114+
val orientation = exif.getAttributeInt(
115+
ExifInterface.TAG_ORIENTATION, 0
116+
)
117+
Log.d("EXIF", "Exif: $orientation")
118+
val matrix = Matrix()
119+
when (orientation) {
120+
6 -> {
121+
matrix.postRotate(90f)
122+
Log.d("EXIF", "Exif: $orientation")
123+
}
124+
3 -> {
125+
matrix.postRotate(180f)
126+
Log.d("EXIF", "Exif: $orientation")
127+
}
128+
8 -> {
129+
matrix.postRotate(270f)
130+
Log.d("EXIF", "Exif: $orientation")
131+
}
132+
}
133+
scaledBitmap = Bitmap.createBitmap(
134+
scaledBitmap, 0, 0,
135+
scaledBitmap.width, scaledBitmap.height, matrix,
136+
true
137+
)
138+
} catch (e: IOException) {
139+
e.printStackTrace()
140+
} catch (e: java.lang.Exception) {
141+
e.printStackTrace()
142+
}
143+
val out: FileOutputStream?
144+
filePath = imageName?.let { getOutputMediaFile(it, context) }!!.absolutePath
145+
try {
146+
out = FileOutputStream(filePath)
147+
148+
//write the compressed bitmap at the destination specified by filename.
149+
scaledBitmap!!.compress(Bitmap.CompressFormat.JPEG, imageQuality, out)
150+
} catch (e: FileNotFoundException) {
151+
e.printStackTrace()
152+
}
153+
} catch (e: java.lang.Exception) {
154+
e.printStackTrace()
155+
}
156+
157+
return filePath
158+
}
159+
160+
}
161+
162+
private fun getOutputMediaFile(imageName: String, context: Context): File? {
163+
var imageFile1: File? = null
164+
try {
165+
imageFile1 = createImageFile(context, imageName)
166+
} catch (e: IOException) {
167+
e.printStackTrace()
168+
}
169+
if (imageFile1!!.exists()) imageFile1.delete()
170+
var imageNew: File? = null
171+
try {
172+
imageNew = createImageFile(context, imageName)
173+
} catch (e: IOException) {
174+
e.printStackTrace()
175+
}
176+
return imageNew
177+
}
178+
179+
private fun calculateInSampleSize(
180+
options: BitmapFactory.Options,
181+
reqWidth: Int,
182+
reqHeight: Int
183+
): Int {
184+
val height = options.outHeight
185+
val width = options.outWidth
186+
var inSampleSize = 1
187+
try {
188+
if (height > reqHeight || width > reqWidth) {
189+
val heightRatio =
190+
(height.toFloat() / reqHeight.toFloat()).roundToInt()
191+
val widthRatio =
192+
(width.toFloat() / reqWidth.toFloat()).roundToInt()
193+
inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
194+
}
195+
val totalPixels = width * height.toFloat()
196+
val totalReqPixelsCap = reqWidth * reqHeight * 2.toFloat()
197+
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
198+
inSampleSize++
199+
}
200+
} catch (e: java.lang.Exception) {
201+
e.printStackTrace()
202+
}
203+
return inSampleSize
204+
}
205+
206+
@Throws(IOException::class)
207+
private fun createImageFile(context: Context, FileName: String): File {
208+
return File(
209+
context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
210+
.toString() + File.separator + FileName + ".png"
211+
)
212+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.android.imagecompressor
2+
3+
import org.junit.Test
4+
5+
import org.junit.Assert.*
6+
7+
/**
8+
* Example local unit test, which will execute on the development machine (host).
9+
*
10+
* See [testing documentation](http://d.android.com/tools/testing).
11+
*/
12+
class ExampleUnitTest {
13+
@Test
14+
fun addition_isCorrect() {
15+
assertEquals(4, 2 + 2)
16+
}
17+
}

app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

0 commit comments

Comments
 (0)