Related
Currently, I am implementing google Speech to Text in my project. The sample code referred is this: Click Here.
I have used the SpeechService and Voice Recorder class from this project.
public class SpeechService extends Service {
public static final List<String> SCOPE =
Collections.singletonList("https://www.googleapis.com/auth/cloud-platform");
private static final String TAG = "SpeechService";
private static final String PREFS = "SpeechService";
private static final String PREF_ACCESS_TOKEN_VALUE = "access_token_value";
private static final String PREF_ACCESS_TOKEN_EXPIRATION_TIME = "access_token_expiration_time";
/**
* We reuse an access token if its expiration time is longer than this.
*/
private static final int ACCESS_TOKEN_EXPIRATION_TOLERANCE = 30 * 60 * 1000; // thirty minutes
/**
* We refresh the current access token before it expires.
*/
private static final int ACCESS_TOKEN_FETCH_MARGIN = 60 * 1000; // one minute
private static final String HOSTNAME = "speech.googleapis.com";
private static final int PORT = 443;
private static Handler mHandler;
private final SpeechBinder mBinder = new SpeechBinder();
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final StreamObserver<StreamingRecognizeResponse> mResponseObserver
= new StreamObserver<StreamingRecognizeResponse>() {
#Override
public void onNext(StreamingRecognizeResponse response) {
Log.e("Speech", "Recognized");
String text = null;
boolean isFinal = false;
if (response.getResultsCount() > 0) {
System.out.println("result count....."+String.valueOf(response.getResultsCount()));
final StreamingRecognitionResult result = response.getResults(0);
isFinal = result.getIsFinal();
if (result.getAlternativesCount() > 0) {
final SpeechRecognitionAlternative alternative = result.getAlternatives(0);
text = alternative.getTranscript();
}
}
if (text != null && isFinal) {
for (Listener listener : mListeners) {
listener.onSpeechRecognized(text, isFinal);
}
} else {
for (Listener listener : mListeners) {
listener.onRandomStupidity();
}
}
}
#Override
public void onError(Throwable t) {
Log.e(TAG, "Error calling the API.", t);
for(Listener listener : mListeners){
listener.onErrorRecognizing();
}
}
#Override
public void onCompleted() {
Log.i(TAG, "API completed.");
}
};
private volatile AccessTokenTask mAccessTokenTask;
private final Runnable mFetchAccessTokenRunnable = new Runnable() {
#Override
public void run() {
fetchAccessToken();
}
};
private SpeechGrpc.SpeechStub mApi;
private StreamObserver<StreamingRecognizeRequest> mRequestObserver;
public static SpeechService from(IBinder binder) {
return ((SpeechBinder) binder).getService();
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
fetchAccessToken();
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mFetchAccessTokenRunnable);
mHandler = null;
// Release the gRPC channel.
if (mApi != null) {
final ManagedChannel channel = (ManagedChannel) mApi.getChannel();
if (channel != null && !channel.isShutdown()) {
try {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e(TAG, "Error shutting down the gRPC channel.", e);
}
}
mApi = null;
}
}
private void fetchAccessToken() {
if (mAccessTokenTask != null) {
return;
}
mAccessTokenTask = new AccessTokenTask();
mAccessTokenTask.execute();
}
private String getDefaultLanguageCode() {
final LangInnerResponse languageToLearn = MemoryCache.getLanguageToLearn();
if(languageToLearn != null) {
Log.e("Test Lang", languageToLearn.getCode());
return languageToLearn.getCode();
} else {
final Locale locale = Locale.getDefault();
final StringBuilder language = new StringBuilder(locale.getLanguage());
final String country = locale.getCountry();
if (!TextUtils.isEmpty(country)) {
language.append("-");
language.append(country);
}
return language.toString();
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void addListener(#NonNull Listener listener) {
mListeners.add(listener);
}
public void removeListener(#NonNull Listener listener) {
mListeners.remove(listener);
}
/**
** Starts recognizing speech audio.
*
* #param sampleRate The sample rate of the audio.
*/
public void startRecognizing(int sampleRate) {
if (mApi == null) {
Log.w(TAG, "API not ready. Ignoring the request.");
return;
}
System.out.println("calling api....");
// Configure the API
mRequestObserver = mApi.streamingRecognize(mResponseObserver);
mRequestObserver.onNext(StreamingRecognizeRequest.newBuilder()
.setStreamingConfig(StreamingRecognitionConfig.newBuilder()
.setConfig(RecognitionConfig.newBuilder()
.setLanguageCode(getDefaultLanguageCode())
.setEncoding(RecognitionConfig.AudioEncoding.LINEAR16)
.setSampleRateHertz(sampleRate)
.build())
.setInterimResults(true)
.setSingleUtterance(true)
.build())
.build());
}
/**
* Recognizes the speech audio. This method should be called every time a chunk of byte buffer
* is ready.
*
* #param data The audio data.
* #param size The number of elements that are actually relevant in the {#code data}.
*/
public void recognize(byte[] data, int size) {
if (mRequestObserver == null) {
return;
}
// Call the streaming recognition API
mRequestObserver.onNext(StreamingRecognizeRequest.newBuilder()
.setAudioContent(ByteString.copyFrom(data, 0, size))
.build());
}
/**
* Finishes recognizing speech audio.
*/
public void finishRecognizing() {
if (mRequestObserver == null) {
return;
}
mRequestObserver.onCompleted();
mRequestObserver = null;
}
public interface Listener {
/**
* Called when a new piece of text was recognized by the Speech API.
*
* #param text The text.
* #param isFinal {#code true} when the API finished processing audio.
*/
void onSpeechRecognized(String text, boolean isFinal);
void onErrorRecognizing();
void onRandomStupidity();
}
/**
* Authenticates the gRPC channel using the specified {#link GoogleCredentials}.
*/
private static class GoogleCredentialsInterceptor implements ClientInterceptor {
private final Credentials mCredentials;
private Metadata mCached;
private Map<String, List<String>> mLastMetadata;
GoogleCredentialsInterceptor(Credentials credentials) {
mCredentials = credentials;
}
private static Metadata toHeaders(Map<String, List<String>> metadata) {
Metadata headers = new Metadata();
if (metadata != null) {
for (String key : metadata.keySet()) {
Metadata.Key<String> headerKey = Metadata.Key.of(
key, Metadata.ASCII_STRING_MARSHALLER);
for (String value : metadata.get(key)) {
headers.put(headerKey, value);
}
}
}
return headers;
}
#Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
final MethodDescriptor<ReqT, RespT> method, CallOptions callOptions,
final Channel next) {
return new ClientInterceptors.CheckedForwardingClientCall<ReqT, RespT>(
next.newCall(method, callOptions)) {
#Override
protected void checkedStart(Listener<RespT> responseListener, Metadata headers)
throws StatusException {
Metadata cachedSaved;
URI uri = serviceUri(next, method);
synchronized (this) {
Map<String, List<String>> latestMetadata = getRequestMetadata(uri);
if (mLastMetadata == null || mLastMetadata != latestMetadata) {
mLastMetadata = latestMetadata;
mCached = toHeaders(mLastMetadata);
}
cachedSaved = mCached;
}
headers.merge(cachedSaved);
delegate().start(responseListener, headers);
}
};
}
/**
* Generate a JWT-specific service URI. The URI is simply an identifier with enough
* information for a service to know that the JWT was intended for it. The URI will
* commonly be verified with a simple string equality check.
*/
private URI serviceUri(Channel channel, MethodDescriptor<?, ?> method)
throws StatusException {
String authority = channel.authority();
if (authority == null) {
throw Status.UNAUTHENTICATED
.withDescription("Channel has no authority")
.asException();
}
// Always use HTTPS, by definition.
final String scheme = "https";
final int defaultPort = 443;
String path = "/" + MethodDescriptor.extractFullServiceName(method.getFullMethodName());
URI uri;
try {
uri = new URI(scheme, authority, path, null, null);
} catch (URISyntaxException e) {
throw Status.UNAUTHENTICATED
.withDescription("Unable to construct service URI for auth")
.withCause(e).asException();
}
// The default port must not be present. Alternative ports should be present.
if (uri.getPort() == defaultPort) {
uri = removePort(uri);
}
return uri;
}
private URI removePort(URI uri) throws StatusException {
try {
return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), -1 /* port */,
uri.getPath(), uri.getQuery(), uri.getFragment());
} catch (URISyntaxException e) {
throw Status.UNAUTHENTICATED
.withDescription("Unable to construct service URI after removing port")
.withCause(e).asException();
}
}
private Map<String, List<String>> getRequestMetadata(URI uri) throws StatusException {
try {
return mCredentials.getRequestMetadata(uri);
} catch (IOException e) {
throw Status.UNAUTHENTICATED.withCause(e).asException();
}
}
}
private class SpeechBinder extends Binder {
SpeechService getService() {
return SpeechService.this;
}
}
private class CreateApiSingle implements SingleOnSubscribe<SpeechGrpc.SpeechStub> {
#Override
public void subscribe(SingleEmitter<SpeechGrpc.SpeechStub> emitter) throws Exception {
final AccessToken accessToken = generateCredentials();
final SpeechGrpc.SpeechStub api = generateApi(accessToken);
emitter.onSuccess(api);
}
private AccessToken generateCredentials() throws IOException {
final SharedPreferences prefs =
getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
// Check if the current token is still valid for a while
if (tokenValue != null && expirationTime > 0) {
if (expirationTime
> System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TOLERANCE) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
}
// ***** WARNING *****
// In this sample, we load the credential from a JSON file stored in a raw resource
// folder of this client app. You should never do this in your app. Instead, store
// the file in your server and obtain an access token from there.
// *******************
final InputStream stream = getResources().openRawResource(R.raw.credential);
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream)
.createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
prefs.edit()
.putString(PREF_ACCESS_TOKEN_VALUE, token.getTokenValue())
.putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME,
token.getExpirationTime().getTime())
.apply();
stream.close();
return token;
}
private SpeechGrpc.SpeechStub generateApi(AccessToken accessToken) {
final ManagedChannel channel = new OkHttpChannelProvider()
.builderForAddress(HOSTNAME, PORT)
.nameResolverFactory(new DnsNameResolverProvider())
.intercept(new GoogleCredentialsInterceptor(new GoogleCredentials(accessToken)
.createScoped(SCOPE)))
.build();
return SpeechGrpc.newStub(channel);
}
}
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
#Override
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs =
getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
// Check if the current token is still valid for a while
if (tokenValue != null && expirationTime > 0) {
if (expirationTime
> System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TOLERANCE) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
}
// ***** WARNING *****
// In this sample, we load the credential from a JSON file stored in a raw resource
// folder of this client app. You should never do this in your app. Instead, store
// the file in your server and obtain an access token from there.
// *******************
final InputStream stream = getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream)
.createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
prefs.edit()
.putString(PREF_ACCESS_TOKEN_VALUE, token.getTokenValue())
.putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME,
token.getExpirationTime().getTime())
.apply();
return token;
} catch (IOException e) {
Log.e(TAG, "Failed to obtain access token.", e);
}
return null;
}
#Override
protected void onPostExecute(AccessToken accessToken) {
mAccessTokenTask = null;
final ManagedChannel channel = new OkHttpChannelProvider()
.builderForAddress(HOSTNAME, PORT)
.nameResolverFactory(new DnsNameResolverProvider())
.intercept(new GoogleCredentialsInterceptor(new GoogleCredentials(accessToken)
.createScoped(SCOPE)))
.build();
mApi = SpeechGrpc.newStub(channel);
// Schedule access token refresh before it expires
if (mHandler != null) {
mHandler.postDelayed(mFetchAccessTokenRunnable,
Math.max(accessToken.getExpirationTime().getTime()
- System.currentTimeMillis()
- ACCESS_TOKEN_FETCH_MARGIN, ACCESS_TOKEN_EXPIRATION_TOLERANCE));
}
}
}}
public class VoiceRecorder {
private static final int[] SAMPLE_RATE_CANDIDATES = new int[]{48000, 44100};
private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;
private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private static final int AMPLITUDE_THRESHOLD = 1500;
private static final int SPEECH_TIMEOUT_MILLIS = 2000;
private static final int MAX_SPEECH_LENGTH_MILLIS = 30 * 1000;
public static abstract class Callback {
/**
* Called when the recorder starts hearing voice.
*/
public void onVoiceStart() {
}
/**
* Called when the recorder is hearing voice.
*
* #param data The audio data in {#link AudioFormat#ENCODING_PCM_16BIT}.
* #param size The size of the actual data in {#code data}.
*/
public void onVoice(byte[] data, int size) {
}
/**
* Called when the recorder stops hearing voice.
*/
public void onVoiceEnd() {
}
}
private final Callback mCallback;
private AudioRecord mAudioRecord;
private Thread mThread;
private byte[] mBuffer;
private final Object mLock = new Object();
/** The timestamp of the last time that voice is heard. */
private long mLastVoiceHeardMillis = Long.MAX_VALUE;
/** The timestamp when the current voice is started. */
private long mVoiceStartedMillis;
public VoiceRecorder(#NonNull Callback callback) {
mCallback = callback;
}
/**
* Starts recording audio.
*
* <p>The caller is responsible for calling {#link #stop()} later.</p>
*/
public void start() {
// Stop recording if it is currently ongoing.
stop();
// Try to create a new recording session.
mAudioRecord = createAudioRecord();
if (mAudioRecord == null) {
throw new RuntimeException("Cannot instantiate VoiceRecorder");
}
// Start recording.
mAudioRecord.startRecording();
// Start processing the captured audio.
mThread = new Thread(new ProcessVoice());
mThread.start();
}
/**
* Stops recording audio.
*/
public void stop() {
synchronized (mLock) {
System.out.println("stop audio record....");
dismiss();
if (mThread != null) {
mThread.interrupt();
mThread = null;
}
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
mBuffer = null;
System.out.println("stop audio record....2");
}
}
/**
* Dismisses the currently ongoing utterance.
*/
public void dismiss() {
if (mLastVoiceHeardMillis != Long.MAX_VALUE) {
mLastVoiceHeardMillis = Long.MAX_VALUE;
mCallback.onVoiceEnd();
}
}
/**
* Retrieves the sample rate currently used to record audio.
*
* #return The sample rate of recorded audio.
*/
public int getSampleRate() {
if (mAudioRecord != null) {
return mAudioRecord.getSampleRate();
}
return 0;
}
/**
* Creates a new {#link AudioRecord}.
*
* #return A newly created {#link AudioRecord}, or null if it cannot be created (missing
* permissions?).
*/
private AudioRecord createAudioRecord() {
for (int sampleRate : SAMPLE_RATE_CANDIDATES) {
final int sizeInBytes = AudioRecord.getMinBufferSize(sampleRate, CHANNEL, ENCODING);
if (sizeInBytes == AudioRecord.ERROR_BAD_VALUE) {
continue;
}
final AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
sampleRate, CHANNEL, ENCODING, sizeInBytes);
if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
mBuffer = new byte[sizeInBytes];
return audioRecord;
} else {
audioRecord.release();
}
}
return null;
}
/**
* Continuously processes the captured audio and notifies {#link #mCallback} of corresponding
* events.
*/
private class ProcessVoice implements Runnable {
#Override
public void run() {
while (true) {
synchronized (mLock) {
if (Thread.currentThread().isInterrupted()) {
break;
}
final int size = mAudioRecord.read(mBuffer, 0, mBuffer.length);
final long now = System.currentTimeMillis();
if (isHearingVoice(mBuffer, size)) {
if (mLastVoiceHeardMillis == Long.MAX_VALUE) {
mVoiceStartedMillis = now;
mCallback.onVoiceStart();
}
mCallback.onVoice(mBuffer, size);
mLastVoiceHeardMillis = now;
if (now - mVoiceStartedMillis > MAX_SPEECH_LENGTH_MILLIS) {
end();
}
} else if (mLastVoiceHeardMillis != Long.MAX_VALUE) {
mCallback.onVoice(mBuffer, size);
if (now - mLastVoiceHeardMillis > SPEECH_TIMEOUT_MILLIS) {
end();
}
}
}
}
}
private void end() {
mLastVoiceHeardMillis = Long.MAX_VALUE;
mCallback.onVoiceEnd();
System.out.println("end...");
}
private boolean isHearingVoice(byte[] buffer, int size) {
for (int i = 0; i < size - 1; i += 2) {
// The buffer has LINEAR16 in little endian.
int s = buffer[i + 1];
if (s < 0) s *= -1;
s <<= 8;
s += Math.abs(buffer[i]);
if (s > AMPLITUDE_THRESHOLD) {
return true;
}
}
return false;
}
}}
Then I implemented the Speech Service & Voice Recorder callback as follows:
private VoiceRecorder voiceRecorder;
private final SpeechService.Listener speechServiceListener = new SpeechService.Listener() {
#Override
public void onSpeechRecognized(final String text, final boolean isFinal) {
if (isFinal) {
System.out.println("ui thread...");
if (!TextUtils.isEmpty(text)) {
runOnUiThread(() -> {
showMessage(text);
flingAnswer(text);
});
}
}
}
#Override
public void onErrorRecognizing() {
showMessage("Please try again. Could not detect.");
}
#Override
public void onRandomStupidity() {
}
};
private SpeechService speechService;
private final VoiceRecorder.Callback voiceCallback = new VoiceRecorder.Callback() {
#Override
public void onVoiceStart() {
if (speechService != null) {
System.out.println("voice start....");
speechService.startRecognizing(voiceRecorder.getSampleRate());
}
}
#Override
public void onVoice(byte[] data, int size) {
if (speechService != null) {
speechService.recognize(data, size);
}
}
#Override
public void onVoiceEnd() {
if (speechService != null) {
speechService.finishRecognizing();
}
}
};
private final ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
speechService = SpeechService.from(binder);
speechService.addListener(speechServiceListener);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
speechService = null;
}
};
For voice input this is the code:
#Override
public void stopRecognizing() {
stopVoiceRecorder();
Log.e("Recording", "Stopped");
}
#Override
public void startRecognizing() {
if (permissionManager != null && permissionManager.askForPermissions()) {
startVoiceRecorder();
vibrate.vibrate(50);//Providing haptic feedback to user on press.
}
Log.e("Recording", "Started");
}
binding.imgVoice.setOnTouchListener((v, event) -> {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
System.out.println("up...");
mCallback.stopRecognizing();
binding.imgVoice
.animate()
.scaleX(1.0f)
.scaleY(1.0f);
binding.imgVoice.setVisibility(View.GONE);
binding.progressBar.setVisibility(View.VISIBLE);
break;
case MotionEvent.ACTION_DOWN:
System.out.println("down...");
binding.imgVoice
.animate()
.scaleX(1.8f)
.scaleY(1.8f);
mCallback.startRecognizing();
break;
}
return true;
});
}
When I press the mic, event registered as Action_Down, I start the voice recorder and on releasing the mic , voice recorder is stopped. Also, with the Action_Down I am scaling up the mic icon which needs to be scaled down on Action_Up . But the ui freezes as a whole most of the times. I find that the onNext() callback for StreamObserver is continuously being invoked before the isFinal becomes true.
private void startVoiceRecorder() {
if (voiceRecorder != null) {
voiceRecorder.stop();
}
voiceRecorder = new VoiceRecorder(voiceCallback);
voiceRecorder.start();
}
private void stopVoiceRecorder() {
if (voiceRecorder != null) {
voiceRecorder.stop();
voiceRecorder = null;
}
}
But I want the mic to scale down as soon as I release the mic(on Action up event) which is not happening.
So if anyone can help me over this?
Thanks in Advance.
I am using the following code to create a qrcode scanner, got this code some one which refered to zxing library. The whole code works fine but when the scanner app close its not calling the onActivityResult after finshing the activity task. can somebody help me heres the code I am using .
My main activity
/**
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) Matt Kane 2010
* Copyright (c) 2011, IBM Corporation
* Copyright (c) 2013, Maciej Nux Jaros
*/
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.LOG;
import org.apache.cordova.PluginResult;
import android.util.Log;
import android.widget.Toast;
/**
* This calls out to the ZXing barcode reader and returns the result.
*
* #sa https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
*/
public class BarcodeScanner extends CordovaPlugin {
public static final int REQUEST_CODE = 0x0ba7c0de;
private static final String SCAN = "scan";
private static final String ENCODE = "encode";
private static final String CANCELLED = "cancelled";
private static final String FORMAT = "format";
private static final String TEXT = "text";
private static final String DATA = "data";
private static final String TYPE = "type";
private static final String SCAN_INTENT = "com.google.zxing.client.android.SCAN";
private static final String ENCODE_DATA = "ENCODE_DATA";
private static final String ENCODE_TYPE = "ENCODE_TYPE";
private static final String ENCODE_INTENT = "com.MobileApp.BarcodeScanner.ENCODE";
private static final String TEXT_TYPE = "TEXT_TYPE";
private static final String EMAIL_TYPE = "EMAIL_TYPE";
private static final String PHONE_TYPE = "PHONE_TYPE";
private static final String SMS_TYPE = "SMS_TYPE";
private static final String LOG_TAG = "BarcodeScanner";
private CallbackContext callbackContext;
/**
* Constructor.
*/
public BarcodeScanner() {
}
/**
* Executes the request.
*
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
* cordova.getThreadPool().execute(runnable);
*
* To run on the UI thread, use:
* cordova.getActivity().runOnUiThread(runnable);
*
* #param action The action to execute.
* #param args The exec() arguments.
* #param callbackContext The callback context used when calling back into JavaScript.
* #return Whether the action was valid.
*
* #sa https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
*/
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
this.callbackContext = callbackContext;
if (action.equals(ENCODE)) {
JSONObject obj = args.optJSONObject(0);
if (obj != null) {
String type = obj.optString(TYPE);
String data = obj.optString(DATA);
// If the type is null then force the type to text
if (type == null) {
type = TEXT_TYPE;
}
if (data == null) {
callbackContext.error("User did not specify data to encode");
return true;
}
// encode(type, data);
} else {
callbackContext.error("User did not specify data to encode");
return true;
}
} else if (action.equals(SCAN)) {
scan(args);
} else {
return false;
}
return true;
}
/**
* Starts an intent to scan and decode a barcode.
*/
public void scan(JSONArray args) {
IntentIntegrator integrator = new IntentIntegrator(this.cordova.getActivity());
integrator.initiateScan();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
String contantsString = null;
IntentResult scanResult = IntentIntegrator.parseActivityResult(
requestCode, resultCode, data);
JSONObject obj = new JSONObject();
if (scanResult != null)
{
// handle scan result
contantsString = scanResult.getContents() == null ? "0"
: scanResult.getContents();
if (contantsString.equalsIgnoreCase("0"))
{
// Toast.makeText(this.cordova.getActivity(),
// "Problem to get the contant Number", Toast.LENGTH_LONG)
// .show();
Log.d(LOG_TAG, "Problem in reading the result possibly read string is 0");
}
else
{
// Toast.makeText(this.cordova.getActivity(), contantsString,
// Toast.LENGTH_LONG).show();
Log.d(LOG_TAG, "succesfully read the barcode");
}
try {
obj.put(TEXT, contantsString);
obj.put(FORMAT, contantsString);
obj.put(CANCELLED, false);
} catch (JSONException e) {
Log.d(LOG_TAG, "This should never happen");
}
} else {
// Toast.makeText(this.cordova.getActivity(),
// "Problem to scan the barcode.", Toast.LENGTH_LONG).show();
Log.d(LOG_TAG, "Problem in reading the result possibly read string is null");
try
{
obj.put(TEXT, "");
obj.put(FORMAT, "");
obj.put(CANCELLED, false);
}
catch (JSONException e) {
Log.d(LOG_TAG, "This should never happen");
}
}
}
}
IntentIntegrator class
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.util.Log;
public final class IntentIntegrator {
public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits
private static final String TAG = IntentIntegrator.class.getSimpleName();
public static final String DEFAULT_TITLE = "Install Barcode Scanner?";
public static final String DEFAULT_MESSAGE =
"This application requires Barcode Scanner. Would you like to install it?";
public static final String DEFAULT_YES = "Yes";
public static final String DEFAULT_NO = "No";
private static final String BS_PACKAGE = "com.google.zxing.client.android";
// supported barcode formats
public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14");
public static final Collection<String> ONE_D_CODE_TYPES =
list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128",
"ITF", "RSS_14", "RSS_EXPANDED");
public static final Collection<String> QR_CODE_TYPES = Collections.singleton("QR_CODE");
public static final Collection<String> DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX");
public static final Collection<String> ALL_CODE_TYPES = null;
public static final Collection<String> TARGET_BARCODE_SCANNER_ONLY = Collections.singleton(BS_PACKAGE);
public static final Collection<String> TARGET_ALL_KNOWN = list(
BS_PACKAGE, // Barcode Scanner
"com.srowen.bs.android", // Barcode Scanner+
"com.srowen.bs.android.simple" // Barcode Scanner+ Simple
// TODO add more -- what else supports this intent?
);
private final Activity activity;
private String title;
private String message;
private String buttonYes;
private String buttonNo;
private Collection<String> targetApplications;
public IntentIntegrator(Activity activity) {
this.activity = activity;
title = DEFAULT_TITLE;
message = DEFAULT_MESSAGE;
buttonYes = DEFAULT_YES;
buttonNo = DEFAULT_NO;
targetApplications = TARGET_ALL_KNOWN;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void setTitleByID(int titleID) {
title = activity.getString(titleID);
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void setMessageByID(int messageID) {
message = activity.getString(messageID);
}
public String getButtonYes() {
return buttonYes;
}
public void setButtonYes(String buttonYes) {
this.buttonYes = buttonYes;
}
public void setButtonYesByID(int buttonYesID) {
buttonYes = activity.getString(buttonYesID);
}
public String getButtonNo() {
return buttonNo;
}
public void setButtonNo(String buttonNo) {
this.buttonNo = buttonNo;
}
public void setButtonNoByID(int buttonNoID) {
buttonNo = activity.getString(buttonNoID);
}
public Collection<String> getTargetApplications() {
return targetApplications;
}
public void setTargetApplications(Collection<String> targetApplications) {
this.targetApplications = targetApplications;
}
public void setSingleTargetApplication(String targetApplication) {
this.targetApplications = Collections.singleton(targetApplication);
}
/**
* Initiates a scan for all known barcode types.
*/
public AlertDialog initiateScan() {
return initiateScan(ALL_CODE_TYPES);
}
/**
* Initiates a scan only for a certain set of barcode types, given as strings corresponding
* to their names in ZXing's {#code BarcodeFormat} class like "UPC_A". You can supply constants
* like {#link #PRODUCT_CODE_TYPES} for example.
*/
public AlertDialog initiateScan(Collection<String> desiredBarcodeFormats) {
Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
// check which types of codes to scan for
if (desiredBarcodeFormats != null) {
// set the desired barcode types
StringBuilder joinedByComma = new StringBuilder();
for (String format : desiredBarcodeFormats) {
if (joinedByComma.length() > 0) {
joinedByComma.append(',');
}
joinedByComma.append(format);
}
intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());
}
String targetAppPackage = findTargetAppPackage(intentScan);
if (targetAppPackage == null) {
return showDownloadDialog();
}
intentScan.setPackage(targetAppPackage);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
activity.startActivityForResult(intentScan, REQUEST_CODE);
return null;
}
private String findTargetAppPackage(Intent intent) {
PackageManager pm = activity.getPackageManager();
List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (availableApps != null) {
for (ResolveInfo availableApp : availableApps) {
String packageName = availableApp.activityInfo.packageName;
if (targetApplications.contains(packageName)) {
return packageName;
}
}
}
return null;
}
private AlertDialog showDownloadDialog() {
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
downloadDialog.setTitle(title);
downloadDialog.setMessage(message);
downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Uri uri = Uri.parse("market://details?id=" + BS_PACKAGE);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
activity.startActivity(intent);
} catch (ActivityNotFoundException anfe) {
// Hmm, market is not installed
Log.w(TAG, "Android Market is not installed; cannot install Barcode Scanner");
}
}
});
downloadDialog.setNegativeButton(buttonNo, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {}
});
return downloadDialog.show();
}
/**
* <p>Call this from your {#link Activity}'s
* {#link Activity#onActivityResult(int, int, Intent)} method.</p>
*
* #return null if the event handled here was not related to this class, or
* else an {#link IntentResult} containing the result of the scan. If the user cancelled scanning,
* the fields will be null.
*/
public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
String contents = intent.getStringExtra("SCAN_RESULT");
String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT");
byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES");
int intentOrientation = intent.getIntExtra("SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE);
Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation;
String errorCorrectionLevel = intent.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL");
return new IntentResult(contents,
formatName,
rawBytes,
orientation,
errorCorrectionLevel);
}
return new IntentResult();
}
return null;
}
/**
* Shares the given text by encoding it as a barcode, such that another user can
* scan the text off the screen of the device.
*
* #param text the text string to encode as a barcode
*/
public void shareText(CharSequence text) {
Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setAction(BS_PACKAGE + ".ENCODE");
intent.putExtra("ENCODE_TYPE", "TEXT_TYPE");
intent.putExtra("ENCODE_DATA", text);
String targetAppPackage = findTargetAppPackage(intent);
if (targetAppPackage == null) {
showDownloadDialog();
} else {
intent.setPackage(targetAppPackage);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
activity.startActivity(intent);
}
}
private static Collection<String> list(String... values) {
return Collections.unmodifiableCollection(Arrays.asList(values));
}
}
IntentResult class
public final class IntentResult {
private final String contents;
private final String formatName;
private final byte[] rawBytes;
private final Integer orientation;
private final String errorCorrectionLevel;
IntentResult() {
this(null, null, null, null, null);
}
IntentResult(String contents,
String formatName,
byte[] rawBytes,
Integer orientation,
String errorCorrectionLevel) {
this.contents = contents;
this.formatName = formatName;
this.rawBytes = rawBytes;
this.orientation = orientation;
this.errorCorrectionLevel = errorCorrectionLevel;
}
/**
* #return raw content of barcode
*/
public String getContents() {
return contents;
}
/**
* #return name of format, like "QR_CODE", "UPC_A". See {#code BarcodeFormat} for more format names.
*/
public String getFormatName() {
return formatName;
}
/**
* #return raw bytes of the barcode content, if applicable, or null otherwise
*/
public byte[] getRawBytes() {
return rawBytes;
}
/**
* #return rotation of the image, in degrees, which resulted in a successful scan. May be null.
*/
public Integer getOrientation() {
return orientation;
}
/**
* #return name of the error correction level used in the barcode, if applicable
*/
public String getErrorCorrectionLevel() {
return errorCorrectionLevel;
}
#Override
public String toString() {
StringBuilder dialogText = new StringBuilder(100);
dialogText.append("Format: ").append(formatName).append('\n');
dialogText.append("Contents: ").append(contents).append('\n');
int rawBytesLength = rawBytes == null ? 0 : rawBytes.length;
dialogText.append("Raw bytes: (").append(rawBytesLength).append(" bytes)\n");
dialogText.append("Orientation: ").append(orientation).append('\n');
dialogText.append("EC level: ").append(errorCorrectionLevel).append('\n');
return dialogText.toString();
}
}
app opens but when it scans the qrcode it doesn't return anything.
This code worked for me ... I broke the above code and used it into mine and it worked.
/**
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) Matt Kane 2010
* Copyright (c) 2011, IBM Corporation
* Copyright (c) 2013, Maciej Nux Jaros
*/
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.util.Log;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
/**
* This calls out to the ZXing barcode reader and returns the result.
*
* #sa https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
*/
public class BarcodeScanner extends CordovaPlugin {
public static final int REQUEST_CODE = 0x0ba7c0de;
private static final String SCAN = "scan";
private static final String ENCODE = "encode";
private static final String CANCELLED = "cancelled";
private static final String FORMAT = "format";
private static final String TEXT = "text";
private static final String DATA = "data";
private static final String TYPE = "type";
private static final String SCAN_INTENT = "com.google.zxing.client.android";
private static final String ENCODE_DATA = "ENCODE_DATA";
private static final String ENCODE_TYPE = "ENCODE_TYPE";
private static final String ENCODE_INTENT = "com.phonegap.plugins.barcodescanner.ENCODE";
private static final String TEXT_TYPE = "TEXT_TYPE";
private static final String EMAIL_TYPE = "EMAIL_TYPE";
private static final String PHONE_TYPE = "PHONE_TYPE";
private static final String SMS_TYPE = "SMS_TYPE";
private static final String LOG_TAG = "BarcodeScanner";
private CallbackContext callbackContext;
/**
* Constructor.
*/
public BarcodeScanner() {
}
/**
* Executes the request.
*
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
* cordova.getThreadPool().execute(runnable);
*
* To run on the UI thread, use:
* cordova.getActivity().runOnUiThread(runnable);
*
* #param action The action to execute.
* #param args The exec() arguments.
* #param callbackContext The callback context used when calling back into JavaScript.
* #return Whether the action was valid.
*
* #sa https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
*/
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
this.callbackContext = callbackContext;
if (action.equals(ENCODE)) {
JSONObject obj = args.optJSONObject(0);
if (obj != null) {
String type = obj.optString(TYPE);
String data = obj.optString(DATA);
// If the type is null then force the type to text
if (type == null) {
type = TEXT_TYPE;
}
if (data == null) {
callbackContext.error("User did not specify data to encode");
return true;
}
encode(type, data);
} else {
callbackContext.error("User did not specify data to encode");
return true;
}
} else if (action.equals(SCAN)) {
scan();
} else {
return false;
}
return true;
}
/**
* Starts an intent to scan and decode a barcode.
*/
public AlertDialog scan() {
Intent intentScan = new Intent(SCAN_INTENT+".SCAN");
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
String targetAppPackage = findTargetAppPackage(intentScan);
if (targetAppPackage == null) {
return showDownloadDialog();
}
this.cordova.startActivityForResult((CordovaPlugin) this, intentScan, REQUEST_CODE);
return null;
}
private String findTargetAppPackage(Intent intent) {
PackageManager pm = this.cordova.getActivity().getPackageManager();
List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (availableApps != null) {
for (ResolveInfo availableApp : availableApps) {
String packageName = availableApp.activityInfo.packageName;
// if (targetApplications.contains(packageName)) {
// return packageName;
// }
if (packageName!=null) {
return packageName;
}
}
}
return null;
}
private AlertDialog showDownloadDialog() {
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(BarcodeScanner.this.cordova.getActivity());
downloadDialog.setTitle("Install Barcode Scanner?");
downloadDialog.setMessage("This application requires Barcode Scanner. Would you like to install it?");
downloadDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Uri uri = Uri.parse("market://details?id=" + SCAN_INTENT);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
BarcodeScanner.this.cordova.getActivity().startActivity(intent);
} catch (ActivityNotFoundException anfe) {
// Hmm, market is not installed
// Log.w(TAG, "Android Market is not installed; cannot install Barcode Scanner");
}
}
});
downloadDialog.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {}
});
return downloadDialog.show();
}
/**
* Called when the barcode scanner intent completes.
*
* #param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* #param resultCode The integer result code returned by the child activity through its setResult().
* #param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
*/
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
JSONObject obj = new JSONObject();
try {
obj.put(TEXT, intent.getStringExtra("SCAN_RESULT"));
obj.put(FORMAT, intent.getStringExtra("SCAN_RESULT_FORMAT"));
obj.put(CANCELLED, false);
} catch (JSONException e) {
Log.d(LOG_TAG, "This should never happen");
}
//this.success(new PluginResult(PluginResult.Status.OK, obj), this.callback);
this.callbackContext.success(obj);
} else if (resultCode == Activity.RESULT_CANCELED) {
JSONObject obj = new JSONObject();
try {
obj.put(TEXT, "");
obj.put(FORMAT, "");
obj.put(CANCELLED, true);
} catch (JSONException e) {
Log.d(LOG_TAG, "This should never happen");
}
//this.success(new PluginResult(PluginResult.Status.OK, obj), this.callback);
this.callbackContext.success(obj);
} else {
//this.error(new PluginResult(PluginResult.Status.ERROR), this.callback);
this.callbackContext.error("Unexpected error");
}
}
}
/**
* Initiates a barcode encode.
*
* #param type Endoiding type.
* #param data The data to encode in the bar code.
*/
public void encode(String type, String data) {
Intent intentEncode = new Intent(ENCODE_INTENT);
intentEncode.putExtra(ENCODE_TYPE, type);
intentEncode.putExtra(ENCODE_DATA, data);
this.cordova.getActivity().startActivity(intentEncode);
}
}
I am working on barcode scanning on button click to increment the quantity counted field by one of a table when the scanning result matches with the item number field of the table. If the scan result matches item number it should update the quantity counted of that row. I am unable to get the scan result itself. Getting NullPointerException.
This is my code.
These are two Java files from zxing.
IntentIntegrator.java
package com.example.mis;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
public class IntentIntegrator {
public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16
// bits
private static final String TAG = IntentIntegrator.class.getSimpleName();
public static final String DEFAULT_TITLE = "Install Barcode Scanner?";
public static final String DEFAULT_MESSAGE = "This application requires Barcode Scanner. Would you like to install it?";
public static final String DEFAULT_YES = "Yes";
public static final String DEFAULT_NO = "No";
private static final String BS_PACKAGE = "com.google.zxing.client.android";
private static final String BSPLUS_PACKAGE = "com.srowen.bs.android";
// supported barcode formats
public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A",
"UPC_E", "EAN_8", "EAN_13", "RSS_14");
public static final Collection<String> ONE_D_CODE_TYPES = list("UPC_A",
"UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128",
"ITF", "RSS_14", "RSS_EXPANDED");
public static final Collection<String> QR_CODE_TYPES = Collections
.singleton("QR_CODE");
public static final Collection<String> DATA_MATRIX_TYPES = Collections
.singleton("DATA_MATRIX");
public static final Collection<String> ALL_CODE_TYPES = null;
public static final List<String> TARGET_BARCODE_SCANNER_ONLY = Collections
.singletonList(BS_PACKAGE);
public static final List<String> TARGET_ALL_KNOWN = list(BSPLUS_PACKAGE, // Barcode
// Scanner+
BSPLUS_PACKAGE + ".simple", // Barcode Scanner+ Simple
BS_PACKAGE // Barcode Scanner
// What else supports this intent?
);
private final Activity activity;
private String title;
private String message;
private String buttonYes;
private String buttonNo;
private List<String> targetApplications;
private final Map<String, Object> moreExtras;
public IntentIntegrator(Activity activity) {
this.activity = activity;
title = DEFAULT_TITLE;
message = DEFAULT_MESSAGE;
buttonYes = DEFAULT_YES;
buttonNo = DEFAULT_NO;
targetApplications = TARGET_ALL_KNOWN;
moreExtras = new HashMap<String, Object>(3);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void setTitleByID(int titleID) {
title = activity.getString(titleID);
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void setMessageByID(int messageID) {
message = activity.getString(messageID);
}
public String getButtonYes() {
return buttonYes;
}
public void setButtonYes(String buttonYes) {
this.buttonYes = buttonYes;
}
public void setButtonYesByID(int buttonYesID) {
buttonYes = activity.getString(buttonYesID);
}
public String getButtonNo() {
return buttonNo;
}
public void setButtonNo(String buttonNo) {
this.buttonNo = buttonNo;
}
public void setButtonNoByID(int buttonNoID) {
buttonNo = activity.getString(buttonNoID);
}
public Collection<String> getTargetApplications() {
return targetApplications;
}
public final void setTargetApplications(List<String> targetApplications) {
if (targetApplications.isEmpty()) {
throw new IllegalArgumentException("No target applications");
}
this.targetApplications = targetApplications;
}
public void setSingleTargetApplication(String targetApplication) {
this.targetApplications = Collections.singletonList(targetApplication);
}
public Map<String, ?> getMoreExtras() {
return moreExtras;
}
public final void addExtra(String key, Object value) {
moreExtras.put(key, value);
}
/**
* Initiates a scan for all known barcode types.
*/
public final AlertDialog initiateScan() {
return initiateScan(ALL_CODE_TYPES);
}
/**
* Initiates a scan only for a certain set of barcode types, given as
* strings corresponding to their names in ZXing's {#code BarcodeFormat}
* class like "UPC_A". You can supply constants like
* {#link #PRODUCT_CODE_TYPES} for example.
*
* #return the {#link AlertDialog} that was shown to the user prompting them
* to download the app if a prompt was needed, or null otherwise
*/
public final AlertDialog initiateScan(
Collection<String> desiredBarcodeFormats) {
Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
// check which types of codes to scan for
if (desiredBarcodeFormats != null) {
// set the desired barcode types
StringBuilder joinedByComma = new StringBuilder();
for (String format : desiredBarcodeFormats) {
if (joinedByComma.length() > 0) {
joinedByComma.append(',');
}
joinedByComma.append(format);
}
intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());
}
String targetAppPackage = findTargetAppPackage(intentScan);
if (targetAppPackage == null) {
return showDownloadDialog();
}
intentScan.setPackage(targetAppPackage);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
attachMoreExtras(intentScan);
startActivityForResult(intentScan, REQUEST_CODE);
return null;
}
/**
* Start an activity. This method is defined to allow different methods of
* activity starting for newer versions of Android and for compatibility
* library.
*
* #param intent
* Intent to start.
* #param code
* Request code for the activity
* #see android.app.Activity#startActivityForResult(Intent, int)
* #see android.app.Fragment#startActivityForResult(Intent, int)
*/
protected void startActivityForResult(Intent intent, int code) {
activity.startActivityForResult(intent, code);
}
private String findTargetAppPackage(Intent intent) {
PackageManager pm = activity.getPackageManager();
List<ResolveInfo> availableApps = pm.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
if (availableApps != null) {
for (String targetApp : targetApplications) {
if (contains(availableApps, targetApp)) {
return targetApp;
}
}
}
return null;
}
private static boolean contains(Iterable<ResolveInfo> availableApps,
String targetApp) {
for (ResolveInfo availableApp : availableApps) {
String packageName = availableApp.activityInfo.packageName;
if (targetApp.equals(packageName)) {
return true;
}
}
return false;
}
private AlertDialog showDownloadDialog() {
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
downloadDialog.setTitle(title);
downloadDialog.setMessage(message);
downloadDialog.setPositiveButton(buttonYes,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
String packageName = targetApplications.get(0);
Uri uri = Uri.parse("market://details?id="
+ packageName);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
activity.startActivity(intent);
} catch (ActivityNotFoundException anfe) {
// Hmm, market is not installed
Log.w(TAG,
"Google Play is not installed; cannot install "
+ packageName);
}
}
});
downloadDialog.setNegativeButton(buttonNo,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
return downloadDialog.show();
}
/**
* <p>
* Call this from your {#link Activity}'s
* {#link Activity#onActivityResult(int, int, Intent)} method.
* </p>
*
* #return null if the event handled here was not related to this class, or
* else an {#link IntentResult} containing the result of the scan.
* If the user cancelled scanning, the fields will be null.
*/
public static IntentResult parseActivityResult(int requestCode,
int resultCode, Intent intent) {
if (requestCode == REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
String contents = intent.getStringExtra("SCAN_RESULT");
String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT");
byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES");
int intentOrientation = intent.getIntExtra(
"SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE);
Integer orientation = intentOrientation == Integer.MIN_VALUE ? null
: intentOrientation;
String errorCorrectionLevel = intent
.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL");
return new IntentResult(contents, formatName, rawBytes,
orientation, errorCorrectionLevel);
}
return new IntentResult();
}
return null;
}
/**
* Defaults to type "TEXT_TYPE".
*
* #see #shareText(CharSequence, CharSequence)
*/
public final AlertDialog shareText(CharSequence text) {
return shareText(text, "TEXT_TYPE");
}
/**
* Shares the given text by encoding it as a barcode, such that another user
* can scan the text off the screen of the device.
*
* #param text
* the text string to encode as a barcode
* #param type
* type of data to encode. See
* {#code com.google.zxing.client.android.Contents.Type}
* constants.
* #return the {#link AlertDialog} that was shown to the user prompting them
* to download the app if a prompt was needed, or null otherwise
*/
public final AlertDialog shareText(CharSequence text, CharSequence type) {
Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setAction(BS_PACKAGE + ".ENCODE");
intent.putExtra("ENCODE_TYPE", type);
intent.putExtra("ENCODE_DATA", text);
String targetAppPackage = findTargetAppPackage(intent);
if (targetAppPackage == null) {
return showDownloadDialog();
}
intent.setPackage(targetAppPackage);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
attachMoreExtras(intent);
activity.startActivity(intent);
return null;
}
private static List<String> list(String... values) {
return Collections.unmodifiableList(Arrays.asList(values));
}
private void attachMoreExtras(Intent intent) {
for (Map.Entry<String, Object> entry : moreExtras.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// Kind of hacky
if (value instanceof Integer) {
intent.putExtra(key, (Integer) value);
} else if (value instanceof Long) {
intent.putExtra(key, (Long) value);
} else if (value instanceof Boolean) {
intent.putExtra(key, (Boolean) value);
} else if (value instanceof Double) {
intent.putExtra(key, (Double) value);
} else if (value instanceof Float) {
intent.putExtra(key, (Float) value);
} else if (value instanceof Bundle) {
intent.putExtra(key, (Bundle) value);
} else {
intent.putExtra(key, value.toString());
}
}
}
}
IntentResult.java
package com.example.mis;
public class IntentResult {
private final String contents;
private final String formatName;
private final byte[] rawBytes;
private final Integer orientation;
private final String errorCorrectionLevel;
IntentResult() {
this(null, null, null, null, null);
}
IntentResult(String contents,
String formatName,
byte[] rawBytes,
Integer orientation,
String errorCorrectionLevel) {
this.contents = contents;
this.formatName = formatName;
this.rawBytes = rawBytes;
this.orientation = orientation;
this.errorCorrectionLevel = errorCorrectionLevel;
}
/**
* #return raw content of barcode
*/
public String getContents() {
return contents;
}
/**
* #return name of format, like "QR_CODE", "UPC_A". See {#code BarcodeFormat} for more format names.
*/
public String getFormatName() {
return formatName;
}
/**
* #return raw bytes of the barcode content, if applicable, or null otherwise
*/
public byte[] getRawBytes() {
return rawBytes;
}
/**
* #return rotation of the image, in degrees, which resulted in a successful scan. May be null.
*/
public Integer getOrientation() {
return orientation;
}
/**
* #return name of the error correction level used in the barcode, if applicable
*/
public String getErrorCorrectionLevel() {
return errorCorrectionLevel;
}
#Override
public String toString() {
StringBuilder dialogText = new StringBuilder(100);
dialogText.append("Format: ").append(formatName).append('\n');
dialogText.append("Contents: ").append(contents).append('\n');
int rawBytesLength = rawBytes == null ? 0 : rawBytes.length;
dialogText.append("Raw bytes: (").append(rawBytesLength).append(" bytes)\n");
dialogText.append("Orientation: ").append(orientation).append('\n');
dialogText.append("EC level: ").append(errorCorrectionLevel).append('\n');
return dialogText.toString();
}
}
This is the activity in which i am implementing barcode scanning
public class InventoryCount extends Activity {
private Button mbtn_scan;
mbtn_scan.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
IntentIntegrator integrator = new IntentIntegrator(
InventoryCount.this);
integrator.initiateScan();
}
});
}
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
// retrieve result of scanning - instantiate ZXing object
IntentResult scanningResult = IntentIntegrator.parseActivityResult(
requestCode, resultCode, intent);
// check we have a valid result
try {
if (resultCode == RESULT_OK) {
Log.i("Scanning Result ", "" + scanningResult);
Toast.makeText(
InventoryCount.this,
"Scanning success the content is : "
+ scanningResult.getContents(),
Toast.LENGTH_SHORT).show();
String sr = scanningResult.getContents();
getBarCodeData(sr);
} else if (resultCode == RESULT_CANCELED) {
Toast toast = Toast.makeText(InventoryCount.this,
"Scanning Cancelled!", Toast.LENGTH_SHORT);
toast.show();
}
} catch (Exception e) {
System.out.println("Error on scanning: "+e);
}
}
public void getBarCodeData(String itmNumber) {
Cursor c;
try {
String qtyCountQry = "SELECT " + DatabaseHandler.KEY_QTYCOUNTED
+ " FROM " + DatabaseHandler.TABLE_MIC2 + " WHERE "
+ DatabaseHandler.KEY_ITEMNUMBER + "='" + itmNumber + "'";
SQLiteDatabase sq = db.getReadableDatabase();
c = sq.rawQuery(qtyCountQry, null);
c.moveToFirst();
String q2 = c.getString(c
.getColumnIndex(DatabaseHandler.KEY_QTYCOUNTED));
Toast.makeText(InventoryCount.this, "Quantity Count is " + q2,
Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(InventoryCount.this, "Exception " + e,
Toast.LENGTH_SHORT).show();
}
}
I have just tried to display scan result and if it matches itemnumber it will display the corresponding quantity in that row as shown in code above. But as of now not even scan result is displaying. Help me.. Also say how to increment the quantity by one and update it in db.
Try to use the zxing library by extending the CaptureActivity Class.
public class ScannerData extends CaptureActivity {
Handler handler = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_scanner);
}
#Override
public void handleDecode(final Result rawResult, Bitmap barcode,
float scaleFactor) {
// TODO Auto-generated method stub
handler = getHandler();
handler.sendEmptyMessageDelayed(R.id.restart_preview,
CaptureActivity.BULK_MODE_SCAN_DELAY_MS);
String mQrcode = rawResult.getText().toString();
}
}
Use the code like this.
You can get the qr-code here
String mQrcode = rawResult.getText().toString();
In Xml you need to include capture.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/frameLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_horizontal"
android:layout_marginRight="25dp">
<FrameLayout
android:layout_width="510dp"
android:layout_height="310dp"
android:background="#drawable/scanner_box"
android:layout_gravity="center" >
<include
android:layout_width="750dp"
android:layout_height="450dp"
android:layout_gravity="center"
android:layout_marginBottom="50dp"
android:layout_marginRight="110dp"
layout="#layout/capture" />
</FrameLayout>
</RelativeLayout>
</RelativeLayout>
I have a question. At this time, the capturePicture of WebView is deprecated.
I want to ask if there is a way to replace the function. I meant it can capture entire of the webview (not only the view is displayed)
Thanks
I finally found out the solution.
Some of codes
public class WebViewActivity extends Activity {
private static WebView webView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webview);
webView = (WebView) findViewById(R.id.webView1);
webView.loadUrl("http://developer.android.com/reference/packages.html");
// webView.loadUrl("http://developer.android.com/training/basics/firstapp/creating-project.html");
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
// do your stuff here
webView.measure(MeasureSpec.makeMeasureSpec(
MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
webView.layout(0, 0, webView.getMeasuredWidth(),
webView.getMeasuredHeight());
webView.setDrawingCacheEnabled(true);
webView.buildDrawingCache();
Bitmap bm = Bitmap.createBitmap(webView.getMeasuredWidth(),
webView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas bigcanvas = new Canvas(bm);
Paint paint = new Paint();
int iHeight = bm.getHeight();
bigcanvas.drawBitmap(bm, 0, iHeight, paint);
webView.draw(bigcanvas);
System.out.println("1111111111111111111111="
+ bigcanvas.getWidth());
System.out.println("22222222222222222222222="
+ bigcanvas.getHeight());
if (bm != null) {
try {
String path = Environment.getExternalStorageDirectory()
.toString();
OutputStream fOut = null;
File file = new File(path, "/aaaa.png");
fOut = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.PNG, 50, fOut);
fOut.flush();
fOut.close();
bm.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
}
The layout.xml
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/webView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Use draw() method of WebView
Example :
ImageView imageview;
WebView webview;
class Background extends AsyncTask<Void, Void, Bitmap>
{
#Override
protected Bitmap doInBackground(Void... params)
{
try
{
Thread.sleep(2000);
Bitmap bitmap = Bitmap.createBitmap(webview.getWidth(), webview.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
webview.draw(canvas);
return bitmap;
}
catch (InterruptedException e){}
catch (Exception e){}
return null;
}
#Override
protected void onPostExecute(Bitmap result)
{
imageview.setImageBitmap(result);
}
}
webview.setWebChromeClient(new WebChromeClient()
{
public void onProgressChanged(WebView view, int progress)
{
if(progress==100)
new Background().execute();
}
});
Just extends BaseWebView below:
public abstract class BaseWebView extends WebView {
private static final String TAG = "BaseWebView";
private static final int DELAY_MS_MAX = 1000;
private static final int DELAY_MS_DRAW = 100;
private static final long REQUEST_ID_INIT = 0;
public BaseWebView(Context context) {
super(context);
init();
}
public BaseWebView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BaseWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* to make sure to set {#link #loadDataFinished} to true at last
* in {#link #DELAY_MS_MAX} ms later.
*/
private static final int MSG_LOAD_DATA_FINISH_DEF = 0;
/**
* to set {#link #loadDataFinished} to true in {#link #DELAY_MS_DRAW} ms later.
*/
private static final int MSG_LOAD_DATA_FINISH_DRAW = 1;
private String description;
/**
* if load data finished
* <P><P>
* mark the status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
*/
private AtomicBoolean loadDataFinished = new AtomicBoolean(true);
/**
* if progress reached 1000
* <P><P>
* mark the status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
*/
private AtomicBoolean progressFinished = new AtomicBoolean(true);
/**
* the request id of load data
* <P><P>
* mark the status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
*/
private AtomicLong loadDataTimestamp = new AtomicLong(REQUEST_ID_INIT);
private Handler handler = new MyHandler(this);
/**
* clear status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}, set to false.
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
* <p>
* <b>Usage:</b> call in load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}
*/
private void clearLoadDataStatus() {
DLog.d(TAG, "clearLoadDataStatus: set false, obj=" + this.hashCode());
loadDataFinished.set(false);
progressFinished.set(false);
// generates a new load request id
loadDataTimestamp.set(System.currentTimeMillis());
}
private void setLoadDataFinished(long requestId) {
DLog.d(TAG, "setLoadDataFinished: id=" + requestId
+ ", loadDataTimestamp=" + loadDataTimestamp.get() + ", obj=" + this.hashCode());
if (!progressFinished.get() || requestId != loadDataTimestamp.get()) {
return;
}
loadDataFinished();
}
private void loadDataFinished() {
DLog.d(TAG, "loadDataFinished: set true, obj=" + this.hashCode());
loadDataFinished.set(true);
// clear load request id
loadDataTimestamp.set(REQUEST_ID_INIT);
}
/**
* get status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}, set to false.
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
*/
public boolean isLoadDataFinished() {
return loadDataFinished.get();
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
private void handleLoadDataFinished(int what) {
if (loadDataTimestamp.get() == REQUEST_ID_INIT) {
// there is no load data actions
//DLog.w(TAG, "handleLoadDataFinished: there is no load data actions, obj="
// + this.hashCode());
return;
}
DLog.d(TAG, "handleLoadDataFinished: obj=" + this.hashCode());
int delay = 0;
long requestId = loadDataTimestamp.get();
if (what == MSG_LOAD_DATA_FINISH_DEF) {
delay = DELAY_MS_MAX;
} else if (what == MSG_LOAD_DATA_FINISH_DRAW) {
delay = DELAY_MS_DRAW;
}
handler.removeMessages(what);
handler.sendMessageDelayed(Message.obtain(handler, what, (Object) requestId), delay);
}
private void init() {
this.setWebChromeClient(new DefWebChromeClient());
this.setHorizontalScrollBarEnabled(false);
this.setVerticalScrollBarEnabled(false);
this.description = onGetDescription();
}
/**
* a description of this WebView
*/
abstract public String onGetDescription();
#Override
public void loadDataWithBaseURL(
#Nullable String baseUrl, String data, #Nullable String mimeType,
#Nullable String encoding, #Nullable String historyUrl) {
clearLoadDataStatus();
super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
#Override
public void loadData(String data, #Nullable String mimeType, #Nullable String encoding) {
clearLoadDataStatus();
super.loadData(data, mimeType, encoding);
}
#Override
public void stopLoading() {
super.stopLoading();
loadDataFinished();
}
#Override
public void setWebChromeClient(WebChromeClient client) {
if (!(client instanceof DefWebChromeClient)) {
throw new IllegalArgumentException(
"the WebChromeClient must be an instance of " + DefWebChromeClient.class);
}
super.setWebChromeClient(client);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Log.d(TAG, "onDraw: " + this.hashCode());
if (progressFinished.get()) {
// set load data finished in DELAY_MS_DRAW.
// !!! Notice that the Runnable will not be exec if this WebView is not to show,
// and this will cause the `loadDataFinished` being `false` all the time,
// if you want that executing whatever, plz use `handle.post()`.
post(new Runnable() {
#Override
public void run() {
handleLoadDataFinished(MSG_LOAD_DATA_FINISH_DRAW);
}
});
}
}
class DefWebChromeClient extends WebChromeClient {
#Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (newProgress == 100) {
progressFinished.set(true);
// set load data finished in DELAY_MS_MAX as def.
// !!! Notice that the Runnable will not be exec if this WebView is not to show,
// and this will cause the `loadDataFinished` being `false` all the time,
// if you want that executing whatever, plz use `handle.post()`.
post(new Runnable() {
#Override
public void run() {
handleLoadDataFinished(MSG_LOAD_DATA_FINISH_DEF);
}
});
}
Log.d(TAG, "onProgressChanged: " + newProgress
+ ", desc=" + description + ", obj=" + view.hashCode());
}
}
static class MyHandler extends Handler {
WeakReference<BaseWebView> mViewReference;
MyHandler(BaseWebView webView) {
mViewReference = new WeakReference<>(webView);
}
#Override
public void handleMessage(Message msg) {
final BaseWebView webView = mViewReference.get();
if (webView == null) {
return;
}
// handle msg
if (msg.what == MSG_LOAD_DATA_FINISH_DEF || msg.what == MSG_LOAD_DATA_FINISH_DRAW) {
webView.setLoadDataFinished((Long) msg.obj);
}
}
}
}
=== END ===
Notice that only loadData() & loadDataWithBaseURL() works above,
you can add loadUrl(), reload() by yourself if needed.
Demo:https://github.com/zhaoya188/completed-load-webview
have a gridView populated by bitmaps that animate in when the bitmap loads asynchronously.
Sometimes when flinging the gridView some of the items do not render properly. The animation will trigger but the bitmap will simply not show up.
I have confirmed that the bitmap is indeed there (at least the data) but it just doesn't render.
This happens when flinging the gridView quickly but also happens on a slow scroll as well.
It seems that the view recycling is not working properly.
Here is my code:
ListView adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(convertView == null){
convertView = new FlipAnimatedCacheableImage(mContext);
}
final ImageInfo info = mapItem(getItem(position));
FlipAnimatedCacheableImage image = (FlipAnimatedCacheableImage)convertView;
image.resetState();
image.setTitle(info.title);
image.setSubTitle(info.subTitle);
image.loadImage(info.imgURL, false);
return convertView;
}
Code for FlipAnimatedCacheableImage:
public class FlipAnimatedCacheableImage extends FrameLayout
{
private static final String TAG = FlipAnimatedCacheableImage.class.getCanonicalName();
protected static final long DURATION = 300;
private ImageView mPlaceHolder;
private NetworkedCacheableImageView mCacheableImage;
private View mTextContainer;
private TextView mTitleTv;
private TextView mSubTitleTv;
private View mProgress;
private ImageLoadListener mListener = new ImageLoadListener()
{
private boolean isShown;
#Override
public void onImageLoaded(boolean animate)
{
if(animate){
mProgress.setVisibility(View.GONE);
mPlaceHolder.setVisibility(View.VISIBLE);
mPlaceHolder.setRotationY(0);
mCacheableImage.setVisibility(View.VISIBLE);
mCacheableImage.setRotationY(-90);
mPlaceHolder.animate().rotationY(90).setDuration(DURATION).start();
mCacheableImage.animate().rotationY(0).setDuration(DURATION).setStartDelay(DURATION).start();
if(!TextUtils.isEmpty(mTitleTv.getText()) || !TextUtils.isEmpty(mSubTitleTv.getText())){
mTextContainer.setVisibility(View.VISIBLE);
mTextContainer.setAlpha(0);
mTextContainer.animate().alpha(1).setDuration(DURATION).setStartDelay(DURATION * 2).start();
}
else{
mTextContainer.setVisibility(View.GONE);
}
isShown = true;
FlipAnimatedCacheableImage.this.invalidate();
}
else{
mPlaceHolder.setVisibility(View.GONE);
mProgress.setVisibility(View.GONE);
mCacheableImage.setVisibility(View.VISIBLE);
mCacheableImage.setRotationY(0);
mCacheableImage.clearAnimation();
if(!TextUtils.isEmpty(mTitleTv.getText()) || !TextUtils.isEmpty(mSubTitleTv.getText())){
mTextContainer.setVisibility(View.VISIBLE);
mTextContainer.setAlpha(1);
}
else{
mTextContainer.setVisibility(View.GONE);
}
FlipAnimatedCacheableImage.this.invalidate();
}
}
};
public FlipAnimatedCacheableImage(Context context, boolean isLarge)
{
this(context, null, 0, isLarge);
}
public FlipAnimatedCacheableImage(Context context)
{
this(context, null);
}
public FlipAnimatedCacheableImage(Context context, AttributeSet attrs)
{
this(context, attrs, 0, false);
}
public FlipAnimatedCacheableImage(Context context, AttributeSet attrs, int defStyle, boolean isLarge)
{
super(context, attrs, defStyle);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.item_image_thumbnail, this);
mCacheableImage = (NetworkedCacheableImageView)this.findViewById(R.id.image_view);
mPlaceHolder = (ImageView)this.findViewById(R.id.place_holder);
mProgress = this.findViewById(R.id.progressBar);
// only used by small images
mTextContainer = this.findViewById(R.id.text_container);
mTitleTv = (TextView)this.findViewById(R.id.text_title);
mSubTitleTv = (TextView)this.findViewById(R.id.text_sub_title);
// listener to animate after loading
mCacheableImage.setLoadListener(mListener);
// set default state
mTextContainer.setVisibility(View.GONE);
mTitleTv.setVisibility(GONE);
mSubTitleTv.setVisibility(GONE);
if(isLarge){
// adjust the size to the correct dimensions, ignore titleAnd subTitle
FrameLayout.LayoutParams imageParams = new FrameLayout.LayoutParams((int)context.getResources()
.getDimension(R.dimen.grid_image_width_large), (int)context.getResources().getDimension(
R.dimen.grid_image_height_large));
findViewById(R.id.content_wrapper).setLayoutParams(imageParams);
}
// this.setOnClickListener(listener);
resetState();
}
public void resetState()
{
mCacheableImage.setVisibility(View.VISIBLE);
mCacheableImage.setRotationY(0);
mProgress.setVisibility(View.VISIBLE);
mPlaceHolder.setVisibility(View.VISIBLE);
mTextContainer.setVisibility(View.GONE);
mTitleTv.setVisibility(GONE);
mSubTitleTv.setVisibility(GONE);
}
public boolean loadImage(String url, boolean fullSize)
{
return mCacheableImage.loadImage(url, fullSize);
}
public void setTitle(String title)
{
mTitleTv.setText(title);
if(!TextUtils.isEmpty(title)){
mTitleTv.setVisibility(View.VISIBLE);
}
else{
mTitleTv.setVisibility(View.GONE);
}
}
public void setSubTitle(String subTitle)
{
mSubTitleTv.setText(subTitle);
if(!TextUtils.isEmpty(subTitle)){
mSubTitleTv.setVisibility(View.VISIBLE);
}
else{
mSubTitleTv.setVisibility(View.GONE);
}
}
}
FlipAnimatedCache will request the image from a cache written by Chris Banes here https://github.com/chrisbanes/Android-BitmapCache
Here is that code:
/*******************************************************************************
* Copyright 2011, 2013 Chris Banes.
*
* 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.
*******************************************************************************/
/**
* Simple extension of CacheableImageView which allows downloading of Images of
* the Internet.
*
* This code isn't production quality, but works well enough for this sample.s
*
* #author Chris Banes
*
*/
public class NetworkedCacheableImageView extends CacheableImageView
{
private static final String TAG = NetworkedCacheableImageView.class.getCanonicalName();
public interface ImageLoadListener
{
public void onImageLoaded(boolean animate);
}
/**
* This task simply fetches an Bitmap from the specified URL and wraps it in
* a wrapper. This implementation is NOT 'best practice' or production ready
* code.
*/
private static class ImageUrlAsyncTask extends AsyncTask<String, Void, CacheableBitmapDrawable>
{
private final BitmapLruCache mCache;
private final WeakReference<ImageView> mImageViewRef;
private final WeakReference<NetworkedCacheableImageView> viewRef;
private final BitmapFactory.Options mDecodeOpts;
private final ImageLoadListener mLoadListener;
private boolean outOfMemoryFailure;
private String mURL;
ImageUrlAsyncTask(ImageView imageView, BitmapLruCache cache, BitmapFactory.Options decodeOpts,
ImageLoadListener listener, NetworkedCacheableImageView view)
{
mCache = cache;
mLoadListener = listener;
mImageViewRef = new WeakReference<ImageView>(imageView);
viewRef = new WeakReference<NetworkedCacheableImageView>(view);
mDecodeOpts = decodeOpts;
}
#Override
protected CacheableBitmapDrawable doInBackground(String... params)
{
try{
// Return early if the ImageView has disappeared.
if(null == mImageViewRef.get()){
return null;
}
mURL = params[0];
// Now we're not on the main thread we can check all caches
CacheableBitmapDrawable result = mCache.get(mURL, mDecodeOpts);
if(null == result){
Log.w("CACHE", "Downloading: " + mURL);
// The bitmap isn't cached so download from the web
HttpURLConnection conn = (HttpURLConnection)new URL(mURL).openConnection();
InputStream is = new BufferedInputStream(conn.getInputStream());
// Add to cache
result = mCache.put(mURL, is, mDecodeOpts);
}
else{
Log.w("CACHE", "Got from Disk Cache: " + mURL);
}
return result;
}
catch(IOException e){
Log.e("Error downloading image", e.toString());
}
catch(OutOfMemoryError e){
Log.e(TAG, "running out of memory, trimming image memory cache");
outOfMemoryFailure = true;
}
return null;
}
#Override
protected void onPostExecute(final CacheableBitmapDrawable result)
{
// super.onPostExecute(result);
if(outOfMemoryFailure || result == null || !result.hasValidBitmap()){
mCache.trimMemory();
// viewRef.get().loadImageAsync(mURL, mDecodeOpts);
Log.e(TAG, "image probably did not load::" + mURL);
}
else{
if(BuildConfig.DEBUG){
Log.d("bitmapCache", "NetworkedCacheableImageView.ImageUrlAsyncTask.onPostExecute() WIDTH:"
+ result.getBitmap().getWidth());
Log.d("bitmapCache", "NetworkedCacheableImageView.ImageUrlAsyncTask.onPostExecute() HEIGHT:"
+ result.getBitmap().getHeight());
}
Log.i(TAG, "RESULT for :" + mURL + " mImageViewRef::" + mImageViewRef + " outOfMemoryFailure::"
+ outOfMemoryFailure);
if(result != null){
Log.i(TAG, "RESULT object for :" + mURL + " result:hasValidBitmap" + result.hasValidBitmap()
+ " result:isReferencedByCache" + result.isReferencedByCache() + " result.isBeingDisplayed() "
+ result.isBeingDisplayed());
}
Runnable runnable = new Runnable()
{
#Override
public void run()
{
if(mImageViewRef != null){
final ImageView iv = mImageViewRef.get();
Log.e(TAG, "RESULT image view reference :" + mURL + " view ref::" + iv);
if(null != iv){
iv.setImageDrawable(result);
if(mLoadListener != null){
mLoadListener.onImageLoaded(true);
}
}
}
}
};
Handler handler = new Handler();
handler.postDelayed(runnable, 50);
}
super.onPostExecute(result);
}
}
private final BitmapLruCache mCache;
private ImageUrlAsyncTask mCurrentTask;
private ImageLoadListener mListener;
public NetworkedCacheableImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
mCache = WatchApplication.getBitmapCache();
}
public void setLoadListener(ImageLoadListener listener)
{
mListener = listener;
}
public void removeListener()
{
mListener = null;
}
/**
* Loads the Bitmap.
*
* #param url
* - URL of image
* #param fullSize
* - Whether the image should be kept at the original size
* #return true if the bitmap was found in the cache
*/
public boolean loadImage(String url, final boolean fullSize)
{
setImageDrawable(null);
// First check whether there's already a task running, if so cancel it
if(TextUtils.isEmpty(url))
return false;
if(null != mCurrentTask){
mCurrentTask.cancel(false);
}
// Check to see if the memory cache already has the bitmap. We can
// safely do
// this on the main thread.
BitmapDrawable wrapper = mCache.getFromMemoryCache(url);
if(null != wrapper){
// The cache has it, so just display it
if(BuildConfig.DEBUG){
Log.w(TAG, "CACHE. FOUND IN MEMORY:" + url);
}
setImageDrawable(wrapper);
if(mListener != null){
mListener.onImageLoaded(false);
}
return true;
}
else{
// Memory Cache doesn't have the URL, do threaded request...
BitmapFactory.Options decodeOpts = null;
if(!fullSize){
decodeOpts = new BitmapFactory.Options();
// decodeOpts.inDensity = DisplayMetrics.DENSITY_XHIGH;
decodeOpts.inPurgeable = true;
decodeOpts.outHeight = this.getHeight();
decodeOpts.outWidth = this.getWidth();
}
loadImageAsync(url, decodeOpts);
return false;
}
}
public void loadImageAsync(String url, BitmapFactory.Options decodeOpts)
{
mCurrentTask = new ImageUrlAsyncTask(this, mCache, decodeOpts, mListener, this);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
SDK11.executeOnThreadPool(mCurrentTask, url);
}
else{
mCurrentTask.execute(url);
}
}
}
Thanks in advance for any help!
Two things worth trying -
For chaining animation operations use a listener:
mPlaceHolder.animate()
.alpha(0f)
.scaleX(0.9f)
.scaleY(0.9f)
.rotationY(90)
.setDuration(DURATION)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mCacheableImage.setImageDrawable(drawable);
mCacheableImage.animate()
.alpha(1f)
.scaleY(1f)
.scaleX(1f)
.rotationY(0)
.setDuration(DURATION)
.setListener(null);
}
});
Use setHasTransientState() to ensure the views are not recycled in the ListView/GridView. See this DevByte video for more info.