I'm trying to implement the LVL (Licensing Verification Library for Android), but running into a problem when the license check fails. I pop up a Dialog which explains that the license check failed and presents them with two buttons. One button is "Retry" which I want to immediately do another check or "Purchase App" which redirects them to buy the app on the App Market.
When I tap the Retry button, I can see from logging messages that my licensing check is called, I receive another "dont allow" callback and I try to show the above failure dialog again (I see onPrepareDialog() get called again).
The problem is the second dialog doesn't actually show. So the user can use the app even though the license check failed. Why isn't the dialog popping up again and again?
I believe this is all of the relevant code:
private void checkLicense() {
Log.d(TAG, "Checking License...");
this.licenseChecker.checkAccess(this.licenseCheckerCallback);
}
private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow() { }
public void dontAllow() {
Log.d(TAG, "License resposne: Don't Allow");
if (isFinishing())
return; // Don't update UI if Activity is finishing.
// Should not allow access. In most cases, the app should assume
// the user has access unless it encounters this. If it does,
// the app should inform the user of their unlicensed ways
// and then either shut down the app or limit the user to a
// restricted set of features.
// In this example, we show a dialog that takes the user to Market.
Log.d(TAG, "Showing No License Dialog");
showDialog(DIALOG_NO_LICENSE_ID);
}
public void applicationError(ApplicationErrorCode errorCode) {
if (isFinishing())
return; // Don't update UI if Activity is finishing.
// This is a polite way of saying the developer made a mistake
// while setting up or calling the license checker library.
showDialog(DIALOG_APPLICATION_ERROR_ID);
}
}
protected Dialog onCreateDialog(int id) {
Log.d(TAG, "Creating Dialog "+id);
switch(id) {
case DIALOG_NO_LICENSE_ID:
return this.makeRetryPurchaseDialog(getString(R.string.no_license));
case DIALOG_APPLICATION_ERROR_ID:
return this.makeRetryPurchaseDialog(this.applicationErrorMessageForDialog);
}
return null;
}
private Dialog makeRetryPurchaseDialog(String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(CalculatorTabActivity.this);
builder.setMessage(message).setCancelable(false)
.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
CalculatorTabActivity.this.checkLicense();
}
}).setNegativeButton("Purchase App", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
CalculatorTabActivity.this.openAppMarket();
}
});
return builder.create();
}
Just as an idea, try to explicitly call for dialog dismissing:
...
.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dissmissDialog(DIALOG_NO_LICENSE_ID);
CalculatorTabActivity.this.checkLicense();
}
})
Also, if that will not work, try using removeDialog(DIALOG_NO_LICENSE_ID) instead of dissmissDialog(DIALOG_NO_LICENSE_ID).
Finally what code (if at all) do you have in onPrepareDialog() ?
Related
Similar questions to this have been trivial at best for answers. I am using a faily up-to-date Google Play Game Services apk 'com.google.android.gms:play-services:7.0.0' and I have onInvitationReceived() and onConnected() implemented.
However, onInvitationReceived() doesn't seem to be called when the invitee accepts the game invite in-game. And while I'm fairly certain onConnected() is called no matter what when the player connects via callback after mGoogleClient.connect() , it seems like the invitation is dropped or something because the player is not redirected to the game screen as I specified if the Bundle contains an invitation (I'm assuming this is called when the app is closed but either way, only a status bar notification is shown for the invite).
Any insight into this would be appreciated. Here are the relevant methods:
onConnected() is from a class, that extends AndroidGame, and AndroidGame extends Activity (I've tried it in both classes, now I just have it overridden in the child class of AndroidGame):
#Override
public void onConnected(Bundle connectionHint) {
// Connected to Google Play services!
// The good stuff goes here.
Games.Invitations.registerInvitationListener(mGoogleClient, Network.getInstance().mListener);
if (connectionHint != null) {
Invitation inv =
connectionHint.getParcelable(Multiplayer.EXTRA_INVITATION);
if (inv != null) {
// accept invitation
RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(Network.getInstance());
roomConfigBuilder.setMessageReceivedListener(Network.getInstance());
roomConfigBuilder.setRoomStatusUpdateListener(Network.getInstance());
roomConfigBuilder.setInvitationIdToAccept(inv.getInvitationId());
RoomConfig roomConfig = roomConfigBuilder.build();
Games.RealTimeMultiplayer.join(mGoogleClient, roomConfig);
// prevent screen from sleeping during handshake
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// go to game screen
setScreen(new CharSelectScreen(this));
}
}
}
onInvitationReceived is from my Network class, which is basically a singleton which handles other listeners as well. onInvitationReceived() is both overridden in the Network class and in the anonymous inner class, just to cover my assets:
#Override
public void onInvitationReceived(Invitation invitation) {
Intent intent = Games.Invitations.getInvitationInboxIntent(game.getGoogleClient());
game.startActivityForResult(intent, RC_INVITATION_INBOX);
mIncomingInvitationId = invitation.getInvitationId();
AlertDialog.Builder builder = new AlertDialog.Builder(game.getApplicationContext());
builder.setMessage("Join Game?")
.setTitle("Bit Game Invitation!");
builder.setPositiveButton("Join", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if(!game.getGoogleClient().isConnected()) {
game.getGoogleClient().connect();
}
// User clicked OK button
Bundle am = RoomConfig.createAutoMatchCriteria(1, 3, 0);
RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(Network.getInstance());
roomConfigBuilder.setMessageReceivedListener(Network.getInstance());
roomConfigBuilder.setRoomStatusUpdateListener(Network.getInstance());
roomConfigBuilder.setAutoMatchCriteria(am);
roomConfigBuilder.setInvitationIdToAccept(mIncomingInvitationId);
RoomConfig roomConfig = roomConfigBuilder.build();
Games.RealTimeMultiplayer.join(game.getGoogleClient(), roomConfig);
// prevent screen from sleeping during handshake
game.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
dialog.dismiss();
// go to game screen
game.setScreen(new CharSelectScreen(game));
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
I solved it, with the short answer being I did not follow the Android Realtime Multiplayer recipe (guidelines) as closely as I should have.
The guidelines calls for easy access to Google Play sign-in, either automatically when starting the app or I'd imagine preferably, a button to sign in. When I added this, my callback onConnected() code worked, the invitation allowed the user to proceed to the designated game screen with a default waiting room pop-up (implementation shown here). I am still debugging the onInvitationReceived() callback, but I suspect that when the invitation is received when the game is open, the game crash means there is something wrong with my implementation.
All in all, I think it was a bit of misunderstanding on my end, the documentation is fairly intuitive. Thank you for helping, and I hope this will help future Google Play Game Services developers.
I am using Snackbar in android and I have implemented an action so that user can undo the action (the action is clearing all the items in the listview).Removing and adding the items back to the listview has already been done and working fine.
My issue is that, items are stored in sqlite database and how can I delete the items from tables? (How can I know that the user has not clicked the undo button, so that I can completely remove the data from database).
This is the code inside OnOptionsItemSelcted()
case R.id.action_clear:
final List<Word> temp = new ArrayList<Word>(data);
data.clear();
adapter.notifyDataSetChanged();
View view = findViewById(R.id.layoutFavWords);
Snackbar.make(view,"Deleted Saved Selection.", Snackbar.LENGTH_LONG).
setAction("Undo", new OnClickListener() {
#Override
public void onClick(View v) {
for(Word word:temp)
data.add(word);
adapter.notifyDataSetChanged();
}
}).show();
break;
So if the user has not clicked the undo button during the visible period of the snackbar, then I need to permanently delete the data from database.
Any solutions for this?
As far as I know, it is by design. You should:
Delete the item as soon as the user taps the delete button;
Store it temporarily in a class variable;
If the user taps Undo, add the item again to the database.
This approach is safer and more robust; you shouldn't wait for the snackbar to be dismissed, because that action could not even happen. Just think of user force-quitting the app while the snackbar is still on: should the item be deleted or not? It should.
A more trustworthy source is g+ post by Ian Lake (deleted because of G+ deprecation). In the comments you can read:
you want your UI to react immediately (not wait for the snackbar to
disappear) - most systems (particularly those that sync to an external
server) have the concept of a 'soft delete' where things are marked as
deleted. In those cases, an undo action would just be unmarking the
record as deleted. This system works even if the user were to leave
the app before the snackbar finishes (you can't assume the snackbar
will always complete its animation!).
The easiest way to do that is to temporarily save the record elsewhere (even a local
variable), then re-insert it if they happen to hit the undo button.
Android Support library v23 added Snackbar.Callback which you can use to listen if the snackbar was dismissed by user or timeout.
Example borrowed from astinxs post:
Snackbar.make(getView(), "Hi there!", Snackbar.LENGTH_LONG).setCallback( new Snackbar.Callback() {
#Override
public void onDismissed(Snackbar snackbar, int event) {
switch(event) {
case Snackbar.Callback.DISMISS_EVENT_ACTION:
Toast.makeText(getActivity(), "Clicked the action", Toast.LENGTH_LONG).show();
break;
case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
Toast.makeText(getActivity(), "Time out", Toast.LENGTH_LONG).show();
break;
}
}
#Override
public void onShown(Snackbar snackbar) {
Toast.makeText(getActivity(), "This is my annoying step-brother", Toast.LENGTH_LONG).show();
}
}).setAction("Go away!", new View.OnClickListener() {
#Override
public void onClick(View v) {
}
}).show();
Example:
final java.util.Timer timer = new Timer();
Snackbar snackbar = Snackbar.make(...).setAction("Undo", new OnClickListener() {
#Override
public void onClick(View v) {
timer.cancel();
for(Word word:temp)
data.add(word);
adapter.notifyDataSetChanged();
}
}).show();
timer.schedule(new TimerTask() {
public void run() {
// delete from db
}
}, snackbar.getDuration());
It may be a good idea to add a little to the snackbar.getDuration() time (100-200ms?) as timers are not very exact in terms of timing and this way they may get called just before the snackbar is about to close, althought the possibility is rather small in this case.
If you don't want to delete the record from database immediately, try this:
// Backup the item for undo
int itemIndex = viewHolder.getAdapterPosition();
Item item = adapter.getItem(itemIndex);
// Delete only from the adapter
adapter.removeItem(itemIndex);
Snackbar.make(getView(), "Item deleted", LENGTH_LONG)
.addCallback(new BaseCallback<Snackbar>() {
public void onDismissed(Snackbar transientBottomBar, int event) {
if (event != DISMISS_EVENT_ACTION) {
// Dismiss wasn't because of tapping "UNDO"
// so here delete the item from databse
}
}
})
.setAction("UNDO", v -> adapter.addItem(item, itemIndex))
.show();
My way is to hv "deleted" column that is boolean, just change the stat to be true if deleted then undo to change back stat to be false, also u maybe want trash controller or scheduler to delete all the false values on every week.
#Override
public void run() {
//Create thread that can alter the UI
AlarmPage.this.runOnUiThread(new Runnable() {
public void run() {
cal = Calendar.getInstance();
//See if current time matches set alarm time
if((cal.get(Calendar.HOUR_OF_DAY) == alarmTime.getCurrentHour())
&& (cal.get(Calendar.MINUTE) == alarmTime.getCurrentMinute())){
//If the sound is playing, stop it and rewind
if(sound.isPlaying()){
ShowDialog();
alarmTimer.cancel();
alarmTask.cancel();
alarmTask = new PlaySoundTask();
alarmTimer = new Timer();
alarmTimer.schedule(alarmTask, sound.getDuration(), sound.getDuration());
}
sound.start();
}
}
});
}
public void ShowDialog() {
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
alertDialog.setTitle("REMINDER!");
alertDialog.setMessage("Turn off alarm by pressing off");
alertDialog.setNegativeButton("Off", new DialogInterface.OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "OFF", Toast.LENGTH_SHORT);
}
});
alertDialog.show();
}
I am making a simple alarm clock app that notifies the user. I want to make a alert box that gives the user the option to turn off the alarm when it goes off. I was able to make the alert box, but it only appears in the app not outside of the app. I understand the app has to be in the background running. If I need to show more code or be more specific, just ask please.
Add a line as:
public void ShowDialog() {
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
alertDialog.setTitle("REMINDER!");
alertDialog.setMessage("Turn off alarm by pressing off");
alertDialog.setNegativeButton("Off", new DialogInterface.OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "OFF", Toast.LENGTH_SHORT).show();
}
});
alertDialog.show();
// line you have to add
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST);
}
check now.
Do not accept answers if they don't address your question, it is misleading.
The accepted answer is not correct, as it will never work outside your application.
Reason:
It requires an activity context not application context.
If you provide application context, your app will crash with IllegalArgumentException- you need to use Theme.AppCompat or their decendents...
If you need functionality as actually stated in the question you have to have a separate activity themed as a Dialog like here
or you can add a custom view to your window using window manager and making it system level alert like here.
Do this create an Activity without ContentView or a View associated with it and call your alertDialog method in your onCreate also remember to set the background of the Activity to Transparent using ColourDrawable
And that activity will look like a dialog or will suit your preference, you can also fall back to Themes so you can set an Activity as Dialog and treat it like Dialog also use DialogFragment
I am working on Android App using NFC and I want to know if there is any way for transfer data between the tag and the app without any movement.
Let me explain a little.
I pass data to the card when I approach the mobile Tag. But if I want to transfer data again, I have to ward off the mobile phone and bring it back to the Tag.
Is there any way of not having to move the Tag every time I go to pass the data?
Thank you very much!
Regards.
EDIT
For example, in my code I have this:
#Override
protected void onNewIntent(Intent intent){
AlertDialog.Builder dialog = new AlertDialog.Builder(WriteTagActivity.this);
if (_writeMode) {
if (intent.getAction().equals(NfcAdapter.ACTION_TAG_DISCOVERED)) {
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
try {
if((writeTag(buildNdefMessage(), detectedTag))){
dialog.setTitle(getString(R.string.transfer_alert))
.setMessage(getString(R.string.transfer_alert_text)).setCancelable(false)
.setIcon(R.drawable.action_about)
.setPositiveButton(getString(R.string.transfer_alert_button), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
finish();
}
}).create().show();
}else{
dialog.setTitle(getString(R.string.transfer_alert_2))
.setMessage(getString(R.string.transfer_alert_text_2)).setCancelable(false)
.setIcon(R.drawable.action_about)
.setPositiveButton(getString(R.string.transfer_alert_button_yes_2), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}).setNegativeButton(getString(R.string.transfer_alert_button_no_2), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
finish();
}
}).create().show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
but if I want to transfer data again to my tag I have to move the mobile phone. How can I transfer data to my Tag without movement and have the tag always with my phone?
Thank you very much!
What makes you believe that you can only transfer data once for every tap of a tag?
If your tag is an NFC tag (Ndef), you can use multiple reads/writes for each tag activation. Similarly if you use any other communication protocol (IsoDep, NfcA, ...) you can invoke the transceive-method multiple times to exchange multiple commands.
EDIT
Regarding your example code, you would simply call your writeTag(...) method multiple times. Of course this would only make sense if your tag dynamically processes the data.
i would like to have my rate button in my dialog to launch marketplace and go to my specific app.
Also how do i add in a message body into this dialog?
private void makeDialog() {
AlertDialog.Builder about = new AlertDialog.Builder(this);
about.setMessage("About The Giveaway");
about.setPositiveButton("Rate", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
//action
}
});
about.setNegativeButton("Close", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {}
});
about.show();
}
}
I wrote a simple library to do that.
It is called AppRate and you can find it on GitHub here.
Features:
Do not prompt the user if the app has crashed once.
Decide exaclty when to prompt the user. (number of launches ...)
Customize the rate dialog to fit your application design.
Usage example:
It is very easy to install and use:
Drop the jar in your libs folder.
Then include the following code in the onCreate method of your MAIN activity.
new AppRate(this)
.setShowIfAppHasCrashed(false)
.setMinDaysUntilPrompt(0)
.setMinLaunchesUntilPrompt(20)
.init();
This code will show a default rate dialog after 20 lauches.
It will be shown only if the app has never crashed.
The rate button points to your application in the Google Play Store.
I hope this can help you. :)
You can launch the Market app using an Intent. Add this to your positiveButton onClick (replacing the URL with your app url)
Intent browserIntent = new Intent(
"android.intent.action.VIEW",
Uri.parse("https://market.android.com/details?id=com.animoca.prettyPetSalon");
startActivity(browserIntent);