This post is about how to integrate DS Photo Editor SDK into your android apps with the help of a simple ImageEditor App.
Creating new project
1 . Create a new project by going to File ⇒ New Android Project, select Empty Activity, provide app name, select language to kotlin and then finally click on finish.
2 . Download the SDK from here. Now go to Android-SDK-Files\Android SDK Files\SDK and then Copy downloaded “ds-photo-editor-sdk-v10.aar” into your project’s /libs directory. Create this directory if it doesn’t already exist in your project.
3. Open the project-level build.gradle, and update the repositories section as below.
build.gradle (project-level)
allprojects {
repositories {
jcenter()
google()
flatDir {
dirs 'libs'}
}
}
4. Open the app-level build.gradle file, and update defaultConfig and dependencies as below.
build.gradle (app-level)
android {
compileSdk 31
defaultConfig {
applicationId "com.c1ctech.imageeditorapp"
minSdk 23
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
renderscriptTargetApi 21
renderscriptSupportModeEnabled 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'
}
//to enable viewBinding
//not a part of ds photo editor integration
viewBinding {
enabled = true
}
}
dependencies {
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'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// DS Photo Editor SDK
implementation(name:'ds-photo-editor-sdk-v10', ext:'aar')
// SDK related dependencies
implementation "io.reactivex.rxjava3:rxjava:3.1.4"
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'com.github.bumptech.glide:glide:4.13.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.0'
}
5. Open the AndroidManifest.xml file and add the below changes as shown below:
- Photo editing apps need more memories than normal apps, so make sure to set android:largeHeap to be true in your application declaration.
- Update the manifest file to include the SDK activities and required permissions.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.c1ctech.imageeditorapp">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ImageEditorApp">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.dsphotoeditor.sdk.activity.DsPhotoEditorActivity"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Holo.NoActionBar" />
<activity
android:name="com.dsphotoeditor.sdk.activity.DsPhotoEditorStickerActivity"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Holo.NoActionBar" />
<activity
android:name="com.dsphotoeditor.sdk.activity.DsPhotoEditorTextActivity"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Holo.NoActionBar"
android:windowSoftInputMode="adjustPan" />
<activity
android:name="com.dsphotoeditor.sdk.activity.DsPhotoEditorCropActivity"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Holo.NoActionBar" />
<activity
android:name="com.dsphotoeditor.sdk.activity.DsPhotoEditorDrawActivity"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Holo.NoActionBar" />
</application>
</manifest>
Note: In order to save edited photos on android devices, you will need to request storage permissions at run time for Android 6.0+.
Creating Layout File
6. The below layout file defines the layout of MainActivity which consists of a button and an imageView.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">
<Button
android:id="@+id/btnPickImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Pick Image"
android:textAllCaps="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:adjustViewBounds="true" />
</LinearLayout>
Complete MainActivity Code
7. The MainActivity file contains the below code:
MainActivity.kt
package com.c1ctech.imageeditorapp
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.c1ctech.imageeditorapp.databinding.ActivityMainBinding
import com.dsphotoeditor.sdk.activity.DsPhotoEditorActivity
import com.dsphotoeditor.sdk.utils.DsPhotoEditorConstants
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
var imageActivityResultLauncher: ActivityResultLauncher<Intent>? = null
var saveImageActivityResultLauncher: ActivityResultLauncher<Intent>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.btnPickImage.setOnClickListener {
checkPermission()
}
imageActivityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
ActivityResultCallback<ActivityResult>() {
if (it.resultCode == Activity.RESULT_OK) {
val uri: Uri? = it.data?.data
var dsPhotoEditorIntent = Intent(this, DsPhotoEditorActivity::class.java)
dsPhotoEditorIntent.data = uri
// An optional parameter, to specify the directory to save the output image on device's external storage.
// If the output directory is omitted, the edited photo will be saved into a folder called "DS_Photo_Editor" by default.
dsPhotoEditorIntent.putExtra(
DsPhotoEditorConstants.DS_PHOTO_EDITOR_OUTPUT_DIRECTORY,
"Images"
)
val toolsToHide = intArrayOf(
DsPhotoEditorActivity.TOOL_WARMTH,
DsPhotoEditorActivity.TOOL_SATURATION,
DsPhotoEditorActivity.TOOL_VIGNETTE,
DsPhotoEditorActivity.TOOL_EXPOSURE
)
// if you don't want some of the tools to show up.
// Just simply pass in the tools to hide in the UI.
dsPhotoEditorIntent.putExtra(
DsPhotoEditorConstants.DS_PHOTO_EDITOR_TOOLS_TO_HIDE,
toolsToHide
)
saveImageActivityResultLauncher?.launch(dsPhotoEditorIntent)
}
})
saveImageActivityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
ActivityResultCallback<ActivityResult>() {
if (it.resultCode == Activity.RESULT_OK) {
//handle the result uri ,by displaying it in an imageView
val uri: Uri? = it.data?.data
activityMainBinding.imageView.setImageURI(uri)
Toast.makeText(this, "Photo Saved", Toast.LENGTH_SHORT).show()
}
})
}
private fun checkPermission() {
var permission = ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
)
//for android version Q and above, we are directly calling the pickImage() method
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
pickImage()
} else {
//requesting permission, if permission is not granted
if (permission != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
100
)
else
pickImage()
}
}
private fun pickImage() {
var intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*"
setResult(100, intent)
imageActivityResultLauncher?.launch(intent)
}
//Callback for the result from requesting permissions.
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 100 && grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
pickImage()
else
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show()
}
}
When you run the app it will look like this: