I have implemented accounts for my app following this tutorial http://blog.udinic.com/2013/04/24/write-your-own-android-authenticator/
Every aspect of the implementation works great except when I try to remove the account with the following code:
public void logout(View view) {
// remove account and restart app
stopService(new Intent(this, ServerSync.class));
Account account = AccountManager.get(this).getAccountsByType(Constants.ACCOUNT_TYPE)[0];
final AccountManagerFuture<Bundle> future = AccountManager.get(this).removeAccount(account, this, new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
try {
future.getResult();
} catch (Exception e) {
e.printStackTrace();
} finally {
Intent intent = getBaseContext().getPackageManager()
.getLaunchIntentForPackage(getBaseContext().getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
finish();
startActivity(intent);
}
}
}, null);
}
When called this produces
NoSuchMethodError: android.accounts.AccountManager.removeAccount
and the app crashes.
I have followed the Android documentation
http://developer.android.com/reference/android/accounts/AccountManager.html
and my app has the correct permissions etc. I even tried overriding getAccountRemovalAllowed in my AccountAuthenticator, forcing it to return true.
Is there some other information missing from the Android documentation?
the version of removeAccount you are using was introduced with the api leve 22 and you are probably using it on a version of Android older than it. You should check the current version at runtime and use the deprecated version of removeAccount for devices with version of Android older than 22
removeAccount (Account account, AccountManagerCallback callback, Handler handler)
if (Build.VERSION.SDK_INT < 22) {
AccountManager.get(this).removeAccount(account, new AccountManagerCallback<Bundle>(){....}, null)
} else {
// the version you are already using
}
Related
I've seen some posts that seem similar, but they're all fairly old and slightly different. I have an app whose codebase I have not touched in a long time. It makes use of the android SpeechRecognizer Service, and has a toggle for the EXTRA_PREFER_OFFLINE intent param. Previously this was working with no issues, but since I've recently dusted it off I noticed that offline functionality immediately returns with error code 7 NO_MATCH.
I have confirmed that offline language packs are installed, and wrote some stand alone code to test SpeechRecognizer outside my larger code base.
I've not been able to find any documented solutions for the NO_MATCH error, but surely it must occur elsewhere.
For context: This was previously working last year/earlier this year (I've seen people claim this wasn't possible after 2015/17) and on Android 8
Sample code snippet:
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(getContext());
final Intent speechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_PREFER_OFFLINE, true);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en-US");
speechRecognizer.setRecognitionListener(new RecognitionListener() {
#Override
public void onReadyForSpeech(Bundle bundle) {
listening = true;
label.setText(R.string.listening_true);
}
#Override
public void onBeginningOfSpeech() {
}
#Override
public void onRmsChanged(float v) {
}
#Override
public void onBufferReceived(byte[] bytes) {
}
#Override
public void onEndOfSpeech() {
label.setText(R.string.listening_false);
listening = false;
}
#Override
public void onError(int i) {
Log.e(TAG, "Error code " + i);
label.setText(R.string.listening_false);
listening = false;
}
#Override
public void onResults(Bundle bundle) {
ArrayList<String> data = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (data != null && data.size() > 0) {
resultsTextView.setText(data.get(0));
resultsTextView.setTextColor(getResources().getColor(R.color.colorAccent));
listening = false;
}
}
#Override
public void onPartialResults(Bundle bundle) {
ArrayList<String> data = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (data != null && data.size() > 0) {
Log.i(TAG, "how many partial results? " + data.size());
resultsTextView.setText(data.get(0));
resultsTextView.setTextColor(getResources().getColor(R.color.design_default_color_primary));
Log.i(TAG, "Partial results: " + data);
}
}
#Override
public void onEvent(int i, Bundle bundle) {
}
});
view.findViewById(R.id.button_mic).setOnClickListener(view1 -> {
if (!listening) {
speechRecognizer.startListening(speechRecognizerIntent);
} else {
speechRecognizer.stopListening();
}
});
Google app's voice input could work offline previously, but unfortunately, this feature has been removed since version 11 (released in March 2020).
Thus, to use this feature, we have to keep the version of Google app on device at 10.99.8 or older.
However, on some new device released after March 2020, the pre-installed version of Google app is already 11 or 12, so it is not a simple task to downgrade it to an older version.
So we need to use adb command to downgrade it from PC. This command will try to force downgrade the app for the 'current user': adb install -r -d --user 0 google.apk
The answer is that this is no loner supported. If you need a short term fix then go to your "Google", not "Google Play Services", app and uninstall updates from the hamburger menu. This seems to downgrade a lot of libraries, including the voice recognition stuff.
A possibility might be to manually bring and load your own libraries, but I haven't tested this personally.
we implemented authentication (and signing) using the androidx support library. It works perfect on Device with Android 10 (also the DeviceCredentialUnlock) and with Biometrics (the code is little different, as we want to know if it was signed with biometrics or with device credentials).
With Android 8 and older it never calls the callback. No error message is logged, the CompletableFuture is never completed.
CompletableFuture<String> signingResult = new CompletableFuture<>();
final String title = fragmentActivity.getString(R.string.LockScreenTitle);
final String description = fragmentActivity.getString(R.string.LockScreenDescription);
final String data = "testData";
Executor executor = ContextCompat.getMainExecutor(fragmentActivity);
final BiometricPrompt.AuthenticationCallback callback = new BiometricPrompt.AuthenticationCallback() {
#Override
public void onAuthenticationError(int errorCode, #NonNull CharSequence errString) {
LOGGER.info("onAuthenticationError");
Exception exception = mapAuthenticationError(errorCode, errString);
signingResult.completeExceptionally(exception);
}
#Override
public void onAuthenticationSucceeded(#NonNull BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
try {
LOGGER.info("onAuthenticationSucceeded");
String signedData = SignInternal(data);
signingResult.complete(signedData);
} catch (SecureTokenException e) {
signingResult.completeExceptionally(e);
}
}
#Override
public void onAuthenticationFailed() {
LOGGER.info("onAuthenticationFailed");
signingResult.completeExceptionally(new SecureTokenException(SecureTokenException.ErrorCode.ABORTED_BY_USER));
}
};
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle(title)
.setDescription(description)
.setDeviceCredentialAllowed(true)
.build();
final BiometricPrompt bp = new BiometricPrompt(fragmentActivity, executor, callback);
fragmentActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
bp.authenticate(promptInfo);
}
});
try {
String signed = signingResult.get();
The keys are generated with the .setUserAuthenticationValidityDurationSeconds(5); property. the sign Method uses the authenticated for signing. But the callback is never called, so we do not come to this point (at Android 9...).
Thank you for your help!
I've recently implemented the androidx biometric lib in my app. First of all the BiometricPrompt.PromptInfo.Builder.setDeviceCredentialAllowed() is deprecated and we should now use the BiometricPrompt.PromptInfo.Builder.setAllowedAuthenticators(int) instead. The int parameter passed to that function must be one of or a combination of the enum values in BiometricManager.Authenticators:
BIOMETRIC_STRONG
BIOMETRIC_WEAK
DEVICE_CREDENTIAL
This Android Developer docs describes the different enums:
https://developer.android.com/reference/androidx/biometric/BiometricManager.Authenticators
From Android Developer docs on Biometric Authentication (I recommend reading this guide btw):
Note: The following combinations of authenticator types aren't supported on Android 10 (API level 29) and lower: DEVICE_CREDENTIAL and BIOMETRIC_STRONG | DEVICE_CREDENTIAL. To check for the presence of a PIN, pattern, or password on Android 10 and lower, use the KeyguardManager.isDeviceSecure() method.
Below is a small block of code that I use to setup my biometric prompt, hopefully this and the information above will guide you in the right direction.
val bioPromptInfoBuilder = BiometricPrompt.PromptInfo.Builder()
.setTitle(authPromptParams.promptTitle)
.setSubtitle(authPromptParams.promptSubtitle)
when {
Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> {
bioPromptInfoBuilder.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
}
kgm.isDeviceSecure -> {
bioPromptInfoBuilder.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_WEAK or
BiometricManager.Authenticators.DEVICE_CREDENTIAL)
}
else -> return null
}
Basically Android 10 and below supports different combinations of the BiometricManager.Authenticators enums.
I'm using the facebook-sdk for the facebook login button. This the facebook library I'm using:
implementation ('com.facebook.android:facebook-android-sdk:4.0.0'){
exclude group: 'com.android.support'
}
This is the crash raised by Firebase Crashlytics:
https://i.stack.imgur.com/jqArL.png
On this devices:
Galaxy S7 not rooted Android 7.0
Huawei Honor 9(STF-L09) not rooted Android 8.0
I check the code to know what is the Facebook library doing but I didn't find anything that I can do from my code to solve it. This is the method that raised the crash(LoginFragment.java line 68):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
loginClient = savedInstanceState.getParcelable(SAVED_LOGIN_CLIENT);
loginClient.setFragment(this);
} else {
loginClient = new LoginClient(this);
}
callingPackage = getActivity().getCallingActivity().getPackageName();
request = (LoginClient.Request)
getActivity().getIntent().getParcelableExtra(EXTRA_REQUEST);
loginClient.setOnCompletedListener(new LoginClient.OnCompletedListener() {
#Override
public void onCompleted(LoginClient.Result outcome) {
onLoginClientCompleted(outcome);
}
});
}
This is the getCallingActivity method that returns null from android.app.Activity.java
public ComponentName getCallingActivity() {
try {
return ActivityManager.getService().getCallingActivity(mToken);
} catch (RemoteException e) {
return null;
}
}
I cannot reproduce the error with the testing devices at the office so I only have the Crashlytics report to solve it. I 'm clueless because it seems to work for most of the devices
Any help would be amazing, thanks in advance
We are using crashlyticsDidDetectCrashDuringPreviousExecution to detect java crashes and report them to our BI systems, but our app is mostly C++ and we are using crashlytics NDK, we can't find anything similar to crashlyticsDidDetectCrashDuringPreviousExecution.
Is there any way that we can actually detect an NDK crash when the app starts?
thanks
Oded
Mike from Fabric here.
Currently, there isn't a way to do this within Fabric or the SDK for an NDK crash.
NOTE: This works on older version only (Crashlytics 2.6.7 and CrashlyticsNDK 1.1.6)
I'm also looking for a solution for this.
We currently found a partial solution. I'm not sure how good it is, it's definitely not official, plus it's asynchronic (which we're trying to overcome by looping), but it's the best solution I found and it seems like it's working
Fabric.with(this, new Crashlytics.Builder().core(core.build()).build(), new CrashlyticsNdk(), new Crashlytics());
if (!userLeft) { // our handling to fix bug, see explanation below
new Thread(new Runnable() {
#Override
public void run() {
SessionEventData crashEventData = null;
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000); // in ms
} catch (InterruptedException e) { }
crashEventData = CrashlyticsNdk.getInstance().getCrashEventData();
if (crashEventData != null)
{
// there was a crash!
// crash timestamp can be found at crashEventData.timestamp
break;
}
}
}
}).start();
}
Explaination for userLeft:
We had some bug with reporting crash for users that exited app, and this is the solution for that. We set this flag to true, and save it on the device (SharedPreferences). We do it on our main activity (which extends NativeActivity), on finish() func.
Code:
#Override
public void finish() {
// set some key such as USER_LEFT to TRUE
super.finish();
}
After that, just get that USER_LEFT value, assign it into userLeft param, and set it back to false on SharedPerferences.
Any insights about this solution?
If I create an app that depends on another app or apps (eg: the Facebook and Twitter apps), yet they are not installed, is there a method of checking for those dependencies and installing them at the same time as my own app?
I did this in my application which requires the zxing scanner app to be installed.
You will want this inside your onclick or ontouch:
try{
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.setPackage("com.google.zxing.client.android");
startActivityForResult(intent, 0);
} catch (Exception e) {
createAlert("Barcode Scanner not installed!", "This application uses " +
"the open source barcode scanner by ZXing Team, you need to install " +
"this before you can use this software!", true);
}
which calls
public void createAlert(String title, String message, Boolean button) {
// http://androidideasblog.blogspot.com/2010/02/how-to-add-messagebox-in-android.html
AlertDialog alertDialog;
alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
if ((button == true)) {
alertDialog.setButton("Download Now",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
Intent browserIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("market://search?q=pname:com.google.zxing.client.android"));
startActivity(browserIntent);
}
});
}
alertDialog.show();
}
Then after sorting out all that code out I realise you asked for it to be installed at the same time as your app. Not sure if i should post this code, but it may be helpful
Short answer: No, you cannot automatically install other applications as dependencies.
Longer answer:
Android Market does not let you declare other applications to install as a dependency. As a system, Market appears to be designed for single application installs -- not Linux distro style mega dependency graphs.
At runtime, you can test for installed apps and punt your user over to the Market if so. See the techniques suggested by #QuickNick (testing if an app is installed) and #TerryProbert (punting to market) if that's what you want.
Your best bet is probably to design your app to gracefully degrade if dependencies are not available, and suggest (or insist) that they head over to market to install them.
Start from this:
Intent mediaIntent = new Intent("com.example.intent.action.NAME");
// add needed categories
List<ResolveInfo> listResolveInfo = getPackageManager().queryIntentServices(mediaIntent, 0);
if (listResolveInfo.size() != 0) {
//normal behavior
} else {
//install what you need
}
I give you example of querying services. If you want to check activities, then you will call queryIntentActivities().
I think following the pattern outlined in this post on the Android Developer Blog will help you.
http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
As TerryProbert points out if you know that the Intent is not available prompt the user to install the missing app.
Here's what I use to return the first mission activity that exists:
try {
Class<?> missionClass = Class.forName(mPackageName+".Mission"+mission);
Method missionDescription;
missionDescription = missionClass.getMethod("missionDescription");
mMissionDescription = (String) missionDescription.invoke(null);
if (mMissionDescription.length() > 0) {
nextMission = mission;
break;
}
} catch (Exception e) {
//DEBUG*/Log.v(this.getClass().getName(), "onResume: Mission no "+mission+" not found: "+e.getMessage());
}
Each mission is held in a separate class, derived from a Mission base class. Derived classes are called Mission1, Mission24 etc.
Not all missions are defined.
The base class has an abstract class missionDescription which returns a string describing the mission.
This code is inside a loop so tests mission=1 to 99, trying to call missionDescription. It returns when the Description for the first mission found is returned.