|
/* |
|
* Copyright 2016 Google Inc. All Rights Reserved. |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
/* Author: Lorenzo Bracco a.k.a. devtry (devtry@riseup.net) */ |
|
|
|
package com.dexin; |
|
|
|
import android.Manifest; |
|
import android.content.DialogInterface; |
|
import android.content.Intent; |
|
import android.graphics.Bitmap; |
|
import android.graphics.Canvas; |
|
import android.graphics.Color; |
|
import android.graphics.ColorFilter; |
|
import android.graphics.PixelFormat; |
|
import android.graphics.drawable.BitmapDrawable; |
|
import android.graphics.drawable.ColorDrawable; |
|
import android.graphics.drawable.Drawable; |
|
import android.net.Uri; |
|
import android.os.AsyncTask; |
|
import android.os.Bundle; |
|
import android.os.Environment; |
|
import android.provider.MediaStore; |
|
import android.support.annotation.IntRange; |
|
import android.support.annotation.NonNull; |
|
import android.support.annotation.Nullable; |
|
import android.support.design.widget.FloatingActionButton; |
|
import android.support.v4.content.FileProvider; |
|
import android.support.v7.app.AlertDialog; |
|
import android.support.v7.app.AppCompatActivity; |
|
import android.support.v7.widget.Toolbar; |
|
import android.util.Log; |
|
import android.view.View; |
|
import android.widget.ImageView; |
|
import android.widget.TextView; |
|
import android.widget.Toast; |
|
|
|
import android.text.Html; |
|
|
|
import android.R.color; |
|
|
|
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 java.util.ArrayList; |
|
import java.util.List; |
|
import java.util.Locale; |
|
|
|
public class MainActivity extends AppCompatActivity { |
|
private static final String CLOUD_VISION_API_KEY = " AIzaSyDGS_DRiMBwXN3ijn68vCe7NoQ-8bcRuL0 "; |
|
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 = MainActivity.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 |
|
protected void onCreate(Bundle savedInstanceState) { |
|
super.onCreate(savedInstanceState); |
|
getWindow().getDecorView().setBackground(getResources().getDrawable(R.drawable.map)); |
|
setContentView(R.layout.activity_main); |
|
|
|
findViewById(R.id.loadingPanel).setVisibility(View.INVISIBLE); |
|
|
|
ImageView viewImage = (ImageView)findViewById(R.id.main_image); |
|
viewImage.setVisibility(View.INVISIBLE); |
|
|
|
/*SupportMapFragment mapFragment = null; |
|
while (mapFragment == null) |
|
mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); |
|
|
|
mapFragment.getMapAsync(this);*/ |
|
|
|
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); |
|
fab.setImageDrawable(getResources().getDrawable(R.drawable.buttonphoto2)); |
|
fab.setOnClickListener(new View.OnClickListener() { |
|
@Override |
|
public void onClick(View view) { |
|
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); |
|
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) findViewById(R.id.image_details); |
|
mMainImage = (ImageView) findViewById(R.id.main_image); |
|
} |
|
|
|
public void startGalleryChooser() { |
|
if (PermissionUtils.requestPermission(this, GALLERY_PERMISSIONS_REQUEST, Manifest.permission.READ_EXTERNAL_STORAGE)) { |
|
Intent intent = new Intent(); |
|
intent.setType("image/*"); |
|
intent.setAction(Intent.ACTION_GET_CONTENT); |
|
startActivityForResult(Intent.createChooser(intent, "Select a photo"), |
|
GALLERY_IMAGE_REQUEST); |
|
} |
|
} |
|
|
|
public void startCamera() { |
|
if (PermissionUtils.requestPermission( |
|
this, |
|
CAMERA_PERMISSIONS_REQUEST, |
|
Manifest.permission.READ_EXTERNAL_STORAGE, |
|
Manifest.permission.CAMERA)) { |
|
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); |
|
Uri photoUri = FileProvider.getUriForFile(this, getApplicationContext().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 = getExternalFilesDir(Environment.DIRECTORY_PICTURES); |
|
return new File(dir, FILE_NAME); |
|
} |
|
|
|
@Override |
|
protected 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(this, getApplicationContext().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(getContentResolver(), uri), |
|
1200); |
|
|
|
callCloudVision(bitmap); |
|
mMainImage.setImageBitmap(bitmap); |
|
|
|
} catch (IOException e) { |
|
Log.d(TAG, "Image picking failed because " + e.getMessage()); |
|
Toast.makeText(this, R.string.image_picker_error, Toast.LENGTH_LONG).show(); |
|
} |
|
} else { |
|
Log.d(TAG, "Image picker gave us a null image."); |
|
Toast.makeText(this, R.string.image_picker_error, Toast.LENGTH_LONG).show(); |
|
} |
|
} |
|
|
|
private void callCloudVision(final Bitmap bitmap) throws IOException { |
|
// Switch text to loading |
|
findViewById(R.id.loadingPanel).setVisibility(View.VISIBLE); |
|
getWindow().getDecorView().setBackground(null); |
|
getWindow().getDecorView().setBackgroundColor(Color.WHITE); |
|
|
|
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); |
|
fab.setImageDrawable(getResources().getDrawable(R.drawable.check)); |
|
|
|
ImageView viewImage = (ImageView)findViewById(R.id.main_image); |
|
viewImage.setVisibility(View.VISIBLE); |
|
|
|
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 = getPackageName(); |
|
visionRequest.getRequestHeaders().set(ANDROID_PACKAGE_HEADER, packageName); |
|
|
|
String sig = PackageManagerUtils.getSignature(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 labelDetection = new Feature(); |
|
labelDetection.setType("LABEL_DETECTION"); |
|
labelDetection.setMaxResults(10); |
|
add(labelDetection); |
|
}}); |
|
|
|
// 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) { |
|
findViewById(R.id.loadingPanel).setVisibility(View.GONE); |
|
mImageDetails.setText(Html.fromHtml(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 = "Recognized as "; |
|
|
|
List<EntityAnnotation> labels = response.getResponses().get(0).getLabelAnnotations(); |
|
if (labels != null) { |
|
for (EntityAnnotation label : labels) { |
|
message += String.format(Locale.US, "<b>%s</b>", label.getDescription()); |
|
message += "<br /><br /><big><b>Bottle</b></big><br /><i>/bot-l/</i><br />A portable container for holding liquids, characteristically having a neck and mouth and made of glass or plastic."; |
|
break; |
|
} |
|
} else { |
|
message += "nothing!"; |
|
} |
|
|
|
return message; |
|
} |
|
} |