Repository: zetbaitsu/Compressor Branch: master Commit: a920146ba104 Files: 48 Total size: 78.8 KB Directory structure: gitextract_ompy11c5/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── README.md ├── README_v2.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── id/ │ │ └── zelory/ │ │ └── compressor/ │ │ └── sample/ │ │ ├── FileUtil.java │ │ └── MainActivity.kt │ └── res/ │ ├── layout/ │ │ └── activity_main.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── values-w820dp/ │ └── dimens.xml ├── build.gradle ├── compressor/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── main/ │ │ ├── AndroidManifest.xml │ │ └── java/ │ │ └── id/ │ │ └── zelory/ │ │ └── compressor/ │ │ ├── Compressor.kt │ │ ├── Util.kt │ │ └── constraint/ │ │ ├── Compression.kt │ │ ├── Constraint.kt │ │ ├── DefaultConstraint.kt │ │ ├── DestinationConstraint.kt │ │ ├── FormatConstraint.kt │ │ ├── QualityConstraint.kt │ │ ├── ResolutionConstraint.kt │ │ └── SizeConstraint.kt │ └── test/ │ └── java/ │ └── id/ │ └── zelory/ │ └── compressor/ │ ├── CompressorTest.kt │ ├── UtilTest.kt │ └── constraint/ │ ├── CompressionTest.kt │ ├── DefaultConstraintTest.kt │ ├── DestinationConstraintTest.kt │ ├── FormatConstraintTest.kt │ ├── QualityConstraintTest.kt │ ├── ResolutionConstraintTest.kt │ └── SizeConstraintTest.kt ├── gradle/ │ ├── publish_maven.gradle │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms ko_fi: zetbaitsu ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea .DS_Store /build /captures ================================================ FILE: .travis.yml ================================================ language: android dist: trusty android: components: # Uncomment the lines below if you want to # use the latest revision of Android SDK Tools # - tools # - platform-tools # The BuildTools version used by your project - build-tools-29.0.2 # The SDK version used to compile your project - android-29 # Additional components - extra-google-google_play_services - extra-google-m2repository - extra-android-m2repository # Specify at least one system image, # if you need to run emulator(s) during your tests - sys-img-x86-android-21 before_script: - touch local.properties script: - ./gradlew clean build jacocoTestReleaseUnitTestReport after_success: - bash <(curl -s https://codecov.io/bash) ================================================ FILE: README.md ================================================ Compressor ====== [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Compressor-blue.svg?style=flat)](http://android-arsenal.com/details/1/3758) [![Build Status](https://travis-ci.org/zetbaitsu/Compressor.svg?branch=master)](https://travis-ci.org/zetbaitsu/Compressor) [![codecov](https://codecov.io/gh/zetbaitsu/Compressor/branch/master/graph/badge.svg)](https://codecov.io/gh/zetbaitsu/Compressor)

Compressor is a lightweight and powerful android image compression library. Compressor will allow you to compress large photos into smaller sized photos with very less or negligible loss in quality of the image. # Gradle ```groovy dependencies { implementation 'id.zelory:compressor:3.0.1' } ``` # Let's compress the image size! #### Compress Image File ```kotlin val compressedImageFile = Compressor.compress(context, actualImageFile) ``` #### Compress Image File to specific destination ```kotlin val compressedImageFile = Compressor.compress(context, actualImageFile) { default() destination(myFile) } ``` ### I want custom Compressor! #### Using default constraint and custom partial of it ```kotlin val compressedImageFile = Compressor.compress(context, actualImageFile) { default(width = 640, format = Bitmap.CompressFormat.WEBP) } ``` #### Full custom constraint ```kotlin val compressedImageFile = Compressor.compress(context, actualImageFile) { resolution(1280, 720) quality(80) format(Bitmap.CompressFormat.WEBP) size(2_097_152) // 2 MB } ``` #### Using your own custom constraint ```kotlin class MyLowerCaseNameConstraint: Constraint { override fun isSatisfied(imageFile: File): Boolean { return imageFile.name.all { it.isLowerCase() } } override fun satisfy(imageFile: File): File { val destination = File(imageFile.parent, imageFile.name.toLowerCase()) imageFile.renameTo(destination) return destination } } val compressedImageFile = Compressor.compress(context, actualImageFile) { constraint(MyLowerCaseNameConstraint()) // your own constraint quality(80) // combine with compressor constraint format(Bitmap.CompressFormat.WEBP) } ``` #### You can create your own extension too ```kotlin fun Compression.lowerCaseName() { constraint(MyLowerCaseNameConstraint()) } val compressedImageFile = Compressor.compress(context, actualImageFile) { lowerCaseName() // your own extension quality(80) // combine with compressor constraint format(Bitmap.CompressFormat.WEBP) } ``` ### Compressor now is using Kotlin coroutines! #### Calling Compressor should be done from coroutines scope ```kotlin // e.g calling from activity lifecycle scope lifecycleScope.launch { val compressedImageFile = Compressor.compress(context, actualImageFile) } // calling from global scope GlobalScope.launch { val compressedImageFile = Compressor.compress(context, actualImageFile) } ``` #### Run Compressor in main thread ```kotlin val compressedImageFile = Compressor.compress(context, actualImageFile, Dispatchers.Main) ``` ### Old version Please read this [readme](https://github.com/zetbaitsu/Compressor/blob/master/README_v2.md) License ------- Copyright (c) 2016 Zetra. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README_v2.md ================================================ Compressor ====== [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Compressor-blue.svg?style=flat)](http://android-arsenal.com/details/1/3758)

Compressor is a lightweight and powerful android image compression library. Compressor will allow you to compress large photos into smaller sized photos with very less or negligible loss in quality of the image. # Gradle ```groovy dependencies { implementation 'id.zelory:compressor:2.1.1' } ``` # Let's compress the image size! #### Compress Image File ```java compressedImageFile = new Compressor(this).compressToFile(actualImageFile); ``` #### Compress Image File to Bitmap ```java compressedImageBitmap = new Compressor(this).compressToBitmap(actualImageFile); ``` ### I want custom Compressor! ```java compressedImage = new Compressor(this) .setMaxWidth(640) .setMaxHeight(480) .setQuality(75) .setCompressFormat(Bitmap.CompressFormat.WEBP) .setDestinationDirectoryPath(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES).getAbsolutePath()) .compressToFile(actualImage); ``` ### Stay cool compress image asynchronously with RxJava! ```java new Compressor(this) .compressToFileAsFlowable(actualImage) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer() { @Override public void accept(File file) { compressedImage = file; } }, new Consumer() { @Override public void accept(Throwable throwable) { throwable.printStackTrace(); showError(throwable.getMessage()); } }); ``` License ------- Copyright (c) 2016 Zetra. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: app/.gitignore ================================================ *.iml .gradle /local.properties /.idea .DS_Store /build /captures ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 29 buildToolsVersion '29.0.2' defaultConfig { applicationId "id.zelory.compressor.sample" minSdkVersion 14 targetSdkVersion 29 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' //implementation project(':compressor') implementation 'id.zelory:compressor:3.0.1' testImplementation 'junit:junit:4.12' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/macbookair/Library/Android/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # 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 *; #} ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/id/zelory/compressor/sample/FileUtil.java ================================================ package id.zelory.compressor.sample; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.OpenableColumns; import android.util.Log; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Created on : June 18, 2016 * Author : zetbaitsu * Name : Zetra * GitHub : https://github.com/zetbaitsu */ class FileUtil { private static final int EOF = -1; private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; private FileUtil() { } public static File from(Context context, Uri uri) throws IOException { InputStream inputStream = context.getContentResolver().openInputStream(uri); String fileName = getFileName(context, uri); String[] splitName = splitFileName(fileName); File tempFile = File.createTempFile(splitName[0], splitName[1]); tempFile = rename(tempFile, fileName); tempFile.deleteOnExit(); FileOutputStream out = null; try { out = new FileOutputStream(tempFile); } catch (FileNotFoundException e) { e.printStackTrace(); } if (inputStream != null) { copy(inputStream, out); inputStream.close(); } if (out != null) { out.close(); } return tempFile; } private static String[] splitFileName(String fileName) { String name = fileName; String extension = ""; int i = fileName.lastIndexOf("."); if (i != -1) { name = fileName.substring(0, i); extension = fileName.substring(i); } return new String[]{name, extension}; } private static String getFileName(Context context, Uri uri) { String result = null; if (uri.getScheme().equals("content")) { Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); try { if (cursor != null && cursor.moveToFirst()) { result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); } } catch (Exception e) { e.printStackTrace(); } finally { if (cursor != null) { cursor.close(); } } } if (result == null) { result = uri.getPath(); int cut = result.lastIndexOf(File.separator); if (cut != -1) { result = result.substring(cut + 1); } } return result; } private static File rename(File file, String newName) { File newFile = new File(file.getParent(), newName); if (!newFile.equals(file)) { if (newFile.exists() && newFile.delete()) { Log.d("FileUtil", "Delete old " + newName + " file"); } if (file.renameTo(newFile)) { Log.d("FileUtil", "Rename file to " + newName); } } return newFile; } private static long copy(InputStream input, OutputStream output) throws IOException { long count = 0; int n; byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; while (EOF != (n = input.read(buffer))) { output.write(buffer, 0, n); count += n; } return count; } } ================================================ FILE: app/src/main/java/id/zelory/compressor/sample/MainActivity.kt ================================================ package id.zelory.compressor.sample import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Color import android.os.Bundle import android.os.Environment import android.util.Log import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import id.zelory.compressor.Compressor import id.zelory.compressor.constraint.default import id.zelory.compressor.constraint.destination import id.zelory.compressor.constraint.format import id.zelory.compressor.constraint.quality import id.zelory.compressor.constraint.resolution import id.zelory.compressor.constraint.size import id.zelory.compressor.loadBitmap import kotlinx.android.synthetic.main.activity_main.actualImageView import kotlinx.android.synthetic.main.activity_main.actualSizeTextView import kotlinx.android.synthetic.main.activity_main.chooseImageButton import kotlinx.android.synthetic.main.activity_main.compressImageButton import kotlinx.android.synthetic.main.activity_main.compressedImageView import kotlinx.android.synthetic.main.activity_main.compressedSizeTextView import kotlinx.android.synthetic.main.activity_main.customCompressImageButton import kotlinx.coroutines.launch import java.io.File import java.io.IOException import java.text.DecimalFormat import java.util.* import kotlin.math.log10 import kotlin.math.pow /** * Created on : January 25, 2020 * Author : zetbaitsu * Name : Zetra * GitHub : https://github.com/zetbaitsu */ class MainActivity : AppCompatActivity() { companion object { private const val PICK_IMAGE_REQUEST = 1 } private var actualImage: File? = null private var compressedImage: File? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) actualImageView.setBackgroundColor(getRandomColor()) clearImage() setupClickListener() } private fun setupClickListener() { chooseImageButton.setOnClickListener { chooseImage() } compressImageButton.setOnClickListener { compressImage() } customCompressImageButton.setOnClickListener { customCompressImage() } } private fun chooseImage() { val intent = Intent(Intent.ACTION_GET_CONTENT) intent.type = "image/*" startActivityForResult(intent, PICK_IMAGE_REQUEST) } private fun compressImage() { actualImage?.let { imageFile -> lifecycleScope.launch { // Default compression compressedImage = Compressor.compress(this@MainActivity, imageFile) setCompressedImage() } } ?: showError("Please choose an image!") } private fun customCompressImage() { actualImage?.let { imageFile -> lifecycleScope.launch { // Default compression with custom destination file /*compressedImage = Compressor.compress(this@MainActivity, imageFile) { default() getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.also { val file = File("${it.absolutePath}${File.separator}my_image.${imageFile.extension}") destination(file) } }*/ // Full custom compressedImage = Compressor.compress(this@MainActivity, imageFile) { resolution(1280, 720) quality(80) format(Bitmap.CompressFormat.WEBP) size(2_097_152) // 2 MB } setCompressedImage() } } ?: showError("Please choose an image!") } private fun setCompressedImage() { compressedImage?.let { compressedImageView.setImageBitmap(BitmapFactory.decodeFile(it.absolutePath)) compressedSizeTextView.text = String.format("Size : %s", getReadableFileSize(it.length())) Toast.makeText(this, "Compressed image save in " + it.path, Toast.LENGTH_LONG).show() Log.d("Compressor", "Compressed image save in " + it.path) } } private fun clearImage() { actualImageView.setBackgroundColor(getRandomColor()) compressedImageView.setImageDrawable(null) compressedImageView.setBackgroundColor(getRandomColor()) compressedSizeTextView.text = "Size : -" } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK) { if (data == null) { showError("Failed to open picture!") return } try { actualImage = FileUtil.from(this, data.data)?.also { actualImageView.setImageBitmap(loadBitmap(it)) actualSizeTextView.text = String.format("Size : %s", getReadableFileSize(it.length())) clearImage() } } catch (e: IOException) { showError("Failed to read picture data!") e.printStackTrace() } } } private fun showError(errorMessage: String) { Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show() } private fun getRandomColor() = Random().run { Color.argb(100, nextInt(256), nextInt(256), nextInt(256)) } private fun getReadableFileSize(size: Long): String { if (size <= 0) { return "0" } val units = arrayOf("B", "KB", "MB", "GB", "TB") val digitGroups = (log10(size.toDouble()) / log10(1024.0)).toInt() return DecimalFormat("#,##0.#").format(size / 1024.0.pow(digitGroups.toDouble())) + " " + units[digitGroups] } } ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================