[Android] Fragment에서 구글 클라우드비전 사용하기
- Mobile/Android
- 2017. 10. 27.
저번 포스팅하고 이어지는 내용입니다. 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에서도 구글클라우드 비전이 정상적으로 작동됩니다.
'Mobile > Android' 카테고리의 다른 글
[Android] 설치 앱 아이콘 바꾸기 (6) | 2017.10.29 |
---|---|
[Android] 로딩화면 구현하기 Splash (5) | 2017.10.28 |
[Android] 구글 클라우드비전 API키 발급 및 사용법 (48) | 2017.10.27 |
[Android] ListView에 EditText넣기 (4) | 2017.10.25 |