I developing android application that need to upload images in server using okhttp3 library.
This library work great with small amount of time writing code. Then suddenly my internet connection get worst. I thought my application not working because no server response for almost a minute. Luckily android studio has great tool for debugging. I notice that my application have network activity therefore the android application is working.
- CustomRequestBody.java
- Data.java
- ItemRecyclerViewAdapter.java
- MainActivity.java
- PopupOfProgress.java
- UploaderAsyncTask.java
Android xml structure:
- activity_main.xml
- image_item.xml
- upload_progress.xml
Php file structure:
- index.php
- upload_receiver.php
Below are the JAVA codes:
1. CustomRequestBody.java
1. CustomRequestBody.java
package com.simpleinfo.uploadfileswithphp; import android.util.Log; import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; import okio.Buffer; import okio.BufferedSink; import okio.ForwardingSink; import okio.Okio; import okio.Sink; public class CustomRequestBody extends RequestBody { RequestBody mDelegateRequestBody; ProgressListener mProgressListener; CountingSink mCountingSink; Data mData; long mContentLength = 0; CustomRequestBody(RequestBody delegateRequestBody,ProgressListener progressListener,Data data){ mDelegateRequestBody = delegateRequestBody; try { mContentLength = mDelegateRequestBody.contentLength(); Log.e("size",mDelegateRequestBody.contentLength()+""); } catch (IOException e) { e.printStackTrace(); } mProgressListener = progressListener; mData = data; } @Override public long contentLength() { return mContentLength; } @Override public MediaType contentType() { return mDelegateRequestBody.contentType(); } @Override public void writeTo(BufferedSink sink) throws IOException { if(mCountingSink == null){ mCountingSink = new CountingSink(sink); } //CountingSink countingSink = new CountingSink(sink); BufferedSink bufferedSink = Okio.buffer(mCountingSink); mDelegateRequestBody.writeTo(bufferedSink); bufferedSink.flush(); } protected final class CountingSink extends ForwardingSink { long mUploaded = 0; long mContentLenght = 0; public CountingSink(Sink delegate) { super(delegate); mContentLenght = contentLength(); } @Override public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); mUploaded += byteCount; if(mProgressListener !=null){ mProgressListener.progress(byteCount,mContentLenght,mData); if( mUploaded >= CustomRequestBody.this.mContentLength ){ mProgressListener.onFileUploadDone(mData); } } } } public interface ProgressListener { void progress(long uploaded, long size, Data data); void onFileUploadDone(Data data); void onError(String errorMessage , Data data); } }
2. Data.java
package com.simpleinfo.uploadfileswithphp; public class Data { private String imageName; private String imagePath; private boolean isUploaded = false; public String getImageName() { return imageName; } public void setImageName(String imageName) { this.imageName = imageName; } public String getImagePath() { return imagePath; } public boolean isUploaded() { return isUploaded; } public void setUploaded(boolean uploaded) { isUploaded = uploaded; } public void setImagePath(String imagePath) { this.imagePath = imagePath; } }
3. ItemRecyclerViewAdapter.java
package com.simpleinfo.uploadfileswithphp; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.support.annotation.NonNull; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.io.File; import java.util.List; public class ItemRecyclerViewAdapter extends RecyclerView.Adapter<ItemRecyclerViewAdapter.CustomViewHolder> { List<Data> mDatas; Context mContext; public ItemRecyclerViewAdapter(List<Data> datas,Context context){ mDatas = datas; mContext = context; } @NonNull @Override public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { LayoutInflater inflater = LayoutInflater.from(mContext); CardView cardView = (CardView) inflater.inflate(R.layout.image_item,viewGroup,false); CustomViewHolder customViewHolder = new CustomViewHolder(cardView); return customViewHolder; } @Override public void onBindViewHolder(@NonNull CustomViewHolder customViewHolder, int i) { Data data = mDatas.get(i); File file = new File(data.getImagePath()); Bitmap bmp = BitmapFactory.decodeFile(file.getAbsolutePath()); customViewHolder.mImageView.setImageBitmap(bmp); customViewHolder.mCardView.setTag(data); customViewHolder.mImageNameTextView.setText(data.getImageName()); if(data.isUploaded()){ customViewHolder.mStatusTextView.setText("Status : Uploaded"); }else{ customViewHolder.mStatusTextView.setText(""); } } @Override public int getItemCount() { return mDatas.size(); } class CustomViewHolder extends RecyclerView.ViewHolder{ CardView mCardView; ImageView mImageView; TextView mImageNameTextView; TextView mStatusTextView; CustomViewHolder(@NonNull CardView cardView) { super(cardView); mCardView = cardView; mImageView = mCardView.findViewById(R.id.image_view); mImageNameTextView = mCardView.findViewById(R.id.image_name_textview); mStatusTextView = mCardView.findViewById(R.id.status_textview); } } }
4. MainActivity.java
package com.simpleinfo.uploadfileswithphp; import android.app.Activity; import android.app.AlertDialog; import android.content.ClipData; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.support.annotation.RequiresApi; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.CardView; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { int GET_IMAGE_REQUEST_CODE = 1; RecyclerView mRecyclerView; RecyclerView.LayoutManager mLayoutManager; ItemRecyclerViewAdapter mAdapter; List<Data> mDatas = new ArrayList<>(); PopupOfProgress mPopupOfProgress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = this.findViewById(R.id.recyclerview); mRecyclerView.setHasFixedSize(true); mLayoutManager = new GridLayoutManager(this,1); mRecyclerView.setLayoutManager(mLayoutManager); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.upload_file) { if(mDatas.size() == 0){ AlertDialog alertDialog = new AlertDialog.Builder(this).create(); alertDialog.setTitle("Message"); alertDialog.setMessage("Please select file."); alertDialog.show(); return false; } mPopupOfProgress = new PopupOfProgress(this); mPopupOfProgress.show(); new UploaderAsyncTask(mDatas, new CustomProgressListener()).execute(); return true; }else if(id == R.id.select_file){ Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); startActivityForResult(Intent.createChooser(intent, "Select Picture"), GET_IMAGE_REQUEST_CODE); } return super.onOptionsItemSelected(item); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == GET_IMAGE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { //TODO: action ClipData clipData = null; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { clipData = data.getClipData(); } if(clipData == null){ String path = ""; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { path = this.getPathForKitKatAbove(this,data.getData()); }else{ path = this.getRealPathFromURI(data.getData()); } Data imageData = new Data(); imageData.setImagePath(path); imageData.setImageName(this.getImageName(path)); mDatas.add(imageData); mAdapter = new ItemRecyclerViewAdapter(mDatas,this); mRecyclerView.setAdapter(mAdapter); mRecyclerView.setItemViewCacheSize(1); return; } int itemCount = clipData.getItemCount(); for(int x = 0 ; x < itemCount ; x++){ ClipData.Item item = clipData.getItemAt(x); Uri uri = item.getUri(); String path = ""; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { path = this.getPathForKitKatAbove(this,uri); }else{ path= this.getRealPathFromURI(uri); } Data imageData = new Data(); imageData.setImagePath(path); imageData.setImageName(this.getImageName(path)); mDatas.add(imageData); } mAdapter = new ItemRecyclerViewAdapter(mDatas,this); mRecyclerView.setAdapter(mAdapter); mRecyclerView.setItemViewCacheSize(1); } } @RequiresApi(api = Build.VERSION_CODES.KITKAT) protected String getPathForKitKatAbove(Context context, Uri uri){ String filePath = ""; if(DocumentsContract.isDocumentUri(context, uri)) { String id = DocumentsContract.getDocumentId(uri); String splitedId = id.split(":")[1]; String[] column = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver(). query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, MediaStore.Images.Media._ID + "=?", new String[]{splitedId}, null); int columnIndex = cursor.getColumnIndex(column[0]); if (cursor.moveToFirst()) { filePath = cursor.getString(columnIndex); } cursor.close(); } return filePath; } protected String getRealPathFromURI(Uri uri) { String[] projection = { MediaStore.Images.Media.DATA }; Cursor cursor = managedQuery(uri, projection, null, null, null); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getString(column_index); } private String getImageName(String path){ String splitedString[] = path.split("/"); return splitedString[splitedString.length -1]; } private void updateStatusInView(){ /** * dismiss upload dialog */ mPopupOfProgress.mAlertDialog.dismiss(); int count = mLayoutManager.getChildCount(); for(int x = 0 ; x < count ; x++){ CardView cardView = (CardView) mLayoutManager.getChildAt(x); Data data = (Data) cardView.getTag(); TextView statusTextView = cardView.findViewById(R.id.status_textview); if(data.isUploaded()){ statusTextView.setText("Status : Uploaded"); }else{ statusTextView.setText("Status : Not uploaded"); } } } class CustomProgressListener implements CustomRequestBody.ProgressListener{ int totalFileToUpload = 0 ; int totalFileAttempToUploaded = 0; float overAllDataUploaded = 0; public void setTotalFileToUpload(int totalFileToUpload) { this.totalFileToUpload = totalFileToUpload; } public void resetOverallUpload(){ overAllDataUploaded = 0; } @Override public void progress(long uploaded, final long size, final Data data) { float sizeFloat = size; overAllDataUploaded += uploaded; final float percentageUpload = (overAllDataUploaded / sizeFloat) * 100; runOnUiThread(new Runnable() { @Override public void run() { mPopupOfProgress.mPerFileProgress.setProgress((int) overAllDataUploaded); mPopupOfProgress.mPerFileProgress.setMax((int) size); mPopupOfProgress.mOverallProgress.setProgress(totalFileAttempToUploaded); mPopupOfProgress.mOverallProgress.setMax(totalFileToUpload); mPopupOfProgress.mFileNameTextView.setText(data.getImageName()); mPopupOfProgress.mPerFileProgressTextview.setText(String.format("%.2f",percentageUpload)+" %"); mPopupOfProgress.mOverallProgressTextView.setText(totalFileAttempToUploaded +"/"+totalFileToUpload); } }); } @Override public void onFileUploadDone(Data data) { totalFileAttempToUploaded++; data.setUploaded(true); if(totalFileToUpload <= totalFileAttempToUploaded){ runOnUiThread(new Runnable() { @Override public void run() { updateStatusInView(); } }); } } @Override public void onError(String errorMessage, final Data data) { data.setUploaded(false); totalFileAttempToUploaded++; runOnUiThread(new Runnable() { @Override public void run() { mPopupOfProgress.mFileNameTextView.setText("Failed :"+data.getImageName()); mPopupOfProgress.mOverallProgressTextView.setText(totalFileAttempToUploaded +"/"+totalFileToUpload); if(totalFileToUpload <= totalFileAttempToUploaded){ updateStatusInView(); } } }); } } }
5. PopupOfProgress.java
package com.simpleinfo.uploadfileswithphp; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.view.LayoutInflater; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; public class PopupOfProgress extends AlertDialog.Builder { Context mContext; ProgressBar mPerFileProgress; ProgressBar mOverallProgress; TextView mFileNameTextView; TextView mPerFileProgressTextview; TextView mOverallProgressTextView; AlertDialog mAlertDialog; public PopupOfProgress(Context context) { super(context); mContext = context; this.init(); } @Override public AlertDialog show() { mAlertDialog = super.show(); return mAlertDialog; } private void init(){ LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.upload_progress,null,false); mPerFileProgress = linearLayout.findViewById(R.id.per_file_progress); mFileNameTextView= linearLayout.findViewById(R.id.file_name_textview); mPerFileProgressTextview= linearLayout.findViewById(R.id.per_file_progress_textview); mOverallProgress = linearLayout.findViewById(R.id.overall_progress); mOverallProgressTextView = linearLayout.findViewById(R.id.overall_progress_textview); this.setView(linearLayout); } }
5. UploaderAsyncTask.java
package com.simpleinfo.uploadfileswithphp; import android.os.AsyncTask; import android.util.Log; import java.io.File; import java.io.IOException; import java.util.List; import okhttp3.CacheControl; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; public class UploaderAsyncTask extends AsyncTask<String,String,String> { private final static String TAG = "UploaderAsyncTask"; private final static String URL = "http://192.168.43.202/upload_receiver.php"; OkHttpClient mClient; List<Data> mDatas; MainActivity.CustomProgressListener mCustomProgressListener; public UploaderAsyncTask(List<Data> mData,MainActivity.CustomProgressListener customProgressListener){ mClient = new OkHttpClient(); mDatas = mData; mCustomProgressListener = customProgressListener; } @Override protected String doInBackground(String... strings) { /** * for overall progress */ mCustomProgressListener.setTotalFileToUpload(mDatas.size()); for(Data data : mDatas){ MultipartBody.Builder mBuilder = new MultipartBody.Builder(); mBuilder.setType(MultipartBody.FORM); File file = new File(data.getImagePath()); mBuilder.addFormDataPart("image", data.getImageName(), RequestBody.create(MediaType.parse("image/jpeg"),file)); mBuilder.addFormDataPart("image_description", "This is the description"); MultipartBody requestBody = mBuilder.build(); CustomRequestBody customRequestBody = new CustomRequestBody(requestBody,mCustomProgressListener,data); Request request = new Request.Builder() .url(URL) .post(customRequestBody) .cacheControl(new CacheControl.Builder().noCache().build()) .build(); try { Response response = mClient.newCall(request).execute(); if(!response.isSuccessful()){ mCustomProgressListener.onError("Failed to upload with error code "+response.code(),data); } ResponseBody responseBodyCopy = response.peekBody(Long.MAX_VALUE); /** * server response */ Log.e(TAG,responseBodyCopy.string()); } catch (IOException e) { e.printStackTrace(); mCustomProgressListener.onError(e.getMessage(),data); }finally { /** * */ mCustomProgressListener.resetOverallUpload(); } } return null; } }
Below are the XML layout files:
1. activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="8dp"> <ProgressBar android:id="@+id/per_file_progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/file_name_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="File:" /> <TextView android:id="@+id/per_file_progress_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" android:gravity="right" android:text="0/0" /> </LinearLayout> <ProgressBar android:id="@+id/overall_progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/overall_progress_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="right" android:text="0/0" /> </LinearLayout> </LinearLayout>
2. image_item.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:elevation="10dp" android:layout_margin="4dp" android:foreground="?android:attr/selectableItemBackground" android:clickable="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <ImageView android:id="@+id/image_view" android:layout_width="100dp" android:layout_height="100dp" app:srcCompat="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp"> <TextView android:id="@+id/image_name_textview" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Image Name" /> <TextView android:id="@+id/status_textview" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Status" /> </LinearLayout> </LinearLayout> </android.support.v7.widget.CardView>
2. upload_progress.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="8dp"> <ProgressBar android:id="@+id/per_file_progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/file_name_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="File:" /> <TextView android:id="@+id/per_file_progress_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" android:gravity="right" android:text="0/0" /> </LinearLayout> <ProgressBar android:id="@+id/overall_progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/overall_progress_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="right" android:text="0/0" /> </LinearLayout> </LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.simpleinfo.uploadfileswithphp"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Make sure to add the code below in build.gradle
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
Below are PHP codes:
1. index.php
<!DOCTYPE html> <html> <head> <title>Android Upload File</title> </head> <body> <h1>Uploaded Images</h1> <?php $directory = "images_uploaded/"; $images = glob($directory . "/*.jpg"); foreach($images as $image){ ?> <img src="<?php echo $image?>" width="200px" height="200px" alt="<?php echo $image?>"> <?php } ?> </body> </html>
2. upload_receiver.php
<?php $target_dir = "images_uploaded"; if (!file_exists($target_dir)) { mkdir($target_dir, 0777, true); } $target_file = $target_dir .'/'. basename($_FILES["image"]["name"]); if(isset($_FILES["image"])) { $check = getimagesize($_FILES["image"]["tmp_name"]); if($check !== false) { if (move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)) { echo "The file ". basename( $_FILES["image"]["name"]). " has been uploaded."; } else { echo "Sorry, there was an error uploading your file."; } } else { echo "File is not an image."; } }else{ echo '*** Failed ***'; } ?>
you have written an excellent blog.. keep share your knowledge...
ReplyDeleteSelenium with Java Training
Selenium with Python Online Training