I am trying to write data to an NFC card using an Android application but when I try to write the data I get a java.io.IOExcetion. The log tells me that it is null. I have added printstacktrace and it points to an error at android.nfc.tech.NdefFormatble.format(NdefFormarmatable.java:131) and android.nfc.tech.NdefFormatble.format(NdefFormarmatable.java:94). I have had a look at this class to see what the error is and Android Studio says that it cannot resolve symbol on some of the imports in that class. I can not figure out what is going wrong and I would appreciate any help in sorting this problem. I did have this working before and then all of a sudden I started getting this error. I did update Android Studio to 3.1.1 but I tried using AS 3.0 and 2.3 but I get the same error. I have included the methods which all being called as well as the stacktrace.
This is where the card is formatted and written to:
private void formatTag(Tag tag, NdefMessage ndefMessage) {
try {
NdefFormatable ndefFormatable = NdefFormatable.get(tag);
if (ndefFormatable == null) {
Toast.makeText(this, "Tag is not ndef formatable!", Toast.LENGTH_SHORT).show();
return;
}
ndefFormatable.connect();
ndefFormatable.format(ndefMessage); ***<----MainActivity.java:469***
ndefFormatable.close();
Toast.makeText(this, "Tag writen!", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Log.e("formatTag", "" + e.getMessage());
e.printStackTrace();
}
}
This is the method for writing the NDEF message
private void writeNdefMessage(Tag tag, NdefMessage ndefMessage) {
try {
if (tag == null) {
Toast.makeText(this, "Tag object cannot be null", Toast.LENGTH_SHORT).show();
return;
}
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
// format tag with the ndef format and writes the message.
formatTag(tag, ndefMessage); **<----- MainActivity.java 494**
} else {
ndef.connect();
if (!ndef.isWritable()) {
Toast.makeText(this, "Tag is not writable!", Toast.LENGTH_SHORT).show();
ndef.close();
return;
}
ndef.writeNdefMessage(ndefMessage);
ndef.close();
Toast.makeText(this, "Tag writen!", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
Log.e("writeNdefMessage", "" + e.getMessage());
e.printStackTrace();
}
This is where writeNdefMessage is call from
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.hasExtra(NfcAdapter.EXTRA_TAG)) {
Toast.makeText(this, "NfcIntent!", Toast.LENGTH_SHORT).show();
if(tglReadWrite.isChecked())
{
Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if(parcelables != null && parcelables.length > 0)
{
readDataFromMessage((NdefMessage) parcelables[0]);
}else{
Toast.makeText(this, "No NDEF messages found!", Toast.LENGTH_SHORT).show();
}
}else{
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
NdefMessage ndefMessage = createNdefMessage(mToken.getText()+";");
writeNdefMessage(tag, ndefMessage); ***<---- MainActivity.java:406***
}
}
}
and here is the StackTrace
04-13 02:50:51.112 6311-6311/com.appsolutedevelopment.labourstaff E/formatTag: null
04-13 02:50:51.112 6311-6311/com.appsolutedevelopment.labourstaff W/System.err: java.io.IOException
04-13 02:50:51.113 6311-6311/com.appsolutedevelopment.labourstaff W/System.err: at android.nfc.tech.NdefFormatable.format(NdefFormatable.java:131)
at android.nfc.tech.NdefFormatable.format(NdefFormatable.java:94)
at com.appsolutedevelopment.labourstaff.MainActivity.formatTag(MainActivity.java:469)
at com.appsolutedevelopment.labourstaff.MainActivity.writeNdefMessage(MainActivity.java:494)
at com.appsolutedevelopment.labourstaff.MainActivity.onNewIntent(MainActivity.java:406)
at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1228)
at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1240)
at android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2946)
at android.app.ActivityThread.performNewIntents(ActivityThread.java:2958)
at android.app.ActivityThread.handleNewIntent(ActivityThread.java:2967)
at android.app.ActivityThread.-wrap15(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1648)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6523)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
And finally the NdefFormatable class
package android.nfc.tech;
import android.nfc.ErrorCodes; ***<---- Cannot resolve symbol 'ErrorCodes'***
import android.nfc.FormatException;
import android.nfc.INfcTag; ***<---- Cannot resolve symbol 'INfcTag'***
import android.nfc.NdefMessage;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.os.RemoteException;
import android.util.Log;
import java.io.IOException;
/**
* Provide access to NDEF format operations on a {#link Tag}.
*
* <p>Acquire a {#link NdefFormatable} object using {#link #get}.
*
* <p>Android devices with NFC must only enumerate and implement this
* class for tags for which it can format to NDEF.
*
* <p>Unfortunately the procedures to convert unformated tags to NDEF formatted
* tags are not specified by NFC Forum, and are not generally well-known. So
* there is no mandatory set of tags for which all Android devices with NFC
* must support {#link NdefFormatable}.
*
* <p class="note"><strong>Note:</strong> Methods that perform I/O operations
* require the {#link android.Manifest.permission#NFC} permission.
*/
public final class NdefFormatable extends BasicTagTechnology {
private static final String TAG = "NFC";
/**
* Get an instance of {#link NdefFormatable} for the given tag.
* <p>Does not cause any RF activity and does not block.
* <p>Returns null if {#link NdefFormatable} was not enumerated in {#link Tag#getTechList}.
* This indicates the tag is not NDEF formatable by this Android device.
*
* #param tag an NDEF formatable tag
* #return NDEF formatable object
*/
public static NdefFormatable get(Tag tag) {
if (!tag.hasTech(TagTechnology.NDEF_FORMATABLE)) return null;
try {
return new NdefFormatable(tag);
} catch (RemoteException e) {
return null;
}
}
/**
* Internal constructor, to be used by NfcAdapter
* #hide
*/
public NdefFormatable(Tag tag) throws RemoteException {
super(tag, TagTechnology.NDEF_FORMATABLE);
}
/**
* Format a tag as NDEF, and write a {#link NdefMessage}.
*
* <p>This is a multi-step process, an IOException is thrown
* if any one step fails.
* <p>The card is left in a read-write state after this operation.
*
* <p>This is an I/O operation and will block until complete. It must
* not be called from the main application thread. A blocked call will be canceled with
* {#link IOException} if {#link #close} is called from another thread.
*
* <p class="note">Requires the {#link android.Manifest.permission#NFC} permission.
*
* #param firstMessage the NDEF message to write after formatting, can be null
* #throws TagLostException if the tag leaves the field
* #throws IOException if there is an I/O failure, or the operation is canceled
* #throws FormatException if the NDEF Message to write is malformed
*/
public void format(NdefMessage firstMessage) throws IOException, FormatException {
format(firstMessage, false); ***<----NdefFormatable.java:94***
}
/**
* Formats a tag as NDEF, write a {#link NdefMessage}, and make read-only.
*
* <p>This is a multi-step process, an IOException is thrown
* if any one step fails.
* <p>The card is left in a read-only state if this method returns successfully.
*
* <p>This is an I/O operation and will block until complete. It must
* not be called from the main application thread. A blocked call will be canceled with
* {#link IOException} if {#link #close} is called from another thread.
*
* <p class="note">Requires the {#link android.Manifest.permission#NFC} permission.
*
* #param firstMessage the NDEF message to write after formatting
* #throws TagLostException if the tag leaves the field
* #throws IOException if there is an I/O failure, or the operation is canceled
* #throws FormatException if the NDEF Message to write is malformed
*/
public void formatReadOnly(NdefMessage firstMessage) throws IOException, FormatException {
format(firstMessage, true);
}
/*package*/ void format(NdefMessage firstMessage, boolean makeReadOnly) throws IOException,
FormatException {
checkConnected();
try {
int serviceHandle = mTag.getServiceHandle();
INfcTag tagService = mTag.getTagService();
int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT);
switch (errorCode) {
case ErrorCodes.SUCCESS:
break;
case ErrorCodes.ERROR_IO:
throw new IOException(); ***<---- NdefFormatable.java:131***
case ErrorCodes.ERROR_INVALID_PARAM:
throw new FormatException();
default:
// Should not happen
throw new IOException();
}
// Now check and see if the format worked
if (!tagService.isNdef(serviceHandle)) {
throw new IOException();
}
// Write a message, if one was provided
if (firstMessage != null) {
errorCode = tagService.ndefWrite(serviceHandle, firstMessage);
switch (errorCode) {
case ErrorCodes.SUCCESS:
break;
case ErrorCodes.ERROR_IO:
throw new IOException();
case ErrorCodes.ERROR_INVALID_PARAM:
throw new FormatException();
default:
// Should not happen
throw new IOException();
}
}
// optionally make read-only
if (makeReadOnly) {
errorCode = tagService.ndefMakeReadOnly(serviceHandle);
switch (errorCode) {
case ErrorCodes.SUCCESS:
break;
case ErrorCodes.ERROR_IO:
throw new IOException();
case ErrorCodes.ERROR_INVALID_PARAM:
throw new IOException();
default:
// Should not happen
throw new IOException();
}
}
} catch (RemoteException e) {
Log.e(TAG, "NFC service dead", e);
}
}
}
If I have missed anything or anybody needs more info put up just ask and I'll get whatever is needed. Id be very grateful if someone could shed some light on this as it has been driving me around the twist for so long.
Thanks.
I found the answer to this question here Android NFC - ndef.writeNdefMessage() throws IOException and erases tag data. This explains what is going on in my app when I try to write to the NFC card after an unsuccessful write operation. If anyone is looking for a solution to this problem then this Android nfcA.connect(), nfcA.transceive(), nfcA.setTimeout() and nfcA.getMaxTransceiveLength() might be of some help. If reading and writing critical data to NFC tokens/cards is what you are trying to achieve then I would suggest using nfcA as oppossed to NDEF.
Related
I want to use the fingerprint sensor on the back of my pixel 2 to press a button in my app when I tap on it.
I found something for it in the docs but it looks like it only reports swipe input and not tap input: https://developer.android.com/reference/android/accessibilityservice/FingerprintGestureController.html
Yes its true. What Michael Dodd is telling about. We can get various events like when fingerprint is succeeded, failed or removed fast like such events. tap event is more likely the finger print removed fast. I didn't find any direct api for getting the tap events. Instead use the authentication for getting the tap in your custom activity. Implement the below code and find the various appropriate events for handling the tap event.
#TargetApi(Build.VERSION_CODES.M)
public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback {
/**
* The timeout for the error to be displayed. Returns to the normal UI after this.
*/
private static final long ERROR_TIMEOUT_MILLIS = 1600;
/**
* The timeout for the success to be displayed. Calls {#link Callback#onAuthenticated()} after this.
*/
private static final long SUCCESS_DELAY_MILLIS = 1300;
/**
* Alias for our key in the Android Key Store
**/
private static final String KEY_NAME = "my_key";
/**
* The {#link Cipher} used to init {#link FingerprintManager}
*/
private Cipher mCipher;
/**
* The {#link KeyStore} used to initiliaze the key {#link #KEY_NAME}
*/
private KeyStore mKeyStore;
/**
* The {#link KeyGenerator} used to generate the key {#link #KEY_NAME}
*/
private KeyGenerator mKeyGenerator;
/**
* The {#link android.hardware.fingerprint.FingerprintManager.CryptoObject}
*/
private final FingerprintManager mFingerprintManager;
/**
* The {#link ImageView} that is used to show the authent state
*/
private final ImageView mIcon;
/**
* The {#link TextView} that is used to show the authent state
*/
private final TextView mErrorTextView;
private final Callback mCallback;
/**
* The {#link CancellationSignal} used after an error happens
*/
private CancellationSignal mCancellationSignal;
/**
* Used if the user cancelled the authentication by himself
*/
private boolean mSelfCancelled;
/**
* Builder class for {#link FingerprintUiHelper} in which injected fields from Dagger
* holds its fields and takes other arguments in the {#link #build} method.
*/
public static class FingerprintUiHelperBuilder {
private final FingerprintManager mFingerPrintManager;
public FingerprintUiHelperBuilder(FingerprintManager fingerprintManager) {
mFingerPrintManager = fingerprintManager;
}
public FingerprintUiHelper build(ImageView icon, TextView errorTextView, Callback callback) {
return new FingerprintUiHelper(mFingerPrintManager, icon, errorTextView,
callback);
}
}
/**
* Constructor for {#link FingerprintUiHelper}. This method is expected to be called from
* only the {#link FingerprintUiHelperBuilder} class.
*/
private FingerprintUiHelper(FingerprintManager fingerprintManager,
ImageView icon, TextView errorTextView, Callback callback) {
mFingerprintManager = fingerprintManager;
mIcon = icon;
mErrorTextView = errorTextView;
mCallback = callback;
}
/**
* Starts listening to {#link FingerprintManager}
*
* #throws SecurityException If the hardware is not available, or the permission are not set
*/
public void startListening() throws SecurityException {
if (initCipher()) {
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(mCipher);
if (!isFingerprintAuthAvailable()) {
return;
}
mCancellationSignal = new CancellationSignal();
mSelfCancelled = false;
mFingerprintManager.authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null);
mIcon.setImageResource(R.drawable.ic_fp_40px);
}
}
/**
* Stops listening to {#link FingerprintManager}
*/
public void stopListening() {
if (mCancellationSignal != null) {
mSelfCancelled = true;
mCancellationSignal.cancel();
mCancellationSignal = null;
}
}
/**
* Called by {#link FingerprintManager} if the authentication threw an error.
*/
#Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
if (!mSelfCancelled) {
showError(errString);
mIcon.postDelayed(new Runnable() {
#Override
public void run() {
mCallback.onError();
}
}, ERROR_TIMEOUT_MILLIS);
}
}
/**
* Called by {#link FingerprintManager} if the user asked for help.
*/
#Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
showError(helpString);
}
/**
* Called by {#link FingerprintManager} if the authentication failed (bad finger etc...).
*/
#Override
public void onAuthenticationFailed() {
showError(mIcon.getResources().getString(
R.string.pin_code_fingerprint_not_recognized));
}
/**
* Called by {#link FingerprintManager} if the authentication succeeded.
*/
#Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
mIcon.setImageResource(R.drawable.ic_fingerprint_success);
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.success_color, null));
mErrorTextView.setText(
mErrorTextView.getResources().getString(R.string.pin_code_fingerprint_success));
mIcon.postDelayed(new Runnable() {
#Override
public void run() {
mCallback.onAuthenticated();
}
}, SUCCESS_DELAY_MILLIS);
}
/**
* Tells if the {#link FingerprintManager#isHardwareDetected()}, {#link FingerprintManager#hasEnrolledFingerprints()},
* and {#link KeyguardManager#isDeviceSecure()}
*
* #return true if yes, false otherwise
* #throws SecurityException If the hardware is not available, or the permission are not set
*/
public boolean isFingerprintAuthAvailable() throws SecurityException {
return mFingerprintManager.isHardwareDetected()
&& mFingerprintManager.hasEnrolledFingerprints()
&& ((KeyguardManager) mIcon.getContext().getSystemService(Context.KEYGUARD_SERVICE)).isDeviceSecure();
}
/**
* Initialize the {#link Cipher} instance with the created key in the {#link #createKey()}
* method.
*
* #return {#code true} if initialization is successful, {#code false} if the lock screen has
* been disabled or reset after the key was generated, or if a fingerprint got enrolled after
* the key was generated.
*/
private boolean initCipher() {
try {
if (mKeyStore == null) {
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
}
createKey();
mKeyStore.load(null);
SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
mCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
mCipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (NoSuchPaddingException | KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
return false;
}
}
/**
* Creates a symmetric key in the Android Key Store which can only be used after the user has
* authenticated with fingerprint.
*/
public void createKey() {
// The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
// for your flow. Use of keys is necessary if you need to know if the set of
// enrolled fingerprints has changed.
try {
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
mKeyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
mKeyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
// Require the user to authenticate with a fingerprint to authorize every use
// of the key
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
mKeyGenerator.generateKey();
} catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
}
/**
* Show an error on the UI using {#link #mIcon} and {#link #mErrorTextView}
*/
private void showError(CharSequence error) {
mIcon.setImageResource(R.drawable.ic_fingerprint_error);
mErrorTextView.setText(error);
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.warning_color, null));
mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
}
/**
* Run by {#link #showError(CharSequence)} with delay to reset the original UI after an error.
*/
Runnable mResetErrorTextRunnable = new Runnable() {
#Override
public void run() {
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.hint_color, null));
mErrorTextView.setText(
mErrorTextView.getResources().getString(R.string.pin_code_fingerprint_text));
mIcon.setImageResource(R.drawable.ic_fp_40px);
}
};
/**
* The interface used to call the original Activity/Fragment... that uses this helper.
*/
public interface Callback {
void onAuthenticated();
void onError();
}
I developed a program with fully functioning speech recognition based off an example on the Android website.
I've made no changes to the code and it just suddenly stopped working. It starts to listen (you can hear the noise), then instantly it stops (you hear the ending noise).
Has anyone else had this problem or have any idea how I can resolve it? Here is the code, there are no errors being output when it runs, just the listener stops almost immediately as it starts listening.
/**
* This method is called when the Speech Recognizer starts to listen for speech input
*/
#Override
public void onBeginningOfSpeech() {
Log.i("SRL", "onBeginningOfSpeech");
}
#Override
public void onBufferReceived(byte[] buffer) {
Log.i("SRL", "onBufferReceived: " + buffer);
}
/**
* This method is called after the speech input has been completed.
*/
#Override
public void onEndOfSpeech() {
Log.i("SRL", "onEndOfSpeech");
}
/**
* This method is called if there has been an error during speech input
* #param errorCode
*/
#Override
public void onError(int errorCode) {
String errorMessage = getErrorText(errorCode);
Log.d("SRL", "FAILED " + errorMessage);
m_speech = SpeechRecognizer.createSpeechRecognizer(this);
m_speech.setRecognitionListener(this);
m_speech.startListening(getIntent());
}
#Override
public void onEvent(int arg0, Bundle arg1) {
Log.i("SRL", "onEvent");
}
/**
* This method is called if the speech recognizer thinks only partial speech was
* input/recognized
* #param arg0
*/
#Override
public void onPartialResults(Bundle arg0) {
Log.i("SRL", "onPartialResults");
}
/**
* This method is called when the speech recognizer is ready for input
* #param arg0
*/
#Override
public void onReadyForSpeech(Bundle arg0) {
Log.i("SRL", "onReadyForSpeech");
}
/**
* This method is called when the speech recognizer has recieved input and recognized it.
* It updates the recognized speech text view on the screen to show users what they have input.
* #param results the text that has been input
*/
#Override
public void onResults(Bundle results) {
recognizedSpeech = (TextView) findViewById(R.id.recognizedSpeech);
Log.i("SRL", "onResults");
ArrayList<String> matches = results
.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
String text = "";
for (String result : matches)
text += result + "\n";
recognizedSpeech.setText(text);
if (recognizedSpeech.getText().toString().contains("yes")) {
PropertySquare square = (PropertySquare) (m_board.getSquare(
players.get(m_currentTurn).getCurrentPosition()));
square.setOwnedBy(players.get(m_currentTurn).getName());
convertTextToSpeech("You now own" + square.getName());
Log.d("buyProperty yes", square.getOwnedBy());
if(manageFunds) {
players.get(m_currentTurn).subtractMoney(square.getPrice());
Log.d("buyProperty yes", players.get(m_currentTurn).getName() +
String.valueOf(players.get(m_currentTurn).getMoney()));
funds.setText(String.valueOf(players.get(m_currentTurn).getMoney()));
}
}
nextTurnRoll();
}
#Override
public void onRmsChanged(float rmsdB) {
Log.i("SRL", "onRmsChanged: " + rmsdB);
}
/**
* This method returns what error was caused if the speech recgonizer throws an error code
* #param errorCode int representing the error thrown
* #return log message including error details
*/
public static String getErrorText(int errorCode) {
String message;
switch (errorCode) {
case SpeechRecognizer.ERROR_AUDIO:
message = "Audio recording error";
break;
case SpeechRecognizer.ERROR_CLIENT:
message = "Client side error";
break;
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
message = "Insufficient permissions";
break;
case SpeechRecognizer.ERROR_NETWORK:
message = "Network error";
break;
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
message = "Network timeout";
break;
case SpeechRecognizer.ERROR_NO_MATCH:
message = "No match";
break;
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
message = "RecognitionService busy";
break;
case SpeechRecognizer.ERROR_SERVER:
message = "error from server";
break;
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
message = "No speech input";
break;
default:
message = "Didn't understand, please try again.";
break;
}
return message;
}
UPDATE:
Looking through the application logs I have found that onRMSChanged is continuously running. Does this mean that the speech recognition is continuously running too, therefore causing my application to not pick up any speech?
I solved my instant NO_MATCH error by removing the offline preferred parameter that I had enabled on the intent.
//Intent.PutExtra(RecognizerIntent.ExtraPreferOffline, true);
It seems that the resolution was to ensure that the device was connected to the internet. I think that the TTS engine's need to connect to the correct internet host when changed and therefore would not listen if there was no internet connection.
I am trying to upload an audio file using quickblox api. I am able to upload audio attachment in my Nexus 7. The issue is, when I try to upload audio files using the same code, in other available devices (Asus zenphone and Karbonn Android one) , I am getting quickblox response exception. The exception is "content_type is too short (minimum is 5 characters),content_type is invalid". Please someone help me with this exception. I am getting exception in the following block.
/**
* This method will invoke when user try to upload a file in the chatbox.
*
* #param dialogId
* #param inputFile
* #param messageId
* #return
* #throws Exception
*
**/
public QBFile loadAttachFile(String dialogId, final File inputFile, final String messageId) throws Exception {
QBFile file = null;
try {
file = QBContent.uploadFileTask(inputFile, true, (String) null,
new QBProgressCallback() {
#Override
public void onProgressUpdate(int arg0) {
// TODO Auto-generated method stub
if (!lastUpdatedProgress.contains(arg0)) {
lastUpdatedProgress.add(arg0);
//Here we will update the progress of the progressbar details
updateAttachmentUploadingProgress(messageId, arg0);
}
}
});
} catch (QBResponseException exc) {
throw new Exception(context.getString(R.string.dlg_fail_upload_attach) );
}
return file;
}
This was an issue with 'acc' file format and 'android.webkit.MimeTypeMap' class
QuickBlox has released the Android SDK 2.3 version with the fix
http://quickblox.com/developers/Android#Framework_changelog
Check it out
I am working on an Android app that records video and allows the user to upload it directly to YouTube using the YouTube Data API v3.
I have set up my app in Google's API console. Under services, I have YouTube Data API v3 enabled. Under API access I have both a section "Client ID for installed applications" (including a Client ID and Client Secret) and a section "Simple API Access" -> "Key for Android apps (with certificates)" (which includes an API key and an "Android Apps" section, which is left blank for now, i.e. allow all Android apps, but I have tried it with setting my android key).
I have based my code from a number of places, primarily:
https://developers.google.com/youtube/v3/code_samples/java#upload_a_video
and
https://code.google.com/p/google-api-java-client/source/browse/tasks-android-sample/src/main/java/com/google/api/services/samples/tasks/android/TasksSample.java?repo=samples
The upload initialises OK, starts the AsyncTask, but then I get an IOException thrown saying:
{
"code": 403,
"errors": [
{
"domain": "usageLimits",
"message": "Access Not Configured",
"reason": "accessNotConfigured"
}
],
"message": "Access Not Configured"
}
Similar SO posts suggest it is to do with my Google API console settings, but I can't find anything wrong. Any suggestions? I wonder if it is because I am not providing my client ID or secret anywhere ...
Thanks.
My code runs from a fragment containing a list of videos. The relevant sections are:
-- Init
public class UploadFragment extends Fragment {
private static GoogleAccountCredential credential;
private static final HttpTransport transport = AndroidHttp.newCompatibleTransport();
private static final JsonFactory jsonFactory = new GsonFactory();
public YouTube youtube;
List<String> scopes = Lists.newArrayList(YouTubeScopes.YOUTUBE_UPLOAD);
private static String VIDEO_FILE_FORMAT = "video/*";
static final int REQUEST_GOOGLE_PLAY_SERVICES = 0;
static final int REQUEST_AUTHORIZATION = 1;
static final int REQUEST_ACCOUNT_PICKER = 2;
-- Setup credential and youtube
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
credential = googleAccountCredential(scopes);
youtube = new YouTube.Builder(transport, jsonFactory, credential)
.setApplicationName("MyAppName")
.build();
...
}
-- On button click, initiate upload
#Override void onClick(View v) {
...
if (hasGooglePlayServices()) {
uploadYouTubeVideos();
...
}
-- Build credential
/**
* Get the credential to authorize the installed application to access user's protected data.
*
* #param scopes list of scopes needed to run YouTube upload.
*/
private static GoogleAccountCredential googleAccountCredential(List<String> scopes) throws Exception {
credential = GoogleAccountCredential.usingOAuth2(context, scopes)
.setSelectedAccountName(PreferenceManager.getAccountName());
return credential;
}
-- Request an account from the user
/**
* Fire intent to get user to choose account
* Return to onActivityResult
*/
private void chooseAccount() {
startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
-- On return from the user choosing and account
-- / requesting authorization
/**
* Returns from chooseAccount and from request authorization
*/
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_AUTHORIZATION:
if (resultCode == Activity.RESULT_OK) {
uploadYouTubeVideos();
} else {
chooseAccount();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) {
String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
credential.setSelectedAccountName(accountName);
PreferenceManager.setAccountName(accountName);
uploadYouTubeVideos();
}
}
break;
}
}
-- Called multiple times depending on what information we have
-- account, authorization, etc.
/**
* Uploads user selected video to the user's YouTube account using OAuth2
* for authentication.
*
* #param videoFile file to be uploaded
*/
public void uploadYouTubeVideos() {
if (credential.getSelectedAccountName() == null) {
chooseAccount();
} else {
File videoFile = getVideoFile();
Insert videoInsert = prepareUpload(videoFile);
new VideoUploadAsyncTask().execute(videoInsert);
}
}
-- Prepare the upload
-- Puts everything together
/**
* Prepare upload. Just leaves execute to be run in AsyncTask.
*
* #param videoFile file to be uploaded
* #return
*/
public Insert prepareUpload( File videoFile ) {
try {
// Add extra information to the video before uploading.
Video videoObjectDefiningMetadata = new Video();
// Set the video to public (default).
VideoStatus status = new VideoStatus();
status.setPrivacyStatus("public");
videoObjectDefiningMetadata.setStatus(status);
// We set a majority of the metadata with the VideoSnippet object.
VideoSnippet snippet = new VideoSnippet();
// Video file name.
snippet.setTitle(videoFile.getName());
snippet.setDescription("Test description");
// Set keywords.
List<String> tags = new ArrayList<String>();
tags.add("test");
snippet.setTags(tags);
// Set completed snippet to the video object.
videoObjectDefiningMetadata.setSnippet(snippet);
InputStreamContent mediaContent = new InputStreamContent(
VIDEO_FILE_FORMAT, new BufferedInputStream(new FileInputStream(videoFile)));
mediaContent.setLength(videoFile.length());
/*
* The upload command includes: 1. Information we want returned after file is successfully
* uploaded. 2. Metadata we want associated with the uploaded video. 3. Video file itself.
*/
YouTube.Videos.Insert videoInsert = youtube.videos()
.insert("snippet,statistics,status", videoObjectDefiningMetadata, mediaContent);
// Set the upload type and add event listener.
MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();
/*
* Sets whether direct media upload is enabled or disabled. True = whole media content is
* uploaded in a single request. False (default) = resumable media upload protocol to upload
* in data chunks.
*/
uploader.setDirectUploadEnabled(false);
MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
public void progressChanged(MediaHttpUploader uploader) throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
Log.d(TAG, "Upload file: Initiation Started");
break;
case INITIATION_COMPLETE:
Log.d(TAG, "Upload file: Initiation Completed");
break;
case MEDIA_IN_PROGRESS:
Log.d(TAG, "Upload file: Upload in progress");
Log.d(TAG, "Upload file: Upload percentage: " + uploader.getProgress());
break;
case MEDIA_COMPLETE:
Log.d(TAG, "Upload file: Upload Completed!");
break;
case NOT_STARTED:
Log.d(TAG, "Upload file: Upload Not Started!");
break;
}
}
};
uploader.setProgressListener(progressListener);
return videoInsert;
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found: " + e.getMessage());
return null;
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.getMessage());
return null;
}
}
-- Require Google play services
/**
* Pop up dialog requesting user to download Google Play Services.
* Returns to onActivityResult
*/
void showGooglePlayServicesAvailabilityErrorDialog(final int connectionStatusCode) {
getActivity().runOnUiThread(new Runnable() {
public void run() {
Dialog dialog =
GooglePlayServicesUtil.getErrorDialog(connectionStatusCode, getActivity(),
REQUEST_GOOGLE_PLAY_SERVICES);
dialog.show();
}
});
}
-- AsyncTask that runs execute on the upload
public class VideoUploadAsyncTask extends AsyncTask<Insert, Void, Void> {
#Override
protected Void doInBackground( Insert... inserts ) {
Insert videoInsert = inserts[0];
try {
Video returnVideo = videoInsert.execute();
} catch (final GooglePlayServicesAvailabilityIOException availabilityException) {
showGooglePlayServicesAvailabilityErrorDialog(
availabilityException.getConnectionStatusCode());
} catch (UserRecoverableAuthIOException userRecoverableException) {
startActivityForResult(
userRecoverableException.getIntent(), UploadFragment.REQUEST_AUTHORIZATION);
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.getMessage());
}
return null;
}
}
}
The answer provided by #Ibrahim was almost correct for me. What I needed to do was edit my API configuration. However, it was not the "Simple API access" section I needed to edit, it was the settings after clicking the button "Create another client Id".
Then I could select "Installed application" -> "Android". After inputting my package name and SHA1, and waiting 15 minutes, my app worked as expect. I also have the "Simple API access" set up. I am not sure if you need both or not.
Yes, YouTube Direct Lite for Android is similar. You have to configure simple API access with your SHA1 key. Explains here.
Everything in the code seems fine. I do not understand why it is giving me this error.
Using Eclipse IDE (Juno), I've clicked "Run" and got the following message on the console:
Error: Could not find or load main class JavaFixHistoryMiner
This was an imported file, I also added an external library
/**
* Example of how to request and process historical rate data from the Java API
*
* #author rkichenama
*/
public class JavaFixHistoryMiner
implements IGenericMessageListener, IStatusMessageListener
{
private static final String server = "http://www.fxcorporate.com/Hosts.jsp";
private static final String TEST_CURRENCY = "EUR/USD";
private FXCMLoginProperties login;
private IGateway gateway;
private String currentRequest;
private boolean requestComplete;
private ArrayList<CollateralReport> accounts = new ArrayList<CollateralReport>();
private HashMap<UTCDate, MarketDataSnapshot> historicalRates = new HashMap<UTCDate, MarketDataSnapshot>();
private static PrintWriter output = new PrintWriter((OutputStream)System.out, true);
public PrintWriter getOutput() { return output; }
public void setOutput(PrintWriter newOutput) { output = newOutput; }
/**
* Creates a new JavaFixHistoryMiner with credentials with configuration file
*
* #param username
* #param password
* #param terminal - which terminal to login into, dependent on the type of account, case sensitive
* #param server - url, like 'http://www.fxcorporate.com/Hosts.jsp'
* #param file - a local file used to define configuration
*/
public JavaFixHistoryMiner(String username, String password, String terminal, String file)
{
// if file is not specified
if(file == null)
// create a local LoginProperty
this.login = new FXCMLoginProperties(username, password, terminal, server);
else
this.login = new FXCMLoginProperties(username, password, terminal, server, file);
}
/**
* Creates a new JavaFixHistoryMiner with credentials and no configuration file
*
* #param username
* #param password
* #param terminal - which terminal to login into, dependent on the type of account, case sensitive
* #param server - url, like 'http://www.fxcorporate.com/Hosts.jsp'
*/
public JavaFixHistoryMiner(String username, String password, String terminal)
{
// call the proper constructor
this(username, password, terminal, null);
}
public JavaFixHistoryMiner(String[] args)
{
// call the proper constructor
this(args[0], args[1], args[2], null);
}
/**
* Attempt to login with credentials supplied in constructor, assigning self as listeners
*/
public boolean login()
{
return this.login(this, this);
}
/**
* Attempt to login with credentials supplied in constructor
*
* #param genericMessageListener - the listener object for trading events
* #param statusMessageListener - the listener object for status events
*
* #return true if login successful, false if not
*/
public boolean login(IGenericMessageListener genericMessageListener, IStatusMessageListener statusMessageListener)
{
try
{
// if the gateway has not been defined
if(gateway == null)
// assign it to a new gateway created by the factory
gateway = GatewayFactory.createGateway();
// register the generic message listener with the gateway
gateway.registerGenericMessageListener(genericMessageListener);
// register the status message listener with the gateway
gateway.registerStatusMessageListener(statusMessageListener);
// if the gateway has not been connected
if(!gateway.isConnected())
{
// attempt to login with the local login properties
gateway.login(this.login);
}
else
{
// attempt to re-login to the api
gateway.relogin();
}
// set the state of the request to be incomplete
requestComplete = false;
// request the current trading session status
currentRequest = gateway.requestTradingSessionStatus();
// wait until the request is complete
while(!requestComplete) {}
// return that this process was successful
return true;
}
catch(Exception e) { e.printStackTrace(); }
// if any error occurred, then return that this process failed
return false;
}
/**
* Attempt to logout, assuming that the supplied listeners reference self
*/
public void logout()
{
this.logout(this, this);
}
/**
* Attempt to logout, removing the supplied listeners prior to disconnection
*
* #param genericMessageListener - the listener object for trading events
* #param statusMessageListener - the listener object for status events
*/
public void logout(IGenericMessageListener genericMessageListener, IStatusMessageListener statusMessageListener)
{
// attempt to logout of the api
gateway.logout();
// remove the generic message listener, stop listening to updates
gateway.removeGenericMessageListener(genericMessageListener);
// remove the status message listener, stop listening to status changes
gateway.removeStatusMessageListener(statusMessageListener);
}
/**
* Request a refresh of the collateral reports under the current login
*/
public void retrieveAccounts()
{
// if the gateway is null then attempt to login
if(gateway == null) this.login();
// set the state of the request to be incomplete
requestComplete = false;
// request the refresh of all collateral reports
currentRequest = gateway.requestAccounts();
// wait until all the reqports have been processed
while(!requestComplete) {}
}
/**
* Send a fully formed order to the API and wait for the response.
*
* #return the market order number of placed trade, NONE if the trade did not execute, null on error
*/
public String sendRequest(ITransportable request)
{
try
{
// set the completion status of the requst to false
requestComplete = false;
// send the request message to the api
currentRequest = gateway.sendMessage(request);
// wait until the api answers on this particular request
// while(!requestComplete) {}
// if there is a value to return, it will be passed by currentResult
return currentRequest;
}
catch(Exception e) { e.printStackTrace(); }
// if an error occured, return no result
return null;
}
/**
* Implementing IStatusMessageListener to capture and process messages sent back from API
*
* #param status - status message received by API
*/
#Override public void messageArrived(ISessionStatus status)
{
// check to the status code
if(status.getStatusCode() == ISessionStatus.STATUSCODE_ERROR ||
status.getStatusCode() == ISessionStatus.STATUSCODE_DISCONNECTING ||
status.getStatusCode() == ISessionStatus.STATUSCODE_CONNECTING ||
status.getStatusCode() == ISessionStatus.STATUSCODE_CONNECTED ||
status.getStatusCode() == ISessionStatus.STATUSCODE_CRITICAL_ERROR ||
status.getStatusCode() == ISessionStatus.STATUSCODE_EXPIRED ||
status.getStatusCode() == ISessionStatus.STATUSCODE_LOGGINGIN ||
status.getStatusCode() == ISessionStatus.STATUSCODE_LOGGEDIN ||
status.getStatusCode() == ISessionStatus.STATUSCODE_PROCESSING ||
status.getStatusCode() == ISessionStatus.STATUSCODE_DISCONNECTED)
{
// display status message
output.println("\t\t" + status.getStatusMessage());
}
}
/**
* Implementing IGenericMessageListener to capture and process messages sent back from API
*
* #param message - message received for processing by API
*/
#Override public void messageArrived(ITransportable message)
{
// decide which child function to send an cast instance of the message
try
{
// if it is an instance of CollateralReport, process the collateral report
if(message instanceof CollateralReport) messageArrived((CollateralReport)message);
// if it is an instance of MarketDataSnapshot, process the historical data
if(message instanceof MarketDataSnapshot) messageArrived((MarketDataSnapshot)message);
// if it is an instance of MarketDataRequestReject, process the historical data request error
if(message instanceof MarketDataRequestReject) messageArrived((MarketDataRequestReject)message);
// if the message is an instance of TradingSessionStatus, cast it and send to child function
else if(message instanceof TradingSessionStatus) messageArrived((TradingSessionStatus)message);
}
catch(Exception e) { e.printStackTrace(output); }
}
/**
* Separate function to handle collateral report requests
*
* #param cr - message interpreted as an instance of CollateralReport
*/
public void messageArrived(CollateralReport cr)
{
// if this report is the result of a direct request by a waiting process
if(currentRequest.equals(cr.getRequestID()) && !accounts.contains(cr))
{
// add the trading account to the account list
accounts.add(cr);
// set the state of the request to be completed only if this is the last collateral report
// requested
requestComplete = cr.isLastRptRequested();
}
}
/**
/**
* Separate function to handle the trading session status updates and pull the trading instruments
*
* #param tss - the message interpreted as a TradingSessionStatus instance
*/
public void messageArrived(TradingSessionStatus tss)
{
// check to see if there is a request from main application for a session update
if(currentRequest.equals(tss.getRequestID()))
{
// set that the request is complete for any waiting thread
requestComplete = true;
// attempt to set up the historical market data request
try
{
// create a new market data request
MarketDataRequest mdr = new MarketDataRequest();
// set the subscription type to ask for only a snapshot of the history
mdr.setSubscriptionRequestType(SubscriptionRequestTypeFactory.SNAPSHOT);
// request the response to be formated FXCM style
mdr.setResponseFormat(IFixDefs.MSGTYPE_FXCMRESPONSE);
// set the intervale of the data candles
mdr.setFXCMTimingInterval(FXCMTimingIntervalFactory.MIN15);
// set the type set for the data candles
mdr.setMDEntryTypeSet(MarketDataRequest.MDENTRYTYPESET_ALL);
// configure the start and end dates
Date now = new Date();
Calendar calendar = (Calendar)Calendar.getInstance().clone();
calendar.setTime(now);
calendar.add(Calendar.DAY_OF_MONTH, -1);
Date beforeNow = calendar.getTime();
// set the dates and times for the market data request
mdr.setFXCMStartDate(new UTCDate(beforeNow));
mdr.setFXCMStartTime(new UTCTimeOnly(beforeNow));
mdr.setFXCMEndDate(new UTCDate(now));
mdr.setFXCMEndTime(new UTCTimeOnly(now));
// set the instrument on which the we want the historical data
mdr.addRelatedSymbol(tss.getSecurity(TEST_CURRENCY));
// send the request
sendRequest(mdr);
}
catch(Exception e) { e.printStackTrace(); }
}
}
/**
* Separate function to handle the rejection of a market data historical snapshot
*
* #param mdrr - message interpreted as an instance of MarketDataRequestReject
*/
public void messageArrived(MarketDataRequestReject mdrr)
{
// display note consisting of the reason the request was rejected
output.println("Historical data rejected; " + mdrr.getMDReqRejReason());
// set the state of the request to be complete
requestComplete = true;
}
/**
* Separate function to handle the receipt of market data snapshots
*
* Current dealing rates are retrieved through the same class as historical requests. The difference
* is that historical requests are 'answers' to a specific request.
*
* #param mds
*/
public void messageArrived(MarketDataSnapshot mds)
{
// if the market data snapshot is part of the answer to a specific request
try
{
if(mds.getRequestID() != null && mds.getRequestID().equals(currentRequest))
{
// add that snapshot to the historicalRates table
synchronized(historicalRates) { historicalRates.put(mds.getDate(), mds); }
// set the request to be complete only if the continuous flaf is at the end
requestComplete = (mds.getFXCMContinuousFlag() == IFixDefs.FXCMCONTINUOUS_END);
}
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Display the historical rates captured
*/
public void displayHistory()
{
// give the table a header
output.println("Rate 15 minute candle History for " + TEST_CURRENCY);
// give the table column headings
output.println("Date\t Time\t\tOBid\tCBid\tHBid\tLBid");
// get the keys for the historicalRates table into a sorted list
SortedSet<UTCDate> candle = new TreeSet<UTCDate>(historicalRates.keySet());
// define a format for the dates
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss z");
// make the date formatter above convert from GMT to EST
sdf.setTimeZone(TimeZone.getTimeZone("EST"));
// go through the keys of the historicalRates table
for(int i = 0; i < candle.size(); i++)
{
// create a single instance of the snapshot
MarketDataSnapshot candleData;
synchronized(historicalRates) { candleData = historicalRates.get(candle.toArray()[i]); }
// convert the key to a Date
Date candleDate = ((UTCDate)candle.toArray()[i]).toDate();
// print out the historicalRate table data
output.println(
sdf.format(candleDate) + "\t" + // the date and time formatted and converted to EST
candleData.getBidOpen() + "\t" + // the open bid for the candle
candleData.getBidClose() + "\t" + // the close bid for the candle
candleData.getBidHigh() + "\t" + // the high bid for the candle
candleData.getBidLow()); // the low bid for the candle
}
// repeat the table column headings
output.println("Date\t Time\t\tOBid\tCBid\tHBid\tLBid");
}
public static void main(String[] args)
{
try
{
// create an instance of the JavaFixHistoryMiner
JavaFixHistoryMiner miner = new JavaFixHistoryMiner("rkichenama", "1311016", "Demo");
// login to the api
miner.login();
// retrieve the trader accounts to ensure login process is complete
miner.retrieveAccounts();
// display nore that the history display is delayed
// partially for theatrics, partially to ensure all the rates are collected
output.println("Displaying history in");
// wait ~ 2.5 seconds
for(int i = 5; i > 0; i--)
{
output.println(i + "...");
Thread.sleep(500);
}
// display the collected rates
miner.displayHistory();
// log out of the api
miner.logout();
}
catch (Exception e) { e.printStackTrace(); }
}
}