[Android] Fragment에서 구글 클라우드비전 사용하기

클라우드비전

저번 포스팅하고 이어지는 내용입니다. Fragment에서 Google Cloud Vision 사용법에 대해서 알아보겠습니다.

구글 클라우드비전을 사용할 줄 모르는 분들은 이전 포스팅을 참고해주세요

구글 클라우드 비전 사용법

 

Fragment에서 구글 클라우드비전 사용하기

개발을 하다보면 Fragment안에서 Google Cloud Vision을 적용시키고 싶은 경우가 있는데요.

Acitivty랑 Fragment가 서로 문법이 많이 달라 고생을 좀 많이 했습니다.

uploadImage

 

클라우드비전

이 3개의 자바 파일 중 Google Cloud Vision에서 우리는 MainActivity만 Fragment로 바꿔주면 됩니다.

 

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:25.1.1'
    compile 'com.android.support:design:25.1.1'
    compile 'com.google.api-client:google-api-client-android:1.20.0' exclude module: 'httpclient'
    compile 'com.google.http-client:google-http-client-gson:1.20.0' exclude module: 'httpclient'
    compile 'com.google.apis:google-api-services-vision:v1-rev2-1.21.0'
}

1. 바꾸기에 앞서 다른 코드는 문제가 없는지 Dependency와 Manifest만 확인하고 넘어가도록 하겠습니다.

일단 Dependency가 제대로 되어있는지 확인하겠습니다.

 

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

2. 그리고 구글 클라우드 비전을 사용하기 위해서는 인터넷과 카메라에 접근을 할 수 있어야 합니다.

Manifest에 인터넷과 카메라가 permission 되어있는지 확인해주세요.

 

package com.example.user.mynavigation;

/**
 * Created by user on 2016-12-26.
 */

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.vision.v1.Vision;
import com.google.api.services.vision.v1.VisionRequest;
import com.google.api.services.vision.v1.VisionRequestInitializer;
import com.google.api.services.vision.v1.model.AnnotateImageRequest;
import com.google.api.services.vision.v1.model.BatchAnnotateImagesRequest;
import com.google.api.services.vision.v1.model.BatchAnnotateImagesResponse;
import com.google.api.services.vision.v1.model.EntityAnnotation;
import com.google.api.services.vision.v1.model.Feature;
import com.google.api.services.vision.v1.model.Image;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import static android.app.Activity.RESULT_OK;

public class TabFragment2 extends Fragment {
    private static final String CLOUD_VISION_API_KEY = "Your API Key";
    public static final String FILE_NAME = "temp.jpg";
    private static final String ANDROID_CERT_HEADER = "X-Android-Cert";
    private static final String ANDROID_PACKAGE_HEADER = "X-Android-Package";

    private static final String TAG = TabFragment2.class.getSimpleName();
    private static final int GALLERY_PERMISSIONS_REQUEST = 0;
    private static final int GALLERY_IMAGE_REQUEST = 1;
    public static final int CAMERA_PERMISSIONS_REQUEST = 2;
    public static final int CAMERA_IMAGE_REQUEST = 3;

    private TextView mImageDetails;
    private ImageView mMainImage;

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

    View view = inflater.inflate(R.layout.tab_fragment_2,null);

    FloatingActionButton fab = (FloatingActionButton)view.findViewById(R.id.fab);

    fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {   
       AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
       builder
       .setMessage(R.string.dialog_select_prompt)
       .setPositiveButton(R.string.dialog_select_gallery, new DialogInterface.OnClickListener() {
           
       @Override
       public void onClick(DialogInterface dialog, int which) {
         startGalleryChooser();
       }
     })
     setNegativeButton(R.string.dialog_select_camera, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
          startCamera();
        }
      });
      builder.create().show();
    }
  });

   mImageDetails = (TextView)view.findViewById(R.id.image_details);
   mMainImage = (ImageView)view.findViewById(R.id.main_image);

   return view;
  }

   public void startGalleryChooser() {
     if (PermissionUtils.requestPermission(getActivity(), GALLERY_PERMISSIONS_REQUEST,
	Manifest.permission.READ_EXTERNAL_STORAGE)) {
	Intent intent = new Intent();
	intent.setType("image/*");
        MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), uri), 1200);	
        intent.setAction(Intent.ACTION_GET_CONTENT);
	startActivityForResult(Intent.createChooser(intent, "Select a photo"), GALLERY_IMAGE_REQUEST);
      }
    }

    public void startCamera() {
      if (PermissionUtils.requestPermission(getActivity(), CAMERA_PERMISSIONS_REQUEST,
	Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA)) {
	Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 이부분 재확인할것
	Uri photoUri = FileProvider.getUriForFile(getActivity(), getContext().getPackageName() + 
        ".provider",getCameraFile());
	intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
	intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
	startActivityForResult(intent, CAMERA_IMAGE_REQUEST);
	}
    }

    public File getCameraFile() {
	File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
	return new File(dir, FILE_NAME);
     }

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

	if (requestCode == GALLERY_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
	    uploadImage(data.getData());
	}else if (requestCode == CAMERA_IMAGE_REQUEST && resultCode == RESULT_OK) {
	    Uri photoUri = FileProvider.getUriForFile(getActivity(), getContext().getPackageName() 
            + ".provider",getCameraFile());
	    uploadImage(photoUri);
	}
     }

    @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
	@NonNull int[] grantResults) {
	  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
	  switch (requestCode) {
	   case CAMERA_PERMISSIONS_REQUEST:
	    if (PermissionUtils.permissionGranted(requestCode, CAMERA_PERMISSIONS_REQUEST, grantResults)) {
		startCamera();
	    }
	    break;
	    case GALLERY_PERMISSIONS_REQUEST:
	    if (PermissionUtils.permissionGranted(requestCode, GALLERY_PERMISSIONS_REQUEST, grantResults)) {
		startGalleryChooser();
	    }
	    break;
	}
    }

    public void uploadImage(Uri uri) {
	if (uri != null) {
	    try {
	        // scale the image to save on bandwidth
	        Bitmap bitmap = scaleBitmapDown(
		MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), uri), 1200);
		callCloudVision(bitmap);
		mMainImage.setImageBitmap(bitmap);

	    }catch (IOException e) {
		Log.d(TAG, "Image picking failed because " + e.getMessage());
		Toast.makeText(getActivity(), R.string.image_picker_error, Toast.LENGTH_LONG).show();
		}
	 } else {
	    Log.d(TAG, "Image picker gave us a null image.");
	    Toast.makeText(getActivity(), R.string.image_picker_error, Toast.LENGTH_LONG).show();
	}
    }

    private void callCloudVision(final Bitmap bitmap) throws IOException {
     // Switch text to loading
    mImageDetails.setText(R.string.loading_message);

    // Do the real work in an async task, because we need to use the network anyway
    new AsyncTask<Object, Void, String >() {
        @Override
        protected String doInBackground(Object... params) {
            try {
                HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
                JsonFactory jsonFactory = GsonFactory.getDefaultInstance();

                VisionRequestInitializer requestInitializer =
                new VisionRequestInitializer(CLOUD_VISION_API_KEY) {
                  /**
                   * We override this so we can inject important identifying fields into the HTTP
                   * headers. This enables use of a restricted cloud platform API key.
                   */
                  @Override
                  protected void initializeVisionRequest(VisionRequest<!--?--> visionRequest)
                    throws IOException {
                    super.initializeVisionRequest(visionRequest);

                    String packageName = getActivity().getPackageName();
                    visionRequest.getRequestHeaders().set(ANDROID_PACKAGE_HEADER, packageName);
                    String sig = 
                    PackageManagerUtils.getSignature(getActivity().getPackageManager(), packageName);
                    visionRequest.getRequestHeaders().set(ANDROID_CERT_HEADER, sig);
                 }
            };
            Vision.Builder builder = new Vision.Builder(httpTransport, jsonFactory, null);
            builder.setVisionRequestInitializer(requestInitializer);

            Vision vision = builder.build();
            BatchAnnotateImagesRequest batchAnnotateImagesRequest =
                    new BatchAnnotateImagesRequest();
            batchAnnotateImagesRequest.setRequests(new ArrayList<annotateimagerequest>() {{
                AnnotateImageRequest annotateImageRequest = new AnnotateImageRequest();

                // Add the image
                Image base64EncodedImage = new Image();
                // Convert the bitmap to a JPEG
                // Just in case it's a format that Android understands but Cloud Vision
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, 90, byteArrayOutputStream);
                byte[] imageBytes = byteArrayOutputStream.toByteArray();

                // Base64 encode the JPEG
                base64EncodedImage.encodeContent(imageBytes);
                annotateImageRequest.setImage(base64EncodedImage);

                // add the features we want
                annotateImageRequest.setFeatures(new ArrayList<feature>() {{
                    Feature textDetection = new Feature();
                    textDetection.setType("TEXT_DETECTION");
                    textDetection.setMaxResults(10);
                    add(textDetection);
                    }});
                    // Add the list of one thing to the request
                    add(annotateImageRequest);
                }});

                Vision.Images.Annotate annotateRequest =
                     vision.images().annotate(batchAnnotateImagesRequest);
                // Due to a bug: requests to Vision API containing large images fail when GZipped.
                annotateRequest.setDisableGZipContent(true);
                Log.d(TAG, "created Cloud Vision request object, sending request");

                BatchAnnotateImagesResponse response = annotateRequest.execute();
                return convertResponseToString(response);

            } catch (GoogleJsonResponseException e) {
            Log.d(TAG, "failed to make API request because " + e.getContent());
            } catch (IOException e) {
                Log.d(TAG, "failed to make API request because of other IOException " +
                    e.getMessage());
            }
            return "Cloud Vision API request failed. Check logs for details.";
        }

        protected void onPostExecute(String result) {
            mImageDetails.setText(result);
        }
      }.execute();
    }

    public Bitmap scaleBitmapDown(Bitmap bitmap, int maxDimension) {
    	int originalWidth = bitmap.getWidth();
	int originalHeight = bitmap.getHeight();
	int resizedWidth = maxDimension;
	int resizedHeight = maxDimension;
	if (originalHeight > originalWidth) {
	    resizedHeight = maxDimension;
	    resizedWidth = (int) (resizedHeight * (float) originalWidth / (float) originalHeight);
	} else if (originalWidth > originalHeight) {
	     resizedWidth = maxDimension;
	     resizedHeight = (int) (resizedWidth * (float) originalHeight / (float) originalWidth);
	} else if (originalHeight == originalWidth) {
	     resizedHeight = maxDimension;
	     resizedWidth = maxDimension;
	}
	     return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false);
	}

	private String convertResponseToString(BatchAnnotateImagesResponse response) {
	    String message = "분석을 완료했습니다.:\n\n";
	    List<entityannotation> labels = response.getResponses().get(0).getTextAnnotations();
	    if (labels != null) {
	        message += labels.get(0).getDescription();
	    } else {
	       message += "nothing";
            }
	   return message;
    }
}

3. Fragment에서 구글 클라우드 비전을 작동시키기 위해서 클라우드 비전 Java코드를 Activity에서 Fragment형식으로 바꿔줍니다. 이렇게 하시면 Fragment에서도 구글클라우드 비전이 정상적으로 작동됩니다.

 

[Android] 구글 클라우드 비전 API 키 발급 및 사용법

 

댓글

Designed by JB FACTORY