0

Trying to learn Android, so I want to upload Image from camera/gallery to server using retrofit, Got Activity code from Internet and tried converting into Fragment. It creates an app, but when I choose from Gallery after selecting Image It gives Error.

After Community Suggestion I updated my Code, and there is just one Error occurring while uploading File

Response{protocol=http/1.1, code=404, message=Not Found, url=.........}

My Full Page Code is Following

package com.example.tc.ui.home;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;



import com.afollestad.materialdialogs.MaterialDialog;
import com.bumptech.glide.Glide;
import com.example.tc.BuildConfig;
import com.example.tc.R;
import com.example.tc.networking.ApiConfig;
import com.example.tc.networking.AppConfig;
import com.example.tc.networking.ServerResponse;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Logger;

import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;


public class HomeFragment extends Fragment implements View.OnClickListener {

    ImageView imageView;
    Button pickImage, upload;
    private static final int REQUEST_TAKE_PHOTO = 0;
    private static final int REQUEST_PICK_PHOTO = 2;
    private Uri mMediaUri;
    private static final int CAMERA_PIC_REQUEST = 1111;

//    private static final String TAG = ImageActivity.class.getSimpleName();
    private static final String TAG = HomeFragment.class.getSimpleName();

    private static final int CAMERA_CAPTURE_IMAGE_REQUEST_CODE = 100;

    public static final int MEDIA_TYPE_IMAGE = 1;

    private Uri fileUri;

    private String mediaPath;

    private Button btnCapturePicture;

    private String mImageFileLocation = "";
    public static final String IMAGE_DIRECTORY_NAME = "Android File Upload";
    ProgressDialog pDialog;
    private String postPath;

    private HomeViewModel homeViewModel;

    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View root = inflater.inflate(R.layout.fragment_home, container, false);

//Suggested by community @Siddharth
        imageView = root.findViewById(R.id.preview);
        pickImage = root.findViewById(R.id.pickImage);
        upload = root.findViewById(R.id.upload);

        pickImage.setOnClickListener(this);
        upload.setOnClickListener(this);
        initDialog();


        return root;

    }

    @Override
    public void onClick(final View v) {
        switch (v.getId()) {
            case R.id.pickImage:
                new MaterialDialog.Builder(getActivity())
                        .title(R.string.uploadImages)
                        .items(R.array.uploadImages)
                        .itemsIds(R.array.itemIds)
                        .itemsCallback(new MaterialDialog.ListCallback() {
                            @Override
                            public void onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
                                switch (which) {
                                    case 0:
                                        Intent galleryIntent = new Intent(Intent.ACTION_PICK,
                                                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                                        startActivityForResult(galleryIntent, REQUEST_PICK_PHOTO);
                                        break;
                                    case 1:
                                        captureImage();

                                        break;
                                    case 2:
                                        imageView.setImageResource(R.drawable.ic_launcher_background);
                                        break;
                                }
                            }
                        })
                        .show();
                break;
            case R.id.upload:
                uploadFile();
                break;
        }
    }

    private boolean isExternalStorageAvailable() {
        String state = Environment.getExternalStorageState();
        if(Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        }
        else {
            return false;
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK) {
            if (requestCode == REQUEST_TAKE_PHOTO || requestCode == REQUEST_PICK_PHOTO) {
                if (data != null) {
                    // Get the Image from data
                    Uri selectedImage = data.getData();
                    String[] filePathColumn = {MediaStore.Images.Media.DATA};
                    Cursor cursor = getActivity().getContentResolver().query(selectedImage, filePathColumn, null, null, null);
                    assert cursor != null;
                    cursor.moveToFirst();

                    int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
                    mediaPath = cursor.getString(columnIndex);
                 //           Toast.makeText(getActivity(), "Hemant File Uploaded Successfully...", Toast.LENGTH_LONG).show();
                    // Set the Image in ImageView for Previewing the Media
                    imageView.setImageBitmap(BitmapFactory.decodeFile(mediaPath));
                    cursor.close();


                    postPath = mediaPath;
                }


            }else if (requestCode == CAMERA_PIC_REQUEST){
                if (Build.VERSION.SDK_INT > 21) {

                    Glide.with(getActivity())
                            .load(mImageFileLocation)
                            .into(imageView);
                    postPath = mImageFileLocation;

                }else{
                    Glide.with(getActivity()).load(fileUri).into(imageView);
                    postPath = fileUri.getPath();

                }

            }

        }
        else if (resultCode != RESULT_CANCELED) {
            Toast.makeText(getActivity(), "Sorry, there was an error!", Toast.LENGTH_LONG).show();
        }
    }



    /**
     * Checking device has camera hardware or not
     * */
    private boolean isDeviceSupportCamera() {
        if (getActivity().getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_CAMERA)) {
            // this device has a camera
            return true;
        } else {
            // no camera on this device
            return false;
        }
    }

    protected void initDialog() {

        pDialog = new ProgressDialog(getActivity());
        pDialog.setMessage(getString(R.string.msg_loading));
        pDialog.setCancelable(true);
    }


    protected void showpDialog() {

        if (!pDialog.isShowing()) pDialog.show();
    }

    protected void hidepDialog() {

        if (pDialog.isShowing()) pDialog.dismiss();
    }


    /**
     * Launching camera app to capture image
     */
    private void captureImage() {

        if (Build.VERSION.SDK_INT > 21) { //use this if Lollipop_Mr1 (API 22) or above
            Intent callCameraApplicationIntent = new Intent();
            callCameraApplicationIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

            // We give some instruction to the intent to save the image
            File photoFile = null;

            try {
                // If the createImageFile will be successful, the photo file will have the address of the file
                photoFile = createImageFile();
                // Here we call the function that will try to catch the exception made by the throw function
            } catch (IOException e) {
                Logger.getAnonymousLogger().info("Exception error in generating the file");
                e.printStackTrace();
            }
            // Here we add an extra file to the intent to put the address on to. For this purpose we use the FileProvider, declared in the AndroidManifest.
            Uri outputUri = FileProvider.getUriForFile(
                    getActivity(),
                    BuildConfig.APPLICATION_ID + ".provider",
                    photoFile);
            callCameraApplicationIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);

            // The following is a new line with a trying attempt
            callCameraApplicationIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

            Logger.getAnonymousLogger().info("Calling the camera App by intent");

            // The following strings calls the camera app and wait for his file in return.
            startActivityForResult(callCameraApplicationIntent, CAMERA_PIC_REQUEST);
        } else {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

            fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);

            intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

            // start the image capture Intent
            startActivityForResult(intent, CAMERA_PIC_REQUEST);
        }


    }

    File createImageFile() throws IOException {
        Logger.getAnonymousLogger().info("Generating the image - method started");

        // Here we create a "non-collision file name", alternatively said, "an unique filename" using the "timeStamp" functionality
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmSS").format(new Date());
        String imageFileName = "IMAGE_" + timeStamp;
        // Here we specify the environment location and the exact path where we want to save the so-created file
        File storageDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES + "/photo_saving_app");
        Logger.getAnonymousLogger().info("Storage directory set");

        // Then we create the storage directory if does not exists
        if (!storageDirectory.exists()) storageDirectory.mkdir();

        // Here we create the file using a prefix, a suffix and a directory
        File image = new File(storageDirectory, imageFileName + ".jpg");
        // File image = File.createTempFile(imageFileName, ".jpg", storageDirectory);

        // Here the location is saved into the string mImageFileLocation
        Logger.getAnonymousLogger().info("File name and path set");

        mImageFileLocation = image.getAbsolutePath();
        // fileUri = Uri.parse(mImageFileLocation);
        // The file is returned to the previous intent across the camera application
        return image;
    }


    /**
     * Here we store the file url as it will be null after returning from camera
     * app
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        // save file url in bundle as it will be null on screen orientation
        // changes
        outState.putParcelable("file_uri", fileUri);
    }

    @Override
    public void onViewStateRestored (Bundle savedInstanceState) {
        super.onViewStateRestored( savedInstanceState );
       // fileUri = savedInstanceState.getParcelable("file_uri");
    }

    /**
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        // get the file url
        fileUri = savedInstanceState.getParcelable("file_uri");
    }
     **/

    /**
     * Receiving activity result method will be called after closing the camera
     * */

    /**
     * ------------ Helper Methods ----------------------
     * */

    /**
     * Creating file uri to store image/video
     */
    public Uri getOutputMediaFileUri(int type) {
        return Uri.fromFile(getOutputMediaFile(type));
    }

    /**
     * returning image / video
     */
    private static File getOutputMediaFile(int type) {

        // External sdcard location
        File mediaStorageDir = new File(
                Environment
                        .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                IMAGE_DIRECTORY_NAME);

        // Create the storage directory if it does not exist
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                Log.d(TAG, "Oops! Failed create "
                        + IMAGE_DIRECTORY_NAME + " directory");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                Locale.getDefault()).format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator
                    + "IMG_" + ".jpg");
        }  else {
            return null;
        }

        return mediaFile;
    }

    // Uploading Image/Video
    private void uploadFile() {
        if (postPath == null || postPath.equals("")) {
            Toast.makeText(getActivity(), "please select an image ", Toast.LENGTH_LONG).show();
            return;
        } else {
            showpDialog();

            // Map is used to multipart the file using okhttp3.RequestBody
            Map<String, RequestBody> map = new HashMap<>();
            File file = new File(postPath);

            // Parsing any Media type file
            RequestBody requestBody = RequestBody.create(MediaType.parse("*/*"), file);
            map.put("file\"; filename=\"" + file.getName() + "\"", requestBody);
            ApiConfig getResponse = AppConfig.getRetrofit().create(ApiConfig.class);
            Call<ServerResponse> call = getResponse.upload("token", map);
            call.enqueue(new Callback<ServerResponse>() {
                @Override
                public void onResponse(Call<ServerResponse> call, Response<ServerResponse> response) {
                    if (response.isSuccessful()){
                        if (response.body() != null){
                            hidepDialog();
                            ServerResponse serverResponse = response.body();
//                            Toast.makeText(getApplicationContext(), serverResponse.getMessage(), Toast.LENGTH_SHORT).show();
                            Toast.makeText(getActivity(), " uploading image", Toast.LENGTH_SHORT).show();

                        }
                    }else {
                        hidepDialog();
                        Toast.makeText(getActivity(), "problem uploading image", Toast.LENGTH_SHORT).show();
                    }
                }

                @Override
                public void onFailure(Call<ServerResponse> call, Throwable t) {
                    hidepDialog();
                    Log.v("Response gotten is", t.getMessage());
                }
            });
        }
    }

}

layout is following

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">




    <ImageView
        android:id="@+id/preview"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:layout_marginTop="10dp"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_launcher_background" />

    <Button
        android:id="@+id/pickImage"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="-200dp"
        android:layout_marginTop="500dp"
        android:layout_marginRight="2dp"
        android:layout_marginBottom="100dp"
        android:text="Pick Image" />

    <Button
        android:id="@+id/upload"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="-200dp"
        android:layout_marginTop="300dp"
        android:layout_marginRight="2dp"
        android:layout_marginBottom="200dp"
        android:text="Upload" />

</LinearLayout>

Implimatations are following

implementation 'com.android.support:support-v4:28.1.0'

    implementation 'com.github.bumptech.glide:glide:3.8.0'
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.2.0'


    testImplementation 'junit:junit:4.12'
    androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })

ApiConfig is

public interface ApiConfig {

    @Multipart
    @POST("images/upload_image.php")
    Call<ServerResponse> upload(
            @Header("Authorization") String authorization,
            @PartMap Map<String, RequestBody> map
    );
}

ServerResponse

public class ServerResponse {

    // variable name should be same as in the json response from php
    @SerializedName("success")
    boolean success;
    @SerializedName("message")
    String message;

    public String getMessage() {
        return message;
    }

    public boolean getSuccess() {
        return success;
    }

}

App Config


public class AppConfig {

//    public static String BASE_URL = "http://unitypuzzlegame.com/";
    public static String BASE_URL = "http://192.168.43.215/Retrofit/ImageUploadApi/";


    public static Retrofit getRetrofit() {

        return new Retrofit.Builder()
                .baseUrl(AppConfig.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
}

Manifests Permission(After community @Mike M Suggestion)

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET"/>

<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

I added another file in res->xml->provider_path.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
Hemant Maurya
  • 119
  • 1
  • 4
  • 13
  • 1
    You've already declared `ImageView imageView` in class level, and why are you declaring and initializing it once again inside `onCreateView()` method? Just remove `ImageView` inside `onCreateView()` – Shashanth Nov 24 '19 at 05:36
  • 1
    Does this answer your question? [What is a NullPointerException, and how do I fix it?](https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – Shashanth Nov 24 '19 at 05:38
  • 2
    As Shashanth states, don't re-declare `imageView` inside `onCreateView()`. Assign the field instead; i.e. `imageView = ...;`. The second error means that there is absent or mismatched data for the `FileProvider` in your manifest; e.g., the `authorities` attribute on the `` element doesn't match the one in the `FileProvider.getUriForFile()` call in your code, or maybe you're missing it altogether, etc. Beyond that, please try to restrict your questions to one problem per post. These are two unrelated issues. Please also provide [mcve]s. There is much more code here than is necessary. – Mike M. Nov 24 '19 at 08:21
  • @Shashanth Yes It fixed 1 issue, Now response protocol=http/1.1 code=404 message=not found, url= – Hemant Maurya Nov 24 '19 at 09:01
  • @MikeM. I applied all of your valuable suggestions and updated the code above. There is just one error is occurring now in file upload retrofit section, If you can check it and answer it in Answer portion please. – Hemant Maurya Nov 24 '19 at 09:45
  • @MikeM. the final mistake was a silly one server address spelling mistake... Fixed it... If you post your comment in Answer box I could tick it as green... – Hemant Maurya Nov 24 '19 at 11:03
  • 1
    I'm good. These are common issues, and have been addressed here before. I would close this as a duplicate, but you have three separate problems in one question. (Please try not to do that in the future.) Feel free to post an answer yourself, explaining everything you fixed, and accept that answer in 2 days, when the system lets you, so this will show as resolved. Thank you, though. I do appreciate the offer. Glad you got everything working. Cheers! – Mike M. Nov 24 '19 at 11:08

2 Answers2

0

this is my code that use for uploading image with Retrofit 2 in android:

        Bitmap bitmap = null;
    FileOutputStream fOut;
//imagePath is the Uri of my image
    File f = new File(URI.create(imagePath.toString()));
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    try {
        bitmap = BitmapFactory.decodeStream(new FileInputStream(f), null, 
options);
        Bitmap out = Bitmap.createScaledBitmap(bitmap, 300, 300, false);
        fOut = new FileOutputStream(f);
        out.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
        fOut.flush();
        fOut.close();
        bitmap.recycle();
        out.recycle();
    } catch (IOException e) {
        e.printStackTrace();

        return;
    }


    RequestBody fileReqBody = RequestBody.create(null, f);

    MultipartBody.Part part = MultipartBody.Part.createFormData("image", 
f.getName(), fileReqBody);


    Map<String, String> headers = new HashMap<>();
//token is the string that i use for Authorization
    headers.put("Authorization", token);
    Call<BaseModel> call = Api.getInstance().getAPI().uploadImage(headers, 
part);

and this is retrofit interface:

    @Multipart
@POST("user/image")
Call<BaseModel> uploadImage(@HeaderMap Map<String, String> token, @Part 
MultipartBody.Part image);

I used too many ways but nothing worked until this solution, Hope to help.

Update me in comments

alireza daryani
  • 787
  • 5
  • 16
0

There were three errors in my code. 1. I already declared ImageView imageView in class level, and why are you declaring and initialising it once again inside onCreateView().

  1. I didn't mentioned in Manifest file.

  2. Api link had a spelling mistake on server side so It was responding 404

I could fix First two with Help of community comments, and 3rd one by myself.

for the 1st problem I removed ImageView declaration inside onCreateView()

//Suggested by community @Siddharth
        imageView = root.findViewById(R.id.preview);
        pickImage = root.findViewById(R.id.pickImage);
        upload = root.findViewById(R.id.upload);


and for the second I added inside Manifest file

Suggested by @MikeM.

      <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

and Vala :)

Hemant Maurya
  • 119
  • 1
  • 4
  • 13