Upload data to firestore before activity is completely destroyed - java

I am making a quiz app. And I want that if the user kills the app their performance be uploaded on the firestore. When he clicks submit Button everything works fine but when I call submitbutton.callOnClick();
in onDestroy() some code is executed but the data is not uploaded.
this is my onDestroy() code
protected void onDestroy() {
Log.d(TAG, "onDestroy: ------");
if (clicked) { }
else
{
clicked = false;
submitButton.callOnClick();
}
Log.d(TAG, "onDestroy: done");
super.onDestroy();
And this is some code of submitButton 's onClickListener
Log.d(TAG, "onClick: ----------------------");
Result result=new Result(new String(ansString),new String(officialAnsString),correct,incorrect,not_attempted,marks);
FireBase_Variables.db.collection(tName+"_result").document(currentUser.getUid()).set(result);
Log.d(TAG, "onClick: ----------------------");
I can see the upper log in the logcat but not the lower one. Which means the line for uploading data is not executed.
What should I do?

You'll want to wait until the write operation is completed, before calling super.onDestroy().
If we unroll the code into a single block, that'd be something like:
protected void onDestroy() {
Log.d(TAG, "onDestroy: ------");
if (clicked) { }
else
{
clicked = false;
Log.d(TAG, "onClick: ----------------------");
Result result=new Result(new String(ansString),new String(officialAnsString),correct,incorrect,not_attempted,marks);
FireBase_Variables.db.collection(tName+"_result").document(currentUser.getUid()).set(result).addOnCompleteListener(new new OnCompleteListener<Void>() {
#Override
public void onComplete(Void aVoid) {
Log.d(TAG, "onDestroy: done");
super.onDestroy();
}
});
}
Also see: How to check a certain data already exists in firestore or not
Note: I didn't compile/run this code, so there may be syntactic problems with it. If you encounter such problems, please try to solve them for yourself and use the edit link under the answer to fix them for folks who come here after you.
In general you'll want to do the above somewhere else than in an onDestroy handler though, as that method is fairly unlikely to be called.

Related

Activity resets on Fragment Transaction

I have an activity that loads multiple fragments depending on user action (button click) or event (FCM Data Message which triggers a LocalBroadcast).
I hit a snag recently when I put a Fragment Transaction inside a BroadcastReceiver, and as soon as the receiver gets triggered, instead of loading up the next fragment, I get the first (default) fragment which is loaded in the OnCreate of the activity, implying that the Activity has reset/restarted somehow.
Given the speed of this, the only error I managed to see before the logcat on Android Studio reset was this :
java.lang.IllegalStateException: Can not perform this action after
onSaveInstanceState
Digging around hasn't helped much, except for this article on Activity State Loss which I discovered. It is rather old (2013) but seems to make sense. However, there is no solution that I can think of, short of making my fragment a bit more complicated, and handing the next fragment's logic in this one itself.
Please find the bit of code where this happens below.
BroadcastReceiver assistanceReceivedStatusReceiver = new BroadcastReceiver() {
public void dummyfunc(){
return;
}
#Override
public void onReceive(Context context, Intent intent) {
// this is triggered by the localBroadcast from FCM Service
boolean requestresult = intent.getBooleanExtra("success", true);
if(!requestresult) {
// we don't have a responder
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: UNABLE TO FIND A RESPONDER");
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: =======================================");
String message = "Unable to find you a responder, please try again!";
frameAnimation.stop();
txtRequestStatus.setText(message);
dialogButtonLayout.setVisibility(View.VISIBLE);
showBottomAppBar();
showMenuFab();
moveMenuRight();
setMenuImage(R.drawable.baseline_undo_white_24dp);
menuButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dialogLayout.setVisibility(View.GONE);
waitingLayout.setVisibility(View.VISIBLE);
moveMenuCenter();
resetMenuImage();
menuButton.setOnClickListener(defaultMenuButtonListener);
}
});
} else {
// we have a responder
// this is a one time receiver - set up an Observable for the Live<Incident>
// and unregister self.
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: RECEIVED A RESPONDER");
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: =======================================");
inIncident = true;
Bundle nextbundle = new Bundle();
responderinfo = intent.getBundleExtra("responderinfo");
nextbundle.putParcelable("data", intent.getBundleExtra("data"));
nextbundle.putBundle("responderinfo", responderinfo);
// GO! GO! GO!!!
//startFragmentWithArgs(new RequestAssistFragmentDeliver(),nextbundle );
RequestAssistFragmentDeliver deliver = new RequestAssistFragmentDeliver();
deliver.setArguments(nextbundle);
((Reviv) getActivity()).getSupportFragmentManager()
.beginTransaction()
.replace(R.id.containerFrameLayout, deliver).commitAllowingStateLoss();
}
}
};
Any ideas on what's going wrong? As always, I'm happy to share more info based on what is needed (the code base is humongous, and knowing what is needed helps me share the relevant segments).
UPDATE 1 :
Sharing the functions as requested by Udit. These are wrapper functions, to help make the code a little more readable. The bottomAppBar (BottomAppBar) and menuButton (FAB) are views that are loaded in the Activity, and I make associations in each Fragment by calling a getter defined in the Activity.
(MainActivity)getActivity.getBottomAppBar();
Functions:
private void showBottomAppBar(){
bottomAppBar.setVisibility(View.VISIBLE);
menuButton.setVisibility(View.VISIBLE);
}
private void moveMenuRight(){
bottomAppBar.setFabAlignmentMode(BottomAppBar.FAB_ALIGNMENT_MODE_END);
}
private void showMenuFab(){
//bottomAppBar.setFabAttached(true);
menuButton.setVisibility(View.VISIBLE);
}
From what I can figure out, your activity is restarting because of the crash while adding the fragment (java.lang.IllegalStateException).
To confirm, you can replace
.commit()
method with
.commitAllowingStateLoss()
and see if this solves your problem

freeze android app when playing music of server

I have an online music player and my problem is when user press on play button
my app freeze for 1 or 2 second(network speed).
I play music of a link of my server and put it in Voice variable and play stream that.
whats the problem of my code?
Thanks.
My code:
detail_voice.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar snackbar = Snackbar.make(detail_date, " " + "دارم میگیرررررررررمش :| :)))", Snackbar.LENGTH_SHORT);
snackbar.show();
try {
mp.setDataSource(Voice);
mp.prepare();
mp.start();
detail_voice.setVisibility(View.GONE);
detail_voice_stop.setVisibility(View.VISIBLE);
} catch (IOException e) {
e.printStackTrace();
}
}
});
detail_voice_stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
detail_voice.setVisibility(View.VISIBLE);
detail_voice_stop.setVisibility(View.GONE);
mp.stop();
mp.reset();
detail_voice_stop.setVisibility(View.GONE);
}
});
The code you've provided is not extensive, but my guess is your problem might indeed have to do with loading your data over the network. Usually when loading data over a network, you'll want to do that asynchronously to avoid the program needlessly waiting for the data, and freeze during that time.
The MediaPlayer class has a prepareAsync() method which will do this for you. But, you cannot put the start() method below that anymore as it is now asynchronous. Try replacing this code:
mp.setDataSource(Voice);
mp.prepare();
mp.start();
With this:
mp.setDataSource(Voice);
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer player) {
player.start();
}
});
mp.prepareAsync();
If that code solves the problem, your connection was indeed the problem. You should not however use the setOnPreparedListener() method every time you click. I'd suggest moving it outside the onClickListener() method so it is only executed once.
This Stackoverflow question has a good implementation too. It might be worth a read as well.

In a snackbar action, how can I be sure it's safe to permanently delete a soft-deleted record from the database?

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.

Some listeners not firing

I am integrating an Android ad SDK that implements a number of listeners. Some of these listeners are not firing within my app. For example onAdLoaded will fire, but onAdDisplayed will not. This works perfectly fine in the sample app that's provided with the SDK, which leads to thinking it's an issue with integration. However, I can't find anything that's causing this snag. It's absolutely bizarre behaviour that I've never come across before. I know I'm grasping at straws over here, but hypothetically speaking what could be the causes for some listeners not registering while others are? How can I debug this?
Here's some code:
ad = new InterstitialAd(lastActivity, placementId);
ad.setAdListener(new InterstitialAdListener() {
//doesn't fire
#Override
public void onInterstitialDisplayed(Ad ad) {
Log.e(TAG, "INTERSTITIAL DISPLAYED");
Toast.makeText(lastActivity, "onInterstitialDisplayed", Toast.LENGTH_SHORT).show();
}
//doesn't fire
#Override
public void onInterstitialDismissed(Ad ad) {
Log.e(TAG, "INTERSTITIAL DISMISSED");
Toast.makeText(lastActivity, "onInterstitialDismissed", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Ad ad, AdError adError) {
Log.e(TAG, "ERROR! " + adError.getErrorMessage());
Toast.makeText(lastActivity, "onError", Toast.LENGTH_SHORT).show();
}
#Override
public void onAdLoaded(Ad ad) {
Log.e(TAG, "AD LOADED!");
AdAdapter.this.ad.show();
Toast.makeText(lastActivity, "onAdLoaded", Toast.LENGTH_SHORT).show();
}
//doesn't fire
#Override
public void onAdClicked(Ad ad) {
Log.e(TAG, "AD CLICKED!");
Toast.makeText(lastActivity, "onAdClicked", Toast.LENGTH_SHORT).show();
}
});
ad.loadAd();
I finally figured out why. I decompiled the SDK to have a closer look. The package name was hardcoded into some of the conditions that relate to the non-firing listeners. I refactor their package name within my build script, but the script ignores strings. So naturally they wouldn't fire because the package name has changed.

How to handle runtime exception on playing audio files?

I have a button that plays an audio file on its click listener. If the button is clicked again and again while the audio file is being played then the app crashes. What's the solution?
Here is some code for reference:
private OnClickListener btnMercyListener = new OnClickListener()
{
public void onClick(View v)
{
// Toast.makeText(getBaseContext(),
// "Mercy audio file is being played",
// Toast.LENGTH_LONG).show();
if (status==true)
{
mp.stop();
mp.release();
status = false;
}
else
{
mp = MediaPlayer.create(iMEvil.this,R.raw.mercy);
//mp.start();
try{
mp.start();
status= true;
//mp.release();
}catch(NullPointerException e)
{
Log.v("MP error",e.toString());
}
}
mp.setOnCompletionListener(new OnCompletionListener(){
// #Override
public void onCompletion(MediaPlayer arg0) {
mp.release();
status = false;
}
}
);
}
};
Two things:
1. Debug the crash and see where it's failing (which line).
2. Surround the whole statement with a try/catch and simply catch an Exception.
If you have an exception or a better idea where your code is failing, then it will be much easier to give you advice on how to fix it... as a matter of fact, you might not even need advice to fix it, you might end up solving the problem by yourself and then you will reap the fruits of your own success.
Update per comments:
The documentation for MediaPlayer indicates what might be the problem given the symptoms the OP is seeing:
To stop playback, call stop(). If you wish to later replay the media, then
you must reset() and prepare() the MediaPlayer object before calling
start() again. (create() calls prepare() the first time.)
It looks like if the play button is pressed too many times, then the media may end up not being in the prepared state and thus throw some exception. The idea of disabling the play button is valid and it should take care of this situation.
Here is some illustrative code on what you want your program to do:
private OnClickListener btnMercyListener = new OnClickListener()
{
public void onClick(View v)
{
if(isPressed)
{
return;
}
isPressed = true;
// create your media player
mp = MediaPlayer.create(iMEvil.this,R.raw.mercy);
// set your listener
mp.setOnCompletionListener(mp.setOnCompletionListener(new OnCompletionListener(){
// #Override
public void onCompletion(MediaPlayer arg0) {
if(!isPressed)
{
return;
}
isPressed = false;
// re-enable your play button
playButton.enable();
// disable the pause button
pauseButton.disable();
mp.release();
mp.prepare();
}
}
);
// disable the play button
playButton.disable();
// enable the pause button
pauseButton.enable();
// start playback
mp.start();
}
};
Of course you should have the appropriate try/catch statements in there so your app doesn't crash, but this code should give you a general idea of what to do.

Categories