I'm trying to learn how to implement a fingerprint API.
In one of fingerprint guides, it gave me a code
#RequiresApi(api = Build.VERSION_CODES.P)
public class BiometricCallbackV28 extends BiometricPrompt.AuthenticationCallback {
private BiometricCallback biometricCallback;
public BiometricCallbackV28(BiometricCallback biometricCallback) {
this.biometricCallback = biometricCallback;
}
#Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
biometricCallback.onAuthenticationSuccessful();
}
#Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
super.onAuthenticationHelp(helpCode, helpString);
biometricCallback.onAuthenticationHelp(helpCode, helpString);
}
#Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
biometricCallback.onAuthenticationError(errorCode, errString);
}
#Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
biometricCallback.onAuthenticationFailed();
}
}
But shouldn't there be a test whether the authentication passed THEN call onAuthenticationSucceeded? I don't see anywhere that calls public void onAuthenticationSucceeded. How does it know that the fingerprint matches? Who calls the method?
But shouldn't there be a test whether the authentication passed
The system knows whether the authentication passed, by comparing the scanned fingerprint against fingerprints registered by the user
I don't see anywhere that calls public void onAuthenticationSucceeded
The framework calls all of those callback methods, based on the results from the biometric hardware.
Related
I am trying to create an authentication system for my App.
When I compile the code, Android Studio throws the following error
java.lang.NoSuchMethodError: No virtual method setDeviceCredentialAllowed(Z)Landroid/hardware/biometrics/BiometricPrompt$Builder; in class Landroid/hardware/biometrics/BiometricPrompt$Builder; or its super classes (declaration of 'android.hardware.biometrics.BiometricPrompt$Builder' appears in /system/framework/framework.jar)
I followed some other possible solutions such as Invalidating Caches and Restarting my Android Studio but it didn't solve the issue
I am using
androidx.biometric:biometric:1.0.0-alpha03
KeyguardManager keyguardManager = (KeyguardManager) getApplication().getSystemService(KEYGUARD_SERVICE);
if(keyguardManager.isKeyguardSecure()){
Toast.makeText(this,"Using keyguard",Toast.LENGTH_SHORT).show();
Builder biometricPromptBuilder = new Builder(this);
biometricPromptBuilder.setTitle("Title");
biometricPromptBuilder.setSubtitle("Subtitle");
biometricPromptBuilder.setDescription("Discription");
biometricPromptBuilder.setNegativeButton("Cancel", newExecutor, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
biometricPromptBuilder.setDeviceCredentialAllowed(true);
android.hardware.biometrics.BiometricPrompt biometricPrompt = biometricPromptBuilder.build();
CancellationSignal cancellationSignal = new CancellationSignal();
biometricPrompt.authenticate(cancellationSignal, newExecutor, new android.hardware.biometrics.BiometricPrompt.AuthenticationCallback() {
#Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
}
#Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
super.onAuthenticationHelp(helpCode, helpString);
}
#Override
public void onAuthenticationSucceeded(android.hardware.biometrics.BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
}
#Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
}
});
}
}
The expected output should be a successful compilation of the App
The androidx API was released in beta01, while the framework (android.hardware.biometrics) API was released in 29.
Edit: Just re-read your post, you are using the framework's version of the API. What is your target API level? The framework's version is only available 29+. You should consider using the androidx.biometric version of the API.
I am implementing the android native fingerprint API for one of our applications.
Below is the code of the handler:
public class FingerprintHandler extends
FingerprintManager.AuthenticationCallback {
private CancellationSignal cancellationSignal;
private Context appContext;
private boolean sendCancellation=false;
public FingerprintHandler(Context context) {
appContext = context;
}
public void startAuth(FingerprintManager manager,
FingerprintManager.CryptoObject cryptoObject) {
cancellationSignal = new CancellationSignal();
if (ActivityCompat.checkSelfPermission(appContext,
Manifest.permission.USE_FINGERPRINT) !=
PackageManager.PERMISSION_GRANTED) {
return;
}
manager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
}
#Override
public void onAuthenticationError(int errMsgId,
CharSequence errString) {
Log.d(TAG,"Authentication error callback");
Log.d(TAG,"Error Value: "+errMsgId);
switch(errMsgId){
case FINGERPRINT_ERROR_LOCKOUT:
Log.d(TAG,"Fingerprint error lockout");
nativeLocked = true;
mPreferences.edit().putLong(LAST_FAILURE, System.currentTimeMillis()).apply();
showError(getString(R.string.test_bio_fingerprint_fingerprint_authentication_failed));
mCancelButton.setEnabled(true);
dialogHandler.postDelayed(new Runnable() {
#Override
public void run() {
mCancelButton.setEnabled(true);
dismissDialog();
sendFailure();
}
}, SUCCESS_OR_FAIL_DELAY_MILLIS);
break;
case FINGERPRINT_ERROR_CANCELED:
Log.d(TAG,"Fingerprint has been cancelled");
if(sendCancellation) {
dismissDialog();
if (useEye)
sendCancelForEye();
else
sendCancel();
}
break;
}
}
#Override
public void onAuthenticationHelp(int helpMsgId,
CharSequence helpString) {
Log.d(TAG,"Authentication helper callback");
retryWithError(R.string.test_bio_fingerprint_fingerprint_too_fast);
}
#Override
public void onAuthenticationFailed() {
Log.d(TAG,"Authentication failed callback");
retryWithError(R.string.test_bio_fingerprint_fingerprint_not_recognized);
}
#Override
public void onAuthenticationSucceeded(
FingerprintManager.AuthenticationResult result) {
Log.d(TAG,"Authentication successfull callback");
onAuthenticationSuccess();
}
public void stopListening(boolean sendCancellation) {
this.sendCancellation=sendCancellation;
Log.d(TAG,"stopListening called");
if (cancellationSignal != null) {
cancellationSignal.cancel();
cancellationSignal = null;
}
}
}
The problem is that when there are 5 incorrect authentication attempts I can see the below in my logs:
Authentication error callback
Error Value: 7
Fingerprint error lockout
Authentication error callback
Error Value: 7
Fingerprint error lockout
The lockout call back gets twice. I am unable to figure out why this is happening. Can someone please help.
You need to check errMsgId because first is triggered for your action callback and second time is triggered for "Fingerprint operation canceled."
Also please notice that FingerPrintManager is deprecated and for the new api for BiometricPrompt the implementation should look like example below (you need to replace BiometricConstants.ERROR_NEGATIVE_BUTTON with your action id code):
#Override
public void onAuthenticationError(int errorCode,
#NonNull CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
if (BiometricConstants.ERROR_NEGATIVE_BUTTON == errorCode) {
//do your job only once
}
}
Trying to understand how to work with this lib.
Use example from twitter examples:
Configuration configuration = new Configuration.Builder(this)
.minConsumerCount(1)
.maxConsumerCount(3)
.loadFactor(3)
.consumerKeepAlive(120)
.build();
jobManager = new JobManager(this, configuration);
jobManager.addJob(new simpleJob("simple text"));
class simpleJob extends Job{
private String text;
public simpleJob(String text) {
super(new Params(1000).requireNetwork().persist());
this.text = text;
}
#Override
public void onAdded() {
Log.i("job", "onAdded");
}
#Override
public void onRun() throws Throwable {
Log.i("job", "onRun");
}
#Override
protected void onCancel() {
Log.i("job", "onCancel");
}
#Override
protected RetryConstraint shouldReRunOnThrowable(Throwable throwable, int runCount,
int maxRunCount) {
Log.i("job", "shouldReRunOnThrowable");
return RetryConstraint.RETRY;
}
}
In console i see only "onAdded". "onRun" never happened. If i add log interceptor, i see error text "error while serializing object simpleJob".
What is it? What object should be serializable?
As i explore, serializetion does not work if job is inner class. You need to place your job in separate class.
I'm looking at retrofit for my networking layer. Is there any way to tell if a particular async request is running at any given moment?
For example, I'd like to know if a request is running so that I can update the user interface at various times. I could do this myself by keeping variables around to track state, but wondering if there's something already in the library for this.
Here is what I would normally do when needing a way to keep track of running requests:
First, using retrofit, as soon as you make the request, you can do the following:
Use EventBus library to post an event to your activity or fragment. Now, this can be done inside onSuccess() method of your Callback or onError() method of the same.
In your activity or fragment's onEvent(EventClassName event) method, you can simply check a variable like [isRunning] from your event to make sure that if the event is still running, you update the UI accordingly and if not, do what you need to do respectively.
When the request is completed, obviously isRunning will be false and you can then update the UI as expected by the user.
I am recommending EventBus here simply because it is much easier to decouple your application code with it; you can send different events that notify the activity of the different statuses of your requests and then update your UI that way.
You can find EventBus here
I hope this helps!
What I personally ended up doing in this case was that I was running the example with Retrofit, Android Priority Jobqueue (from yigit's fork) and Otto eventbus.
public enum SingletonBus {
INSTANCE;
private Bus bus;
private Handler handler = new Handler(Looper.getMainLooper());
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public <T> void postToSameThread(final T event) {
bus.post(event);
}
public <T> void postToMainThread(final T event) {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
public <T> void register(T subscriber) {
bus.register(subscriber);
}
public <T> void unregister(T subscriber) {
bus.unregister(subscriber);
}
}
public interface Interactor {
void injectWith(PresenterComponent presenterComponent);
}
public interface SendCertificateRequestInteractor
extends Interactor {
interface Listener {
void onSuccessfulEvent(SuccessfulEvent e);
void onFailureEvent(FailureEvent e);
}
class SuccessfulEvent
extends EventResult<CertificateBO> {
public SuccessfulEvent(CertificateBO certificateBO) {
super(certificateBO);
}
}
class FailureEvent
extends EventResult<Throwable> {
public FailureEvent(Throwable throwable) {
super(throwable);
}
}
void sendCertificateRequest(String username, String password);
}
Pay attention to the Job here:
public class SendCertificateRequestInteractorImpl
implements SendCertificateRequestInteractor {
private Presenter presenter;
private boolean isInjected = false;
#Inject
public JobManager jobManager;
public SendCertificateRequestInteractorImpl(Presenter presenter) {
this.presenter = presenter;
}
#Override
public void sendCertificateRequest(String username, String password) {
if(!isInjected) {
injectWith(presenter.getPresenterComponent());
isInjected = true;
}
InteractorJob interactorJob = new InteractorJob(presenter, username, password);
long jobId = jobManager.addJob(interactorJob); //this is where you can get your jobId for querying the status of the task if you want
}
#Override
public void injectWith(PresenterComponent presenterComponent) {
presenterComponent.inject(this);
}
public static class InteractorJob
extends Job {
private final static int PRIORITY = 1;
private final static String TAG = InteractorJob.class.getSimpleName();
private String username;
private String password;
#Inject
public MyService myService;
public InteractorJob(Presenter presenter, String username, String password) {
super(new Params(PRIORITY).requireNetwork());
presenter.getPresenterComponent().inject(this);
this.username = username;
this.password = password;
}
#Override
public void onAdded() {
// Job has been saved to disk.
// This is a good place to dispatch a UI event to indicate the job will eventually run.
// In this example, it would be good to update the UI with the newly posted tweet.
}
#Override
public void onRun()
throws Throwable {
String certificate = myService.getCertificate(username, password);
SingletonBus.INSTANCE.postToMainThread(new SuccessfulEvent(certificate));
}
#Override
protected void onCancel() {
// Job has exceeded retry attempts or shouldReRunOnThrowable() has returned false.
Log.e(TAG, "Cancelled job.");
}
#Override
protected boolean shouldReRunOnThrowable(Throwable throwable) {
// An error occurred in onRun.
// Return value determines whether this job should retry running (true) or abort (false).
Log.e(TAG, "Failed to execute job.", throwable);
SingletonBus.INSTANCE.postToMainThread(new FailureEvent(throwable));
return false;
}
}
}
And then
#Subscribe
#Override
public void onSuccessfulEvent(SendCertificateRequestInteractor.SuccessfulEvent e) {
String certificate = e.getResult();
//do things
}
#Subscribe
#Override
public void onFailureEvent(SendCertificateRequestInteractor.FailureEvent e) {
Throwable throwable = e.getResult();
//handle error
}
More about android priority jobqueue here.
This way, technically the async handling is referred to the job queue, while Retrofit itself is using the synchronous interface. It works well as long as you don't need to access the headers of the response. Although to be fair, I was also keeping track of whether the job was running with a boolean instead of the job manager and the id as well..
Also, I haven't figured out how to use dependency injection properly with persisted jobs; nor do I really know how they intended to make that work. Of course, it'd work if it was using the application scoped component rather than a supplied presenter scoped one, but that is irrelevant.
You'll probably need to customize this solution to your own scenario, and use only what you actually need.
I am using TapJoys offerwall to give the users a way to get "free" game coins for stuff, however, the offerwall never makes the callback when i return to my activity.
I think i have the relevant implements
public class MainActivity extends BaseGameActivity implements ITimerCallback,
TapjoyAwardPointsNotifier, TapjoyEarnedPointsNotifier, TapjoyNotifier {
and i do connect to the server.
// Enables logging to the console.
TapjoyLog.enableLogging(true);
// Connect with the Tapjoy server.
TapjoyConnect.requestTapjoyConnect(getApplicationContext(), appID, secretKey);
TapjoyConnect.getTapjoyConnectInstance().setEarnedPointsNotifier(MainActivity.this);
I call the offerwall like this
TapjoyConnect.getTapjoyConnectInstance().showOffersWithCurrencyID(
currencyID, false);
And my callback methods that never gets called
#Override
public void earnedTapPoints(int amount) {
displayText = "Earned points: " + amount;
Log.d(TAG, displayText);
gameToast(displayText);
}
#Override
public void getUpdatePoints(String currencyName, int pointTotal) {
displayText = "Earned points: " + pointTotal;
Log.d(TAG, displayText);
gameToast(displayText);
}
#Override
public void getUpdatePointsFailed(String error) {
Log.d(TAG, error);
gameToast(displayText);
}
#Override
public void getAwardPointsResponse(String s, int i) {
displayText = s + i;
Log.d(TAG, displayText);
gameToast(displayText);
}
#Override
public void getAwardPointsResponseFailed(String s) {
Log.d(TAG, s);
gameToast(s);
}
None of the toasts get shown and there is nothing in the log...
in my game I did it to make it work
#Override
protected void onResume()
{
TapjoyConnect.getTapjoyConnectInstance().getTapPoints(this);
super.onResume();
}
https://knowledge.tapjoy.com/en/integration/managed-currency
NOTE: It is best to call getTapPoints(...) on application startup and resume. The callback notifiers for SPEND and AWARD points also return the total virtual currency balance of the user, so use these to update the total amount of currency the user has.