I will write on Kotlin Upload Image From Camera To Server Android Example.
You will learn how to capture image from camera and then send it to the remote server (PHP-MySQL) using Kotlin in android app.
We will not use any external library to make http calls rather we will be dependent on the Multipart class.
Here, we will capture the image first and then will upload it to server, if you want to upload the image from your gallery read kotlin upload image gallery android example.
First of all, see the below images as an output of this example.
Writing PHP
To interact with server from android app, we need to write two php scripts.
First of all, make a php file named “config.php” and write the following code in it
<?php $host="localhost"; $user="your username"; $password="your password"; $db = "your db name"; $con = mysqli_connect($host,$user,$password,$db); // Check connection if (mysqli_connect_errno()) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); }else{ //echo "Connect"; } ?>
Above file gas some common configuration lines.
Now make another PHP file like uploadfile.php and it should have below lines
<?php if($_SERVER['REQUEST_METHOD']=='POST'){ // echo $_SERVER["DOCUMENT_ROOT"]; // /home1/demonuts/public_html //including the database connection file include_once("config.php"); //$_FILES['image']['name'] give original name from parameter where 'image' == parametername eg. city.jpg //$_FILES['image']['tmp_name'] temporary system generated name $originalImgName= $_FILES['filename']['name']; $tempName= $_FILES['filename']['tmp_name']; $folder="uploadedFiles/"; $url = "https://www.demonuts.com/Demonuts/JsonTest/Tennis/uploadedFiles/".$originalImgName; //update path as per your directory structure if(move_uploaded_file($tempName,$folder.$originalImgName)){ $query = "INSERT INTO upload_image_video (pathToFile) VALUES ('$url')"; if(mysqli_query($con,$query)){ $query= "SELECT * FROM upload_image_video WHERE pathToFile='$url'"; $result= mysqli_query($con, $query); $emparray = array(); if(mysqli_num_rows($result) > 0){ while ($row = mysqli_fetch_assoc($result)) { $emparray[] = $row; } echo json_encode(array( "status" => "true","message" => "Successfully file added!" , "data" => $emparray) ); }else{ echo json_encode(array( "status" => "false","message" => "Failed!") ); } }else{ echo json_encode(array( "status" => "false","message" => "Failed!") ); } //echo "moved to ".$url; }else{ echo json_encode(array( "status" => "false","message" => "Failed!") ); } } ?>
Step 1. New Project With Kotlin
Go to your android studio and create a new project.
When you are doing this, make sure that you select empty activity as the default one and select Kotlin as the primary language.
Step 2. Gradle Changes
Open your build.gradle(Project: your project name) file and add the below line
maven { url 'https://jitpack.io' }
in the following structure
allprojects { repositories { google() jcenter() }
So the final code block for build.gradle(Project: your project name) file is as the below
buildscript { ext.kotlin_version = '1.3.31' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.4.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() maven { url 'https://jitpack.io' } } } task clean(type: Delete) { delete rootProject.buildDir }
That’s it for build.gradle(Project: your project name) file.
Now open your another gradle file which is build.gradle(Module: app) file.
In this file, write the below lines
android{ useLibrary 'org.apache.http.legacy' } packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' }
in the root or parent level android { …. } tag.
Now, add the following lines
implementation 'com.karumi:dexter:5.0.0' implementation group: 'org.apache.httpcomponents' , name: 'httpclient-android' , version: '4.3.5.1' implementation('org.apache.httpcomponents:httpmime:4.3') { exclude module: "httpclient" }
inside dependencies { … }
Here, first line is the dexter library which will help us to implement runtime permissions.
All other lines will help us to make http calls with Multipart.
So final source block for build.gradle(Module: app) file is as the following
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 28 defaultConfig { applicationId "com.example.uploadcamerakotlin" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } android{ useLibrary 'org.apache.http.legacy' } packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'com.karumi:dexter:5.0.0' implementation group: 'org.apache.httpcomponents' , name: 'httpclient-android' , version: '4.3.5.1' implementation('org.apache.httpcomponents:httpmime:4.3') { exclude module: "httpclient" } }
Step 3. Manifest
Now open your AndroidManifest.xml file and add the following lines in it.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" />
These lines are the permissions for read external storage, write external storage and for internet.
For first two permissions, we need to ask for runtime permissions because they are dangerous for user privacy.We will do this in the MainActivity.kt class later.
Step 4. Multipart Class
Now create a new class and give it a name like MultipartRequester.kt
Write down the below source code in MultipartRequester.kt file.
import android.app.Activity import android.app.ActivityManager import android.content.Context import android.os.AsyncTask import android.util.Log import android.widget.Toast import org.apache.http.client.HttpClient import org.apache.http.client.methods.HttpPost import org.apache.http.entity.ContentType import org.apache.http.entity.mime.MIME import org.apache.http.entity.mime.MultipartEntityBuilder import org.apache.http.impl.client.DefaultHttpClient import org.apache.http.params.HttpConnectionParams import org.apache.http.util.EntityUtils import java.io.File class MultiPartRequester( private val activity: Activity, private val map: MutableMap<String, String>, private val serviceCode: Int, asyncTaskCompleteListener: AsyncTaskCompleteListener ) { private var mAsynclistener: AsyncTaskCompleteListener? = null private var httpclient: HttpClient? = null private var request: AsyncHttpRequest? = null init { // is Internet Connection Available... mAsynclistener = asyncTaskCompleteListener as AsyncTaskCompleteListener request = AsyncHttpRequest().execute(map["url"]) as AsyncHttpRequest } internal inner class AsyncHttpRequest : AsyncTask<String, Void, String>() { override fun doInBackground(vararg urls: String): String? { map.remove("url") try { val httppost = HttpPost(urls[0]) httpclient = DefaultHttpClient() HttpConnectionParams.setConnectionTimeout( httpclient!!.getParams(), 600000 ) val builder = MultipartEntityBuilder .create() for (key in map.keys) { if (key.equals("filename", ignoreCase = true)) { val f = File(map[key]) builder.addBinaryBody( key, f, ContentType.MULTIPART_FORM_DATA, f.getName() ) } else { builder.addTextBody( key, map[key], ContentType .create("text/plain", MIME.DEFAULT_CHARSET) ) } Log.d("TAG", key + "---->" + map[key]) // System.out.println(key + "---->" + map.get(key)); } httppost.setEntity(builder.build()) val manager = activity .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager if (manager.memoryClass < 25) { System.gc() } val response = httpclient!!.execute(httppost) return EntityUtils.toString( response.getEntity(), "UTF-8" ) } catch (e: Exception) { e.printStackTrace() } catch (oume: OutOfMemoryError) { System.gc() Toast.makeText( activity.parent.parent, "Run out of memory please colse the other background apps and try again!", Toast.LENGTH_LONG ).show() } finally { if (httpclient != null) httpclient!!.getConnectionManager().shutdown() } return null } override fun onPostExecute(response: String) { if (mAsynclistener != null) { mAsynclistener!!.onTaskCompleted(response, serviceCode) } } } private fun showToast(msg: String) { Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show() } }
This class will help us to make http call to the remote server.
We will use the object of this class in the main activity.
Step 5. An Interface
Let us create a new kotlin class and give it a name like AsyncTaskCompleteListener.kt
Add the below lines in AsyncTaskCompleteListener.kt file.
interface AsyncTaskCompleteListener { fun onTaskCompleted(response: String, serviceCode: Int) }
It is an interface and we will implement it in the main activity.
Step 6. Last Customization
We have reached at out last step. We will customize activity_main.xml and MainActivity.kt file in this step.
So, in your activity_main.xml file, add the below code
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn" android:layout_gravity="center_horizontal" android:layout_marginTop="0dp" android:textAppearance="?android:attr/textAppearanceLarge" android:text="Select or Capture Image" /> <ImageView android:layout_width="300dp" android:layout_height="200dp" android:layout_gravity="center" android:layout_marginTop="20dp" android:scaleType="fitXY" android:src="@mipmap/ic_launcher" android:id="@+id/iv"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:layout_marginLeft="30dp" android:text="Below is the URL of uploaded image" android:textColor="#000" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tv" android:layout_marginTop="20dp" android:layout_marginLeft="30dp" android:text="dddd" android:textColor="#000" android:textSize="20sp" /> </LinearLayout>
Above contains one button, one image view and two text views.
One text view is static and it will not change it’s text. Another text view will hold the URL of the uploaded image.
Image view will preview the uploaded image.
Now in your MainActivity.kt class, write down the following source snippet
import android.Manifest import android.content.Intent import android.graphics.Bitmap import android.media.MediaScannerConnection import android.net.Uri import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.os.Environment import android.util.Log import android.view.View import android.widget.Button import android.widget.ImageView import android.widget.TextView import android.widget.Toast import com.example.uploadgallerykotlin.AsyncTaskCompleteListener import com.example.uploadgallerykotlin.MultiPartRequester import java.io.ByteArrayOutputStream import java.io.File import java.io.FileOutputStream import java.io.IOException import java.util.* import com.karumi.dexter.PermissionToken import com.karumi.dexter.MultiplePermissionsReport import com.karumi.dexter.listener.multi.MultiplePermissionsListener import com.karumi.dexter.Dexter import com.karumi.dexter.listener.PermissionRequest import org.json.JSONException import org.json.JSONObject class MainActivity : AppCompatActivity() , AsyncTaskCompleteListener { private var btn: Button? = null private var tv: TextView? = null private var imageview: ImageView? = null private val CAMERA = 1 internal var uploadURL = "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php" var arraylist: ArrayList<HashMap<String, String>>? = null override fun onCreate(savedInstanceState:Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) requestMultiplePermissions() btn = findViewById<View>(R.id.btn) as Button tv = findViewById<View>(R.id.tv) as TextView imageview = findViewById<View>(R.id.iv) as ImageView btn!!.setOnClickListener { val intent = Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE) startActivityForResult(intent, CAMERA) } } public override fun onActivityResult(requestCode:Int, resultCode:Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == CAMERA) { if (data != null) { val contentURI = data!!.data try { val bitmap = data.extras!!.get("data") as Bitmap val path = saveImage(bitmap) Toast.makeText(this@MainActivity, "Image Saved!", Toast.LENGTH_SHORT).show() imageview!!.setImageBitmap(bitmap) uploadImage(path) } catch (e: IOException) { e.printStackTrace() Toast.makeText(this@MainActivity, "Failed!", Toast.LENGTH_SHORT).show() } } } } private fun uploadImage(path: String) { val map = HashMap<String, String>() map.put("url", "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php") map.put("filename", path) MultiPartRequester(this, map, CAMERA, this) } override fun onTaskCompleted(response: String, serviceCode: Int) { Log.d("respon", response.toString()) when (serviceCode) { CAMERA -> if (isSuccess(response)) { val url = getURL(response) tv!!.text = url tv!!.setOnClickListener(View.OnClickListener { val browserIntent = Intent(Intent.ACTION_VIEW) browserIntent.data = Uri.parse(url) startActivity(browserIntent) }) } } } fun isSuccess(response: String): Boolean { try { val jsonObject = JSONObject(response) return jsonObject.optString("status") == "true" } catch (e: JSONException) { e.printStackTrace() } return false } fun getURL(response:String):String { var url = "" try { val jsonObject = JSONObject(response) jsonObject.toString().replace("\\\\", "") if (jsonObject.getString("status").equals("true")) { arraylist = ArrayList<HashMap<String, String>>() val dataArray = jsonObject.getJSONArray("data") for (i in 0 until dataArray.length()) { val dataobj = dataArray.getJSONObject(i) url = dataobj.optString("pathToFile") } } } catch (e: JSONException) { e.printStackTrace() } return url } fun saveImage(myBitmap: Bitmap):String { val bytes = ByteArrayOutputStream() myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bytes) val wallpaperDirectory = File( (Environment.getExternalStorageDirectory()).toString() + IMAGE_DIRECTORY) // have the object build the directory structure, if needed. Log.d("fee",wallpaperDirectory.toString()) if (!wallpaperDirectory.exists()) { wallpaperDirectory.mkdirs() } try { Log.d("heel",wallpaperDirectory.toString()) val f = File(wallpaperDirectory, ((Calendar.getInstance() .getTimeInMillis()).toString() + ".jpg")) f.createNewFile() val fo = FileOutputStream(f) fo.write(bytes.toByteArray()) MediaScannerConnection.scanFile(this, arrayOf(f.getPath()), arrayOf("image/jpeg"), null) fo.close() Log.d("TAG", "File Saved::--->" + f.getAbsolutePath()) return f.getAbsolutePath() } catch (e1: IOException) { e1.printStackTrace() } return "" } companion object { private val IMAGE_DIRECTORY = "/demonuts_upload" } private fun requestMultiplePermissions() { Dexter.withActivity(this) .withPermissions( Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE ) .withListener(object : MultiplePermissionsListener { override fun onPermissionsChecked(report: MultiplePermissionsReport) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { Toast.makeText(applicationContext, "All permissions are granted by user!", Toast.LENGTH_SHORT) .show() } // check for permanent denial of any permission if (report.isAnyPermissionPermanentlyDenied) { // show alert dialog navigating to Settings //openSettingsDialog() } } override fun onPermissionRationaleShouldBeShown(permissions: List<PermissionRequest>, token: PermissionToken) { token.continuePermissionRequest() } }).withErrorListener { Toast.makeText(applicationContext, "Some Error! ", Toast.LENGTH_SHORT).show() } .onSameThread() .check() } }
More Description
Let us read some more description on main activity’s code.
First of all, see the following lines
private var btn: Button? = null private var tv: TextView? = null private var imageview: ImageView? = null private val CAMERA = 1 internal var uploadURL = "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php" var arraylist: ArrayList<HashMap<String, String>>? = null
First line is the object of the button class. Second is the object of the text view.
Third line is for the object of the image view. Fourth line is one variable which have 1 as a value.
Fifth line is also a variable which holds the URL which we need to call to upload the image.
Last line is making an arraylist of hash map.
In the onCreate() method, compiler will first execute requestMultiplePermissions() function.
Below is the code for requestMultiplePermissions() function.
private fun requestMultiplePermissions() { Dexter.withActivity(this) .withPermissions( Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE ) .withListener(object : MultiplePermissionsListener { override fun onPermissionsChecked(report: MultiplePermissionsReport) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { Toast.makeText(applicationContext, "All permissions are granted by user!", Toast.LENGTH_SHORT) .show() } // check for permanent denial of any permission if (report.isAnyPermissionPermanentlyDenied) { // show alert dialog navigating to Settings //openSettingsDialog() } } override fun onPermissionRationaleShouldBeShown(permissions: List<PermissionRequest>, token: PermissionToken) { token.continuePermissionRequest() } }).withErrorListener { Toast.makeText(applicationContext, "Some Error! ", Toast.LENGTH_SHORT).show() } .onSameThread() .check() }
Above method is using the dexter library. This method is implementing the runtime permissions for us.
We will ask for read and write external sources in this function.
Now focus on the following codes
btn!!.setOnClickListener { val intent = Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE) startActivityForResult(intent, CAMERA) }
Compiler will execute the above code when the user clicks the button. It will open the android device camera so that user can capture the image before he uploads it.
When the user captures the image, compiler will call onActivityResult() method.
public override fun onActivityResult(requestCode:Int, resultCode:Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == CAMERA) { if (data != null) { val contentURI = data!!.data try { val bitmap = data.extras!!.get("data") as Bitmap val path = saveImage(bitmap) Toast.makeText(this@MainActivity, "Image Saved!", Toast.LENGTH_SHORT).show() imageview!!.setImageBitmap(bitmap) uploadImage(path) } catch (e: IOException) { e.printStackTrace() Toast.makeText(this@MainActivity, "Failed!", Toast.LENGTH_SHORT).show() } } } }
First of all, compiler will create the bitmap from the data. After this, it will use saveImage() method to save image in the external storage.
After this, it will set this image into the image view.
Below is the code lines for saveImage() method.
fun saveImage(myBitmap: Bitmap):String { val bytes = ByteArrayOutputStream() myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bytes) val wallpaperDirectory = File( (Environment.getExternalStorageDirectory()).toString() + IMAGE_DIRECTORY) // have the object build the directory structure, if needed. Log.d("fee",wallpaperDirectory.toString()) if (!wallpaperDirectory.exists()) { wallpaperDirectory.mkdirs() } try { Log.d("heel",wallpaperDirectory.toString()) val f = File(wallpaperDirectory, ((Calendar.getInstance() .getTimeInMillis()).toString() + ".jpg")) f.createNewFile() val fo = FileOutputStream(f) fo.write(bytes.toByteArray()) MediaScannerConnection.scanFile(this, arrayOf(f.getPath()), arrayOf("image/jpeg"), null) fo.close() Log.d("TAG", "File Saved::--->" + f.getAbsolutePath()) return f.getAbsolutePath() } catch (e1: IOException) { e1.printStackTrace() } return "" }
This method uses bitmap to save the image.
After saving the image, compiler will call uploadImage() method to upload the image to the server.
Following is the code block for uploadImage() method.
private fun uploadImage(path: String) { val map = HashMap<String, String>() map.put("url", "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php") map.put("filename", path) MultiPartRequester(this, map, CAMERA, this) }
Compiler will first create the hashmap. Then it will add two maps in it. One is for URL to the PHP web service and another is for path to the image.
Here, We need to implement an interface to this main activity. See the below line
class MainActivity : AppCompatActivity() , AsyncTaskCompleteListener
So if we implement this interface, we need to write a method onTaskCompleted().
Compiler will run this onTaskCompleted() method when system has uploaded the image to the server and also had get the JSON response.
Following is the code for onTaskCompleted() method
override fun onTaskCompleted(response: String, serviceCode: Int) { Log.d("respon", response.toString()) when (serviceCode) { CAMERA -> if (isSuccess(response)) { val url = getURL(response) tv!!.text = url tv!!.setOnClickListener(View.OnClickListener { val browserIntent = Intent(Intent.ACTION_VIEW) browserIntent.data = Uri.parse(url) startActivity(browserIntent) }) } } }
In this method, compiler will fetch the URL to the uploaded image from the JSON response.
Then it will set the text value of a text view as this URL. Now there is on click implementation in this method for text view.
Meaning is that, when the user clicks the text view, compiler will open the uploaded image into the web browser.
Download Code For Kotlin Upload Image From Camera
https://github.com/demonuts/Kotlin-Upload-Image-From-Camera-To-Server-Android-Example