I am currently developing my first app that able to switch activity tab (Android's Fragment). I have 4 tabs, in one of the tabs, I have implemented an image recognition for character recognition (OCR). I used simplified OCR Tesseract library which is called Easy OCR Library made by Priyank Verma. I have tested the example from that library and it works. But when I use the library for my own app, it doesn't work properly.
Let me explain, in one of my app's tab, I have a 'Scan' button that utilizes use my phone camera to take a picture. After the picture have been taken, my app should scan the captured image in the saved directory for character recognition. Next, my app will return to the previous 'Scan' tab and then will display the output of the character recognition above the 'Scan' button in that tab. And that just it, my app doesn't seem to scan. The real problem is that after the picture have been taken & saved, my app returns back to the tab with no output at all. My code should work but sadly it doesn't. No error, no nothing and I am losing my mind for days to figure out what is wrong.
Here's the code:-
---------- My App's Manifest ----------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kaydarinapp.queueappv2">
<!-- Save file permission -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Read file permission -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Camera permission -->
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- Internet permission -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity"
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
---------- From My App's Tab ----------
My Tab's XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".Tab2"
android:gravity="center_horizontal"
>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView"
android:layout_marginTop="45dp"
android:layout_gravity="center_horizontal"
android:textSize="8pt"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/button"
android:layout_marginTop="20dp"
android:text="Scan"
android:layout_gravity="center_vertical" />
</LinearLayout>
My Tab Java
package com.kaydarinapp.queueappv2;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.wordpress.priyankvex.easyocrscannerdemo.Config;
import com.wordpress.priyankvex.easyocrscannerdemo.EasyOcrScanner;
import com.wordpress.priyankvex.easyocrscannerdemo.EasyOcrScannerListener;
/**
* Created by Kaydarin on 6/1/2016.
*/
//Our class extending fragment
public class Tab2 extends Fragment implements EasyOcrScannerListener {
private LinearLayout linearLayout;
private FragmentActivity fragActivity;
EasyOcrScanner mEasyOcrScanner;
TextView textView;
ProgressDialog mProgressDialog;
Button btnCapture;
//Overriden method onCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
fragActivity = super.getActivity();
linearLayout = (LinearLayout) inflater.inflate(R.layout.tab2, container, false);
textView = (TextView) fragActivity.findViewById(R.id.textView);
// initialize EasyOcrScanner instance.
mEasyOcrScanner = new EasyOcrScanner(getActivity(), "EasyOcrScanner",
Config.REQUEST_CODE_CAPTURE_IMAGE, "eng");
// Set ocrScannerListener
mEasyOcrScanner.setOcrScannerListener(this);
btnCapture = (Button) linearLayout.findViewById(R.id.button);
btnCapture.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
mEasyOcrScanner.takePicture();
}
});
return linearLayout;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Call onImageTaken() in onActivityResult.
if (resultCode == getActivity().RESULT_OK && requestCode == Config.REQUEST_CODE_CAPTURE_IMAGE){
mEasyOcrScanner.onImageTaken();
}
}
/**
* Callback when after taking picture, scanning process starts.
* Good place to show a progress dialog.
* #param filePath file path of the image file being processed.
*/
#Override
public void onOcrScanStarted(String filePath) {
mProgressDialog = new ProgressDialog(getActivity());
mProgressDialog.setMessage("Scanning...");
mProgressDialog.show();
}
/**
* Callback when scanning is finished.
* Good place to hide teh progress dialog.
* #param bitmap Bitmap of image that was scanned.
* #param recognizedText Scanned text.
*/
#Override
public void onOcrScanFinished(Bitmap bitmap, String recognizedText) {
textView.setText(recognizedText);
if (mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
}
}
---------- From Easy OCR Library ----------
Easy OCR Library's Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wordpress.priyankvex.easyocrscanner">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA"/>
</manifest>
Config Java
package com.wordpress.priyankvex.easyocrscannerdemo;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*/
public class Config {
public static String TAG = "OcrScanner";
public static int REQUEST_CODE_CAPTURE_IMAGE = 1995;
}
EasyOCRScannerListener Java
package com.wordpress.priyankvex.easyocrscannerdemo;
import android.graphics.Bitmap;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*
* Interface for the callbacks for {#link EasyOcrScanner}.
*/
public interface EasyOcrScannerListener {
public void onOcrScanStarted(String filePath);
public void onOcrScanFinished(Bitmap bitmap, String recognizedText);
}
EasyOCRScanner Java
package com.wordpress.priyankvex.easyocrscannerdemo;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import java.io.File;
import java.util.Calendar;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*
* Class to handle scanning of image.
*/
public class EasyOcrScanner {
protected Activity mActivity;
private String directoryPathOriginal;
private String filePathOriginal;
private int requestCode;
private EasyOcrScannerListener mOcrScannerListener;
private String trainedDataCode;
public EasyOcrScanner(Activity activity, String directoryPath, int requestCode, String trainedDataCode){
this.mActivity = activity;
this.directoryPathOriginal = directoryPath;
this.requestCode = requestCode;
this.trainedDataCode = trainedDataCode;
}
public void takePicture(){
Intent e = new Intent("android.media.action.IMAGE_CAPTURE");
this.filePathOriginal = FileUtils.getDirectory(this.directoryPathOriginal) + File.separator + Calendar.getInstance().getTimeInMillis() + ".jpg";
e.putExtra("output", Uri.fromFile(new File(this.filePathOriginal)));
startActivity(e);
}
public void onImageTaken(){
Log.d(Config.TAG, "onImageTaken with path " + this.filePathOriginal);
ImageProcessingThread thread = new ImageProcessingThread(this.mOcrScannerListener,
this.filePathOriginal, this.directoryPathOriginal, this.mActivity, this.trainedDataCode);
thread.execute();
}
private void startActivity(Intent intent){
if(this.mActivity != null) {
this.mActivity.startActivityForResult(intent, this.requestCode);
}
}
public void setOcrScannerListener(EasyOcrScannerListener mOcrScannerListener) {
this.mOcrScannerListener = mOcrScannerListener;
}
}
FileUtils Java
package com.wordpress.priyankvex.easyocrscannerdemo;
import android.os.Environment;
import android.util.Log;
import java.io.File;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*/
public class FileUtils {
public static String getDirectory(String folderName) {
File directory = null;
directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + folderName);
if(!directory.exists()) {
directory.mkdirs();
}
return directory.getAbsolutePath();
}
public static String getTessdataDirectory(String directoryPath){
File tessdataDirectory = new File(directoryPath + "/tessdata");
if (tessdataDirectory.mkdirs()){
Log.d(Config.TAG, "tessdata directory created");
}
return tessdataDirectory.getAbsolutePath();
}
}
ImageProcessingThread Java
package com.wordpress.priyankvex.easyocrscannerdemo;
import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.AsyncTask;
import android.util.Log;
import com.googlecode.tesseract.android.TessBaseAPI;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*
* Async Task to process the image and scan the image using tesseract library.
* Equipped with proper callbacks.
*/
public class ImageProcessingThread extends AsyncTask<Void, Void, Void> {
private EasyOcrScannerListener mOcrScannerListener;
private String filePath;
private Bitmap mBitmap;
private String scannedText;
// trained data file used by Tesseract will be copied in directoryPath/tessdata
private String directoryPath;
private String absoluteDirectoryPath;
private Activity mActivity;
String trainedDataCode;
public ImageProcessingThread(EasyOcrScannerListener ocrScannerListener, String filePath,
String directoryPath, Activity activity, String trainedDataCode) {
this.mOcrScannerListener = ocrScannerListener;
this.filePath = filePath;
this.directoryPath = directoryPath;
this.absoluteDirectoryPath = FileUtils.getDirectory(this.directoryPath);
this.mActivity = activity;
this.trainedDataCode = trainedDataCode;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mOcrScannerListener.onOcrScanStarted(this.filePath);
}
#Override
protected Void doInBackground(Void... params) {
processImage();
makeTessdataReady();
scannedText = scanImage();
Log.d(Config.TAG, "Scanned test : " + scannedText);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
mOcrScannerListener.onOcrScanFinished(mBitmap, scannedText);
}
private void processImage() {
int imageOrientationCode = getImageOrientation();
Bitmap rawBitmap = getBitmapFromPath();
// Getting the bitmap in right orientation.
this.mBitmap = rotateBitmap(rawBitmap, imageOrientationCode);
}
private Bitmap getBitmapFromPath() {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(this.filePath, bmOptions);
return bitmap;
}
private int getImageOrientation() {
ExifInterface exif = null;
try {
exif = new ExifInterface(this.filePath);
} catch (IOException e) {
e.printStackTrace();
}
assert exif != null;
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
return orientation;
}
private Bitmap rotateBitmap(Bitmap bitmap, int orientation){
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
return bitmap;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
matrix.setScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.setRotate(180);
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.setRotate(90);
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.setRotate(-90);
break;
default:
return bitmap;
}
try {
Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
return bmRotated;
}
catch (OutOfMemoryError e) {
e.printStackTrace();
return null;
}
}
private String scanImage(){
TessBaseAPI baseApi = new TessBaseAPI();
Log.d(Config.TAG, "Data path : " + FileUtils.getDirectory(this.directoryPath));
baseApi.init(FileUtils.getDirectory(this.directoryPath) + "/", this.trainedDataCode);
baseApi.setImage(this.mBitmap);
String recognizedText = baseApi.getUTF8Text();
baseApi.end();
return recognizedText;
}
private void makeTessdataReady(){
// created test data directory if necessary under absoluteDirectoryPath and returns its absolute path.
String tessdirectoryPath = FileUtils.getTessdataDirectory(this.absoluteDirectoryPath);
if (!(new File(tessdirectoryPath+ "/" + this.trainedDataCode + ".traineddata")).exists()) {
try {
AssetManager assetManager = mActivity.getAssets();
InputStream in = assetManager.open("tessdata/" + this.trainedDataCode + ".traineddata");
//GZIPInputStream gin = new GZIPInputStream(in);
// Output stream with the location where we have to write the eng.traineddata file.
OutputStream out = new FileOutputStream(tessdirectoryPath + "/" + this.trainedDataCode
+ ".traineddata");
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
//while ((lenf = gin.read(buff)) > 0) {
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
//gin.close();
out.close();
Log.v(Config.TAG, "Copied " + " traineddata");
} catch (IOException e) {
Log.e(Config.TAG, "Was unable to copy " + " traineddata " + e.toString());
}
}
else{
Log.d(Config.TAG, "tessdata already present");
}
}
}
I hope you guys can help me. It's been days to figure out what's wrong with this code.......
I guess the problem is that the camera activity does not return RESULT_OK, but RESULT_CANCELED - therefore tesseract is never called. You can verify this e. g. by adding in "Tab2.java" at the start of method "onActivityResult":
Log.d("PictureEdit", "onActivityResult w/ resultCode=" + resultCode + " (is ok: " + (resultCode == RESULT_OK) + " is canceled: " + (resultCode == RESULT_CANCELED) + ")" + " requestCode=" + requestCode);
Why is that so? Because starting with Android 6 (Marshmallow) it is not suffice to just declare the necessary permissions in the manifest. Additionally the permissions must got at runtime from the user.
See here: https://developer.android.com/training/permissions/requesting.html
Here is the code I use for getting permissions to camera and r/w to external storage:
private void checkPermissions(){
if (Build.VERSION.SDK_INT >= 23) {
boolean needWriteExtStorage = checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED;
boolean needCamera = checkSelfPermission(android.Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED;
if (needWriteExtStorage || needCamera){
String[] permissions = new String[((needWriteExtStorage)?(1):(0)) + ((needCamera)?(1):(0))];
int idx = 0;
if (needWriteExtStorage){
permissions[idx] = android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
idx++;
}
if (needCamera){
permissions[idx] = android.Manifest.permission.CAMERA;
idx++;
}
ActivityCompat.requestPermissions(
this, permissions, 1);
}
} else {
//permission is automatically granted on sdk<23 upon installation
//Log.e("testing", "Permission is already granted");
}
}
Related
This is my first Android App project and I am trying to apply OCR function in my Android App by using Easy OCR library made by Priyank Verma's Easy OCR Library as it is the most simplified & most recent OCR library I can get on. But I think the creator of that library wont answer people's query in his own blog, so I try to ask here. If this is not a good question, I will delete it.
So here's the problem, I have an app with four tabs and in one of the tabs, I tried to use the library example which I try to apply them in my tab. In that tab, there's a 'Scan' button that will take a picture, save it and then scan it. After that, it should return output of text characters from the scanning process. And the execution of the app successfully return with zero error.
But the problem here is, it failed to save the picture by which it will lead the failure of the scanning process thus returning nothing. I can't see any problem here, maybe someone with sharp eyes can detect them?
||||| UPDATE 1 |||||
I have set breakpoints as suggested by Ishita Sinha and I have found out the file did save, it just saved on different directory which is on my internal storage (/storage/emulated/0/EasyOcrScanner or internal storage/EasyOcrScanner). The real conflict occur here is when after the image have been saved, which is scanning of the image. Still, I can't figure out why is that. I have marked '%%' in the FileUtils Java and EasyOCRScanner Java to show what I have found. I need some assist on how to successfully scan after the image has been saved.
||||||||||||||||||||||||||||||
||||| UPDATE 2 |||||
I have pushed my project on GitHub as requested by Anand Savjani. You guys can check there. The project name is 'Queue-App'.
https://github.com/Kaydarin/Queue-App
||||||||||||||||||||||||||||||
---------- From My App's Tab ----------
My App's Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kaydarinapp.queueappv2">
<!-- Save file permission -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Read file permission -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Camera permission -->
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- Internet permission -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity"
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
My Tab's XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".Tab2"
android:gravity="center_horizontal"
>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView"
android:layout_marginTop="45dp"
android:layout_gravity="center_horizontal"
android:textSize="8pt"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/button"
android:layout_marginTop="20dp"
android:text="Scan"
android:layout_gravity="center_vertical" />
</LinearLayout>
My Tab Java
package com.kaydarinapp.queueappv2;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.wordpress.priyankvex.easyocrscannerdemo.Config;
import com.wordpress.priyankvex.easyocrscannerdemo.EasyOcrScanner;
import com.wordpress.priyankvex.easyocrscannerdemo.EasyOcrScannerListener;
/**
* Created by Kaydarin on 6/1/2016.
*/
//Our class extending fragment
public class Tab2 extends Fragment implements EasyOcrScannerListener {
private LinearLayout linearLayout;
private FragmentActivity fragActivity;
EasyOcrScanner mEasyOcrScanner;
TextView textView;
ProgressDialog mProgressDialog;
Button btnCapture;
//Overriden method onCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
fragActivity = super.getActivity();
linearLayout = (LinearLayout) inflater.inflate(R.layout.tab2, container, false);
textView = (TextView) fragActivity.findViewById(R.id.textView);
// initialize EasyOcrScanner instance.
mEasyOcrScanner = new EasyOcrScanner(getActivity(), "EasyOcrScanner",
Config.REQUEST_CODE_CAPTURE_IMAGE, "eng");
// Set ocrScannerListener
mEasyOcrScanner.setOcrScannerListener(this);
btnCapture = (Button) linearLayout.findViewById(R.id.button);
btnCapture.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
mEasyOcrScanner.takePicture();
}
});
return linearLayout;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Call onImageTaken() in onActivityResult.
if (resultCode == getActivity().RESULT_OK && requestCode == Config.REQUEST_CODE_CAPTURE_IMAGE){
mEasyOcrScanner.onImageTaken();
}
}
/**
* Callback when after taking picture, scanning process starts.
* Good place to show a progress dialog.
* #param filePath file path of the image file being processed.
*/
#Override
public void onOcrScanStarted(String filePath) {
mProgressDialog = new ProgressDialog(getActivity());
mProgressDialog.setMessage("Scanning...");
mProgressDialog.show();
}
/**
* Callback when scanning is finished.
* Good place to hide teh progress dialog.
* #param bitmap Bitmap of image that was scanned.
* #param recognizedText Scanned text.
*/
#Override
public void onOcrScanFinished(Bitmap bitmap, String recognizedText) {
textView.setText(recognizedText);
if (mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
}
}
---------- From Easy OCR Library ----------
Easy OCR Library's Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wordpress.priyankvex.easyocrscanner">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA"/>
</manifest>
Config Java
package com.wordpress.priyankvex.easyocrscannerdemo;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*/
public class Config {
public static String TAG = "OcrScanner";
public static int REQUEST_CODE_CAPTURE_IMAGE = 1995;
}
EasyOCRScannerListener Java
package com.wordpress.priyankvex.easyocrscannerdemo;
import android.graphics.Bitmap;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*
* Interface for the callbacks for {#link EasyOcrScanner}.
*/
public interface EasyOcrScannerListener {
public void onOcrScanStarted(String filePath);
public void onOcrScanFinished(Bitmap bitmap, String recognizedText);
}
EasyOCRScanner Java
package com.wordpress.priyankvex.easyocrscannerdemo;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import java.io.File;
import java.util.Calendar;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*
* Class to handle scanning of image.
*/
public class EasyOcrScanner {
protected Activity mActivity;
private String directoryPathOriginal;
private String filePathOriginal;
private int requestCode;
private EasyOcrScannerListener mOcrScannerListener;
private String trainedDataCode;
public EasyOcrScanner(Activity activity, String directoryPath, int requestCode, String trainedDataCode){
this.mActivity = activity;
this.directoryPathOriginal = directoryPath;
this.requestCode = requestCode;
this.trainedDataCode = trainedDataCode;
}
public void takePicture(){
Intent e = new Intent("android.media.action.IMAGE_CAPTURE");
this.filePathOriginal = FileUtils.getDirectory(this.directoryPathOriginal) + File.separator + Calendar.getInstance().getTimeInMillis() + ".jpg";
e.putExtra("output", Uri.fromFile(new File(this.filePathOriginal)));
startActivity(e);
} // %%%%%%%%%%%% POSITIVE BREAKPOINT: AFTER THIS POINT, THE APP DOES NOT APPEAR TO SCAN THE CAPTURED IMAGE AS IT RETURN BACK TO THE APP WITHOUT ANY SCANNED TEXT OUTPUT %%%%%%%%%%%%
public void onImageTaken(){
Log.d(Config.TAG, "onImageTaken with path " + this.filePathOriginal);
ImageProcessingThread thread = new ImageProcessingThread(this.mOcrScannerListener,
this.filePathOriginal, this.directoryPathOriginal, this.mActivity, this.trainedDataCode);
thread.execute();
}
private void startActivity(Intent intent){
if(this.mActivity != null) {
this.mActivity.startActivityForResult(intent, this.requestCode);
}
}
public void setOcrScannerListener(EasyOcrScannerListener mOcrScannerListener) {
this.mOcrScannerListener = mOcrScannerListener;
}
}
FileUtils Java
package com.wordpress.priyankvex.easyocrscannerdemo;
import android.os.Environment;
import android.util.Log;
import java.io.File;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*/
public class FileUtils {
public static String getDirectory(String folderName) {
File directory = null;
directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + folderName);
if(!directory.exists()) {
directory.mkdirs();
}
return directory.getAbsolutePath(); // %%%%%%%%%%%% POSITIVE BREAKPOINT: IT SHOWS THAT THE FILE DIRECTORY SAVED IN INTERNAL STORAGE %%%%%%%%%%%%
}
public static String getTessdataDirectory(String directoryPath){
File tessdataDirectory = new File(directoryPath + "/tessdata");
if (tessdataDirectory.mkdirs()){
Log.d(Config.TAG, "tessdata directory created");
}
return tessdataDirectory.getAbsolutePath();
}
}
ImageProcessingThread Java
package com.wordpress.priyankvex.easyocrscannerdemo;
import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.AsyncTask;
import android.util.Log;
import com.googlecode.tesseract.android.TessBaseAPI;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by Priyank(#priyankvex) on 27/8/15.
*
* Async Task to process the image and scan the image using tesseract library.
* Equipped with proper callbacks.
*/
public class ImageProcessingThread extends AsyncTask<Void, Void, Void> {
private EasyOcrScannerListener mOcrScannerListener;
private String filePath;
private Bitmap mBitmap;
private String scannedText;
// trained data file used by Tesseract will be copied in directoryPath/tessdata
private String directoryPath;
private String absoluteDirectoryPath;
private Activity mActivity;
String trainedDataCode;
public ImageProcessingThread(EasyOcrScannerListener ocrScannerListener, String filePath,
String directoryPath, Activity activity, String trainedDataCode) {
this.mOcrScannerListener = ocrScannerListener;
this.filePath = filePath;
this.directoryPath = directoryPath;
this.absoluteDirectoryPath = FileUtils.getDirectory(this.directoryPath);
this.mActivity = activity;
this.trainedDataCode = trainedDataCode;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mOcrScannerListener.onOcrScanStarted(this.filePath);
}
#Override
protected Void doInBackground(Void... params) {
processImage();
makeTessdataReady();
scannedText = scanImage();
Log.d(Config.TAG, "Scanned test : " + scannedText);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
mOcrScannerListener.onOcrScanFinished(mBitmap, scannedText);
}
private void processImage() {
int imageOrientationCode = getImageOrientation();
Bitmap rawBitmap = getBitmapFromPath();
// Getting the bitmap in right orientation.
this.mBitmap = rotateBitmap(rawBitmap, imageOrientationCode);
}
private Bitmap getBitmapFromPath() {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(this.filePath, bmOptions);
return bitmap;
}
private int getImageOrientation() {
ExifInterface exif = null;
try {
exif = new ExifInterface(this.filePath);
} catch (IOException e) {
e.printStackTrace();
}
assert exif != null;
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
return orientation;
}
private Bitmap rotateBitmap(Bitmap bitmap, int orientation){
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
return bitmap;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
matrix.setScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.setRotate(180);
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.setRotate(90);
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.setRotate(-90);
break;
default:
return bitmap;
}
try {
Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
return bmRotated;
}
catch (OutOfMemoryError e) {
e.printStackTrace();
return null;
}
}
private String scanImage(){
TessBaseAPI baseApi = new TessBaseAPI();
Log.d(Config.TAG, "Data path : " + FileUtils.getDirectory(this.directoryPath));
baseApi.init(FileUtils.getDirectory(this.directoryPath) + "/", this.trainedDataCode);
baseApi.setImage(this.mBitmap);
String recognizedText = baseApi.getUTF8Text();
baseApi.end();
return recognizedText;
}
private void makeTessdataReady(){
// created tessdata directory if necessary under absoluteDirectoryPath and returns its absolute path.
String tessdirectoryPath = FileUtils.getTessdataDirectory(this.absoluteDirectoryPath);
if (!(new File(tessdirectoryPath+ "/" + this.trainedDataCode + ".traineddata")).exists()) {
try {
AssetManager assetManager = mActivity.getAssets();
InputStream in = assetManager.open("tessdata/" + this.trainedDataCode + ".traineddata");
//GZIPInputStream gin = new GZIPInputStream(in);
// Output stream with the location where we have to write the eng.traineddata file.
OutputStream out = new FileOutputStream(tessdirectoryPath + "/" + this.trainedDataCode
+ ".traineddata");
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
//while ((lenf = gin.read(buff)) > 0) {
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
//gin.close();
out.close();
Log.v(Config.TAG, "Copied " + " traineddata");
} catch (IOException e) {
Log.e(Config.TAG, "Was unable to copy " + " traineddata " + e.toString());
}
}
else{
Log.d(Config.TAG, "tessdata already present");
}
}
}
Hope someone can me help here. Really appreciate it.....
I am currently developing an Android App, which integrates Google Calendar in it. As we know if we wanted to choose an activity to be the starting activity, we just add the "intent-filter" thingy. I added at my Landing Page activity but when I debug on Emulator, it will auto add the "intent-filter" at my Google Calendar activity part and comment out the one I added at my Landing Page.
This is the code at AndroidManifest.xml
<activity
android:name="com.example.dylicious.mydoctors.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="com.example.dylicious.mydoctors.ViewDocActivity"
android:label="#string/title_activity_view_doc" >
</activity>
<activity
android:name="com.example.dylicious.mydoctors.ViewDoctorProf"
android:label="#string/title_activity_view_doctor_prof" >
</activity>
<activity
android:name="com.example.dylicious.mydoctors.StorePatientProfile"
android:label="#string/title_activity_store_patient_profile" >
</activity>
<activity
android:name="com.example.dylicious.mydoctors.ViewPatientProfile"
android:label="#string/title_activity_view_patient_profile" >
</activity>
<activity
android:name="com.example.dylicious.mydoctors.AppointmentActivity"
android:label="#string/title_activity_appointment" >
<!--<intent-filter>-->
<!--<action android:name="android.intent.action.MAIN" />-->
<!--<category android:name="android.intent.category.LAUNCHER" />-->
<!--</intent-filter>-->
</activity>
This is the one I edited, AppointmentActivity is the Google Calendar part. I kept commenting out and delete the part in AppointmentActivity and clean solution and etc. but it doesn't work anything. Just want to check if I had miss out anything as I'm just a newbie in the Android App Development field. Thanks a bunch in advance!
While this is the class for AppointmentActivity.java
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
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.client.util.ExponentialBackOff;
import com.google.api.services.calendar.CalendarScopes;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.Arrays;
import java.util.List;
public class AppointmentActivity extends Activity {
com.google.api.services.calendar.Calendar mService;
GoogleAccountCredential credential;
private TextView mStatusText;
private TextView mResultsText;
ProgressDialog mProgress;
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = { CalendarScopes.CALENDAR_READONLY };
/**
* Create the main activity.
* #param savedInstanceState previously saved instance data.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout activityLayout = new LinearLayout(this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
activityLayout.setLayoutParams(lp);
activityLayout.setOrientation(LinearLayout.VERTICAL);
activityLayout.setPadding(16, 16, 16, 16);
ViewGroup.LayoutParams tlp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
mStatusText = new TextView(this);
mStatusText.setLayoutParams(tlp);
mStatusText.setTypeface(null, Typeface.BOLD);
mStatusText.setText("Retrieving data...");
activityLayout.addView(mStatusText);
mResultsText = new TextView(this);
mResultsText.setLayoutParams(tlp);
mResultsText.setPadding(16, 16, 16, 16);
mResultsText.setVerticalScrollBarEnabled(true);
mResultsText.setMovementMethod(new ScrollingMovementMethod());
activityLayout.addView(mResultsText);
mProgress = new ProgressDialog(this);
mProgress.setMessage("Calling Google Calendar API ...");
setContentView(activityLayout);
// Initialize credentials and service object.
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
credential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(SCOPES))
.setBackOff(new ExponentialBackOff())
.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
mService = new com.google.api.services.calendar.Calendar.Builder(
transport, jsonFactory, credential)
.setApplicationName("Google Calendar API Android Quickstart")
.build();
}
/**
* Called whenever this activity is pushed to the foreground, such as after
* a call to onCreate().
*/
#Override
protected void onResume() {
super.onResume();
if (isGooglePlayServicesAvailable()) {
refreshResults();
} else {
mStatusText.setText("Google Play Services required: " +
"after installing, close and relaunch this app.");
}
}
/**
* Called when an activity launched here (specifically, AccountPicker
* and authorization) exits, giving you the requestCode you started it with,
* the resultCode it returned, and any additional data from it.
* #param requestCode code indicating which activity result is incoming.
* #param resultCode code indicating the result of the incoming
* activity result.
* #param data Intent (containing result data) returned by incoming
* activity result.
*/
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode != RESULT_OK) {
isGooglePlayServicesAvailable();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null &&
data.getExtras() != null) {
String accountName =
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
credential.setSelectedAccountName(accountName);
SharedPreferences settings =
getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.commit();
}
} else if (resultCode == RESULT_CANCELED) {
mStatusText.setText("Account unspecified.");
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode != RESULT_OK) {
chooseAccount();
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* Attempt to get a set of data from the Google Calendar API to display. If the
* email address isn't known yet, then call chooseAccount() method so the
* user can pick an account.
*/
private void refreshResults() {
if (credential.getSelectedAccountName() == null) {
chooseAccount();
} else {
if (isDeviceOnline()) {
mProgress.show();
new ApiAsyncTask(this).execute();
} else {
mStatusText.setText("No network connection available.");
}
}
}
/**
* Clear any existing Google Calendar API data from the TextView and update
* the header message; called from background threads and async tasks
* that need to update the UI (in the UI thread).
*/
public void clearResultsText() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mStatusText.setText("Retrieving data…");
mResultsText.setText("");
}
});
}
/**
* Fill the data TextView with the given List of Strings; called from
* background threads and async tasks that need to update the UI (in the
* UI thread).
* #param dataStrings a List of Strings to populate the main TextView with.
*/
public void updateResultsText(final List<String> dataStrings) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (dataStrings == null) {
mStatusText.setText("Error retrieving data!");
} else if (dataStrings.size() == 0) {
mStatusText.setText("No data found.");
} else {
mStatusText.setText("Data retrieved using" +
" the Google Calendar API:");
mResultsText.setText(TextUtils.join("\n\n", dataStrings));
}
}
});
}
/**
* Show a status message in the list header TextView; called from background
* threads and async tasks that need to update the UI (in the UI thread).
* #param message a String to display in the UI header TextView.
*/
public void updateStatus(final String message) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mStatusText.setText(message);
}
});
}
/**
* Starts an activity in Google Play Services so the user can pick an
* account.
*/
private void chooseAccount() {
startActivityForResult(
credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
/**
* Checks whether the device currently has a network connection.
* #return true if the device has a network connection, false otherwise.
*/
private boolean isDeviceOnline() {
ConnectivityManager connMgr =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
/**
* Check that Google Play services APK is installed and up to date. Will
* launch an error dialog for the user to update Google Play Services if
* possible.
* #return true if Google Play Services is available and up to
* date on this device; false otherwise.
*/
private boolean isGooglePlayServicesAvailable() {
final int connectionStatusCode =
GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (GooglePlayServicesUtil.isUserRecoverableError(connectionStatusCode)) {
showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
return false;
} else if (connectionStatusCode != ConnectionResult.SUCCESS ) {
return false;
}
return true;
}
/**
* Display an error dialog showing that Google Play Services is missing
* or out of date.
* #param connectionStatusCode code describing the presence (or lack of)
* Google Play Services on this device.
*/
void showGooglePlayServicesAvailabilityErrorDialog(
final int connectionStatusCode) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
connectionStatusCode,
AppointmentActivity.this,
REQUEST_GOOGLE_PLAY_SERVICES);
dialog.show();
}
});
}
}
Thanks!!
omit the intent-filter in your AppointmentActivity AndroidManifest file declaration. since calling action android:name="android.intent.action.MAIN" which defines the main entry point in your application and android:name="android.intent.category.LAUNCHER" means that the entry point should be listed in the application launcher, so you see there can't be 2 main entry points in the application.
I made a simple app that reads images and retrieves the number image as text with android. But the problem is that the accuracy is only about 60% and some unwanted noise also shows as well. I do perceive that the accuracy cannot be good as 100%,however, I believe that there must be a way to improve it. But, since I'm an amateur, I find it difficult. I've searched around google but was unable to gain a solid information.
I want to read the numbers 596 , 00 , and 012345 from a oriental lucky tickets like the image below.
Tesseract-ocr works best on images of characters which meet the following criteria:
The input image should have atleast 300 dpi
The input image should be black and white
There should be minimal noise in the input image (i.e. the text should be clearly distinguishable from the background)
Text lines should be straight
The image should be centered around the text to be detected
(See the tesseract-ocr wiki for further details)
For a given input image, tesseract will try to pre-process and clean the image to meet these criteria, but to maximise your detection accuracy, it is best to do the pre-processing yourself.
Based on the input image you provided, the main problem is that there is too much background noise. To remove the background noise from the text in the image, I have found that applying the Stroke Width Transform (SWT) algorithm with a threshold value to remove noise gives promising results. A fast implementation of SWT with many configurable parameters is provided in the libCCV library. How well it cleans the image depends on a number of factors including image size, uniformity of stroke width and other input parameters to the algorithm. A list of the configurable parameters is provided here.
You then pass the output of SWT to tesseract to obtain the text values of characters in the image.
If the image passed to tesseract still contains some noise, it may return some false detections such as punctuation characters. Given that the image you are processing is likely to only contain letters and numbers a-z A-Z 0-9, you can simply apply a regex to the output to remove any final false detections.
you can use Vision for text detection.
Add dependency in app gradle
compile 'com.google.android.gms:play-services-vision:10.0.0'
Add in Manifest.xml
<meta-data
android:name="com.google.android.gms.vision.DEPENDENCIES"
android:value="ocr" />
MainActivity.java
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.TextView;
import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.text.TextBlock;
import com.google.android.gms.vision.text.TextRecognizer;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_GALLERY = 0;
private static final int REQUEST_CAMERA = 1;
private static final String TAG = MainActivity.class.getSimpleName();
private Uri imageUri;
private TextView detectedTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.choose_from_gallery).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, REQUEST_GALLERY);
}
});
findViewById(R.id.take_a_photo).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String filename = System.currentTimeMillis() + ".jpg";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, filename);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, REQUEST_CAMERA);
}
});
detectedTextView = (TextView) findViewById(R.id.detected_text);
detectedTextView.setMovementMethod(new ScrollingMovementMethod());
}
private void inspectFromBitmap(Bitmap bitmap) {
TextRecognizer textRecognizer = new TextRecognizer.Builder(this).build();
try {
if (!textRecognizer.isOperational()) {
new AlertDialog.
Builder(this).
setMessage("Text recognizer could not be set up on your device").show();
return;
}
Frame frame = new Frame.Builder().setBitmap(bitmap).build();
SparseArray<TextBlock> origTextBlocks = textRecognizer.detect(frame);
List<TextBlock> textBlocks = new ArrayList<>();
for (int i = 0; i < origTextBlocks.size(); i++) {
TextBlock textBlock = origTextBlocks.valueAt(i);
textBlocks.add(textBlock);
}
Collections.sort(textBlocks, new Comparator<TextBlock>() {
#Override
public int compare(TextBlock o1, TextBlock o2) {
int diffOfTops = o1.getBoundingBox().top - o2.getBoundingBox().top;
int diffOfLefts = o1.getBoundingBox().left - o2.getBoundingBox().left;
if (diffOfTops != 0) {
return diffOfTops;
}
return diffOfLefts;
}
});
StringBuilder detectedText = new StringBuilder();
for (TextBlock textBlock : textBlocks) {
if (textBlock != null && textBlock.getValue() != null) {
detectedText.append(textBlock.getValue());
detectedText.append("\n");
}
}
detectedTextView.setText(detectedText);
}
finally {
textRecognizer.release();
}
}
private void inspect(Uri uri) {
InputStream is = null;
Bitmap bitmap = null;
try {
is = getContentResolver().openInputStream(uri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inSampleSize = 2;
options.inScreenDensity = DisplayMetrics.DENSITY_LOW;
bitmap = BitmapFactory.decodeStream(is, null, options);
inspectFromBitmap(bitmap);
} catch (FileNotFoundException e) {
Log.w(TAG, "Failed to find the file: " + uri, e);
} finally {
if (bitmap != null) {
bitmap.recycle();
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
Log.w(TAG, "Failed to close InputStream", e);
}
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_GALLERY:
if (resultCode == RESULT_OK) {
inspect(data.getData());
}
break;
case REQUEST_CAMERA:
if (resultCode == RESULT_OK) {
if (imageUri != null) {
inspect(imageUri);
}
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="org.komamitsu.android_ocrsample.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/choose_from_gallery"
android:id="#+id/choose_from_gallery"
tools:context=".MainActivity"
android:layout_marginTop="23dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/take_a_photo"
android:id="#+id/take_a_photo"
tools:context=".MainActivity"
android:layout_marginTop="11dp"
android:layout_below="#+id/choose_from_gallery"
android:layout_centerHorizontal="true" />
<TextView
android:text=""
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/detected_text"
android:layout_alignParentBottom="true"
android:layout_below="#+id/take_a_photo"
android:layout_margin="25dp"
android:layout_centerHorizontal="true"
android:background="#EEEEEE"
android:scrollbars="vertical" />
</RelativeLayout>
I'm trying to make an app in Android Studio which requires you to download a file to the sdcard/Download/ folder on the users device. The problem is that when I enter the URL to download from and click Download, the app says "Unfortunately, * has stopped". I have quite a few files so here they are.
First, heres the Manifest...
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.NautGames.xecta.app" >
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.NautGames.xecta.app.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- Permission: Writing to SDCard -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
</manifest>
Heres the MainActivity class...
package com.NautGames.xecta.app;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
//Chat bot library
import org.alicebot.ab.Chat;
import org.alicebot.ab.Bot;
import android.view.View;
import android.widget.Button;
import android.os.Environment;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
TextView input;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//EditText mEdit = (EditText)findViewById(R.id.editText1);
public void buttonOnClick(View v)
{
input = (TextView) findViewById(R.id.editText1);
String dbPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/Ab";
Button button=(Button) v;
//Creating bot
String botname="xecta";
String path= dbPath;
Bot xecta = new Bot(botname, path);
Chat chatSession = new Chat(xecta);
String request = input.getText().toString();
String response = chatSession.multisentenceRespond(request);
((Button) v).setText(response);
}
public void buttonDownload(View v)
{
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
DownloaderThread (to download file)...
package com.NautGames.xecta.app;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import android.os.Environment;
import android.os.Message;
/**
* Downloads a file in a thread. Will send messages to the
* AndroidFileDownloader activity to update the progress bar.
*/
public class DownloaderThread extends Thread
{
// constants
private static final int DOWNLOAD_BUFFER_SIZE = 4096;
// instance variables
private AndroidFileDownloader parentActivity;
private String downloadUrl;
/**
//Instantiates a new DownloaderThread object.
// #param parentActivity Reference to AndroidFileDownloader activity.
// #param inUrl String representing the URL of the file to be downloaded.
*/
public DownloaderThread(AndroidFileDownloader inParentActivity, String inUrl)
{
downloadUrl = "";
if(inUrl != null)
{
downloadUrl = inUrl;
}
parentActivity = inParentActivity;
}
/**
* Connects to the URL of the file, begins the download, and notifies the
* AndroidFileDownloader activity of changes in state. Writes the file to
* the root of the SD card.
*/
#Override
public void run()
{
URL url;
URLConnection conn;
int fileSize, lastSlash;
String fileName;
BufferedInputStream inStream;
BufferedOutputStream outStream;
File outFile;
FileOutputStream fileStream;
Message msg;
// we're going to connect now
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_CONNECTING_STARTED,
0, 0, downloadUrl);
parentActivity.activityHandler.sendMessage(msg);
try
{
url = new URL(downloadUrl);
conn = url.openConnection();
conn.setUseCaches(false);
fileSize = conn.getContentLength();
// get the filename
lastSlash = url.toString().lastIndexOf('/');
fileName = "file.bin";
if(lastSlash >=0)
{
fileName = url.toString().substring(lastSlash + 1);
}
if(fileName.equals(""))
{
fileName = "file.bin";
}
// notify download start
int fileSizeInKB = fileSize / 1024;
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_DOWNLOAD_STARTED,
fileSizeInKB, 0, fileName);
parentActivity.activityHandler.sendMessage(msg);
// start download
inStream = new BufferedInputStream(conn.getInputStream());
outFile = new File(Environment.getExternalStorageDirectory() + "/" + fileName);
fileStream = new FileOutputStream(outFile);
outStream = new BufferedOutputStream(fileStream, DOWNLOAD_BUFFER_SIZE);
byte[] data = new byte[DOWNLOAD_BUFFER_SIZE];
int bytesRead = 0, totalRead = 0;
while(!isInterrupted() && (bytesRead = inStream.read(data, 0, data.length)) >= 0)
{
outStream.write(data, 0, bytesRead);
// update progress bar
totalRead += bytesRead;
int totalReadInKB = totalRead / 1024;
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_UPDATE_PROGRESS_BAR,
totalReadInKB, 0);
parentActivity.activityHandler.sendMessage(msg);
}
outStream.close();
fileStream.close();
inStream.close();
if(isInterrupted())
{
// the download was canceled, so let's delete the partially downloaded file
outFile.delete();
}
else
{
// notify completion
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_DOWNLOAD_COMPLETE);
parentActivity.activityHandler.sendMessage(msg);
}
}
catch(MalformedURLException e)
{
String errMsg = parentActivity.getString(R.string.error_message_bad_url);
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_ENCOUNTERED_ERROR,
0, 0, errMsg);
parentActivity.activityHandler.sendMessage(msg);
}
catch(FileNotFoundException e)
{
String errMsg = parentActivity.getString(R.string.error_message_file_not_found);
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_ENCOUNTERED_ERROR,
0, 0, errMsg);
parentActivity.activityHandler.sendMessage(msg);
}
catch(Exception e)
{
String errMsg = parentActivity.getString(R.string.error_message_general);
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_ENCOUNTERED_ERROR,
0, 0, errMsg);
parentActivity.activityHandler.sendMessage(msg);
}
}
}
And lastly, The AndroidFileDownload(also to download file)...
package com.NautGames.xecta.app;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class AndroidFileDownloader extends Activity implements OnClickListener
{
// Used to communicate state changes in the DownloaderThread
public static final int MESSAGE_DOWNLOAD_STARTED = 1000;
public static final int MESSAGE_DOWNLOAD_COMPLETE = 1001;
public static final int MESSAGE_UPDATE_PROGRESS_BAR = 1002;
public static final int MESSAGE_DOWNLOAD_CANCELED = 1003;
public static final int MESSAGE_CONNECTING_STARTED = 1004;
public static final int MESSAGE_ENCOUNTERED_ERROR = 1005;
// instance variables
private AndroidFileDownloader thisActivity;
private Thread downloaderThread;
private ProgressDialog progressDialog;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
thisActivity = this;
downloaderThread = null;
progressDialog = null;
setContentView(R.layout.activity_main);
Button button = (Button) this.findViewById(R.id.download_button);
button.setOnClickListener(this);
}
/** Called when the user clicks on something. */
#Override
public void onClick(View view)
{
EditText urlInputField = (EditText) this.findViewById(R.id.url_input);
String urlInput = urlInputField.getText().toString();
downloaderThread = new DownloaderThread(thisActivity, urlInput);
downloaderThread.start();
}
/**
* This is the Handler for this activity. It will receive messages from the
* DownloaderThread and make the necessary updates to the UI.
*/
public Handler activityHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch(msg.what)
{
/*
* Handling MESSAGE_UPDATE_PROGRESS_BAR:
* 1. Get the current progress, as indicated in the arg1 field
* of the Message.
* 2. Update the progress bar.
*/
case MESSAGE_UPDATE_PROGRESS_BAR:
if(progressDialog != null)
{
int currentProgress = msg.arg1;
progressDialog.setProgress(currentProgress);
}
break;
/*
* Handling MESSAGE_CONNECTING_STARTED:
* 1. Get the URL of the file being downloaded. This is stored
* in the obj field of the Message.
* 2. Create an indeterminate progress bar.
* 3. Set the message that should be sent if user cancels.
* 4. Show the progress bar.
*/
case MESSAGE_CONNECTING_STARTED:
if(msg.obj != null && msg.obj instanceof String)
{
String url = (String) msg.obj;
// truncate the url
if(url.length() > 16)
{
String tUrl = url.substring(0, 15);
tUrl += "...";
url = tUrl;
}
String pdTitle = thisActivity.getString(R.string.progress_dialog_title_connecting);
String pdMsg = thisActivity.getString(R.string.progress_dialog_message_prefix_connecting);
pdMsg += " " + url;
dismissCurrentProgressDialog();
progressDialog = new ProgressDialog(thisActivity);
progressDialog.setTitle(pdTitle);
progressDialog.setMessage(pdMsg);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setIndeterminate(true);
// set the message to be sent when this dialog is canceled
Message newMsg = Message.obtain(this, MESSAGE_DOWNLOAD_CANCELED);
progressDialog.setCancelMessage(newMsg);
progressDialog.show();
}
break;
/*
* Handling MESSAGE_DOWNLOAD_STARTED:
* 1. Create a progress bar with specified max value and current
* value 0; assign it to progressDialog. The arg1 field will
* contain the max value.
* 2. Set the title and text for the progress bar. The obj
* field of the Message will contain a String that
* represents the name of the file being downloaded.
* 3. Set the message that should be sent if dialog is canceled.
* 4. Make the progress bar visible.
*/
case MESSAGE_DOWNLOAD_STARTED:
// obj will contain a String representing the file name
if(msg.obj != null && msg.obj instanceof String)
{
int maxValue = msg.arg1;
String fileName = (String) msg.obj;
String pdTitle = thisActivity.getString(R.string.progress_dialog_title_downloading);
String pdMsg = thisActivity.getString(R.string.progress_dialog_message_prefix_downloading);
pdMsg += " " + fileName;
dismissCurrentProgressDialog();
progressDialog = new ProgressDialog(thisActivity);
progressDialog.setTitle(pdTitle);
progressDialog.setMessage(pdMsg);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setProgress(0);
progressDialog.setMax(maxValue);
// set the message to be sent when this dialog is canceled
Message newMsg = Message.obtain(this, MESSAGE_DOWNLOAD_CANCELED);
progressDialog.setCancelMessage(newMsg);
progressDialog.setCancelable(true);
progressDialog.show();
}
break;
/*
* Handling MESSAGE_DOWNLOAD_COMPLETE:
* 1. Remove the progress bar from the screen.
* 2. Display Toast that says download is complete.
*/
case MESSAGE_DOWNLOAD_COMPLETE:
dismissCurrentProgressDialog();
displayMessage(getString(R.string.user_message_download_complete));
break;
/*
* Handling MESSAGE_DOWNLOAD_CANCELLED:
* 1. Interrupt the downloader thread.
* 2. Remove the progress bar from the screen.
* 3. Display Toast that says download is complete.
*/
case MESSAGE_DOWNLOAD_CANCELED:
if(downloaderThread != null)
{
downloaderThread.interrupt();
}
dismissCurrentProgressDialog();
displayMessage(getString(R.string.user_message_download_canceled));
break;
/*
* Handling MESSAGE_ENCOUNTERED_ERROR:
* 1. Check the obj field of the message for the actual error
* message that will be displayed to the user.
* 2. Remove any progress bars from the screen.
* 3. Display a Toast with the error message.
*/
case MESSAGE_ENCOUNTERED_ERROR:
// obj will contain a string representing the error message
if(msg.obj != null && msg.obj instanceof String)
{
String errorMessage = (String) msg.obj;
dismissCurrentProgressDialog();
displayMessage(errorMessage);
}
break;
default:
// nothing to do here
break;
}
}
};
/**
* If there is a progress dialog, dismiss it and set progressDialog to
* null.
*/
public void dismissCurrentProgressDialog()
{
if(progressDialog != null)
{
progressDialog.hide();
progressDialog.dismiss();
progressDialog = null;
}
}
/**
* Displays a message to the user, in the form of a Toast.
* #param message Message to be displayed.
*/
public void displayMessage(String message)
{
if(message != null)
{
Toast.makeText(thisActivity, message, Toast.LENGTH_SHORT).show();
}
}
}
And heres the Log:
Process: com.NautGames.xecta.app, PID: 3032
java.lang.IllegalStateException: Could not find a method onClick(View) in the activity class com.NautGames.xecta.app.MainActivity for onClick handler on view class android.widget.Button with id 'download_button'
at android.view.View$1.onClick(View.java:3810)
at android.view.View.performClick(View.java:4438)
at android.view.View$PerformClick.run(View.java:18422)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.om.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NoSuchMethodException: onClick [class android.view.View]
at java.lang.Class.getConstructorOrMethod(Class.java:472)
at java.lang.Class.getMethod(Class.java:857)
at android.view.View$1.onClick(View.java:3803)
at android.view.View.performClick(View.java:4438)
at android.view.View$PerformClick.run(View.java:18422)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Could anyone tell me what the problem is?
Thanks in advance.
Since you said it's in AndroidFileDownload, I'd suggest you to do as follows:
#Override
public void onClick(View view)
// you need to get the id of the clicked view:
if(view.getId() == R.id.download_button) {
EditText urlInputField = (EditText) thisActivity.findViewById(R.id.url_input);
String urlInput = urlInputField.getText().toString();
downloaderThread = new DownloaderThread(thisActivity, urlInput);
downloaderThread.start();
}
}
This is the cause of: NoSuchMethodException: onClick [class android.view.View]. It means that your onClick method cannot find your button (id) where you did set an onClickListener method.
And then, in the future, I think the problem will be that AndroidFileDownload is not declared in your Manifest file. You should add it as follows:
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.NautGames.xecta.app.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
// add every activity in your app like this below
<activity
android:name="com.NautGames.xecta.app.AndroidFileDownloader"
android:label="#string/app_name" />
</application>
I'm building a simple paint app and encountering this error given by the logcat:
I'm using the following code:
This SaveDrawing.java serves as the main activity
package com.example.SaveDrawing;
import com.example.SaveDrawing.drawings.DrawingActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class SaveDrawing extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onClick(View view){
switch (view.getId()){
case R.id.drawBtn:
//Intent drawIntent = new Intent(this, DrawingActivity.class);
//startActivity( drawIntent);
//Toast.makeText(getBaseContext(), "This is the Toast message", Toast.LENGTH_SHORT).show();
Intent nextActivity = new Intent(SaveDrawing.this, DrawingActivity.class);
startActivity(nextActivity);
break;
}
}
}
Then the next activity after clicking the button is DrawingActivity.java
package com.example.SaveDrawing.drawings;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.io.FileOutputStream;
import com.example.SaveDrawing.R;
import com.example.SaveDrawing.R.id;
import com.example.SaveDrawing.R.layout;
public class DrawingActivity extends Activity implements View.OnTouchListener{
private DrawingSurface drawingSurface;
private DrawingPath currentDrawingPath;
private Paint currentPaint;
private Button redoBtn;
private Button undoBtn;
private static File APP_FILE_PATH = new File("/sdcard/TutorialForAndroidDrawings");
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawing_activity);
setCurrentPaint();
drawingSurface = (DrawingSurface) findViewById(R.id.drawingSurface);
drawingSurface.setOnTouchListener(this);
redoBtn = (Button) findViewById(R.id.redoBtn);
undoBtn = (Button) findViewById(R.id.undoBtn);
redoBtn.setEnabled(false);
undoBtn.setEnabled(false);
}
private void setCurrentPaint(){
currentPaint = new Paint();
currentPaint.setDither(true);
currentPaint.setColor(0xFFFFFF00);
currentPaint.setStyle(Paint.Style.STROKE);
currentPaint.setStrokeJoin(Paint.Join.ROUND);
currentPaint.setStrokeCap(Paint.Cap.ROUND);
currentPaint.setStrokeWidth(3);
}
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
currentDrawingPath = new DrawingPath();
currentDrawingPath.paint = currentPaint;
currentDrawingPath.path = new Path();
currentDrawingPath.path.moveTo(motionEvent.getX(), motionEvent.getY());
currentDrawingPath.path.lineTo(motionEvent.getX(), motionEvent.getY());
}else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){
currentDrawingPath.path.lineTo(motionEvent.getX(), motionEvent.getY());
}else if(motionEvent.getAction() == MotionEvent.ACTION_UP){
currentDrawingPath.path.lineTo(motionEvent.getX(), motionEvent.getY());
drawingSurface.addDrawingPath(currentDrawingPath);
undoBtn.setEnabled(true);
redoBtn.setEnabled(false);
}
return true;
}
public void onClick(View view){
switch (view.getId()){
case R.id.colorRedBtn:
currentPaint = new Paint();
currentPaint.setDither(true);
currentPaint.setColor(0xFFFF0000);
currentPaint.setStyle(Paint.Style.STROKE);
currentPaint.setStrokeJoin(Paint.Join.ROUND);
currentPaint.setStrokeCap(Paint.Cap.ROUND);
currentPaint.setStrokeWidth(3);
break;
case R.id.colorBlueBtn:
currentPaint = new Paint();
currentPaint.setDither(true);
currentPaint.setColor(0xFF00FF00);
currentPaint.setStyle(Paint.Style.STROKE);
currentPaint.setStrokeJoin(Paint.Join.ROUND);
currentPaint.setStrokeCap(Paint.Cap.ROUND);
currentPaint.setStrokeWidth(3);
break;
case R.id.colorGreenBtn:
currentPaint = new Paint();
currentPaint.setDither(true);
currentPaint.setColor(0xFF0000FF);
currentPaint.setStyle(Paint.Style.STROKE);
currentPaint.setStrokeJoin(Paint.Join.ROUND);
currentPaint.setStrokeCap(Paint.Cap.ROUND);
currentPaint.setStrokeWidth(3);
break;
case R.id.undoBtn:
drawingSurface.undo();
if( drawingSurface.hasMoreUndo() == false ){
undoBtn.setEnabled( false );
}
redoBtn.setEnabled( true );
break;
case R.id.redoBtn:
drawingSurface.redo();
if( drawingSurface.hasMoreRedo() == false ){
redoBtn.setEnabled( false );
}
undoBtn.setEnabled( true );
break;
case R.id.saveBtn:
final Activity currentActivity = this;
Handler saveHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
final AlertDialog alertDialog = new AlertDialog.Builder(currentActivity).create();
alertDialog.setTitle("Saved 1");
alertDialog.setMessage("Your drawing had been saved :)");
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
return;
}
});
alertDialog.show();
}
} ;
new ExportBitmapToFile(this,saveHandler, drawingSurface.getBitmap()).execute();
break;
}
}
private class ExportBitmapToFile extends AsyncTask<Intent,Void,Boolean> {
private Context mContext;
private Handler mHandler;
private Bitmap nBitmap;
public ExportBitmapToFile(Context context,Handler handler,Bitmap bitmap) {
mContext = context;
nBitmap = bitmap;
mHandler = handler;
}
#Override
protected Boolean doInBackground(Intent... arg0) {
try {
if (!APP_FILE_PATH.exists()) {
APP_FILE_PATH.mkdirs();
}
final FileOutputStream out = new FileOutputStream(new File(APP_FILE_PATH + "/myAwesomeDrawing.png"));
nBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
return true;
}catch (Exception e) {
e.printStackTrace();
}
//mHandler.post(completeRunnable);
return false;
}
#Override
protected void onPostExecute(Boolean bool) {
super.onPostExecute(bool);
if ( bool ){
mHandler.sendEmptyMessage(1);
}
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.SaveDrawing"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk android:minSdkVersion="8" />
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".SaveDrawing"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".DrawingActivity" android:screenOrientation="landscape" ></activity>
<activity android:name=".drawings.DrawingActivity"/>
</application>
</manifest>
I have the following in my explorer. I'm not so sure if I got a wrong file structure.
Anyone can help me? I'm kinda new to java and android programming. Many thanks!!! :)
remove <activity android:name=".DrawingActivity" android:screenOrientation="landscape" ></activity> from your manifest as there is no such activity. there is only DrawingActivity in your project structure. Then clean build Run.
Also another possible problem could having an Activity Class name SaveDrawing in the package. You should try to rename the package, update relevant changes to the manifest and try running the app again..
You probably forgot to declare DrawingActivity in your manifest.xml
This is because you attempt to launch com.example.SaveDrawing.drawings/com.example.SaveDrawing.drawings.DrawingActivity,
but actual activity you launch in code is com.example.SaveDrawing/com.example.SaveDrawing.drawings.DrawingActivity
Please change
Intent nextActivity = new Intent(SaveDrawing.this, DrawingActivity.class);
to
Intent nextActivity = new Intent();
nextActivity.setClassName ("com.example.SaveDrawing.drawings", "com.example.SaveDrawing.drawings.DrawingActivity");
It might work.