Detect if Firebase has read from the database in Android? - android

I'm playing around with Firebase and Android and I'm storing my data online in Firebase. I have an activity with 3 EditTexts and a button and I want it to essentially check that the 3 values in the textboxes are in the database and if so, move onto the next activity.
These values are static and so I'm using the .addListenerForSingleValueEvent method and using it as an anonymous function. I want this to work even if the device is offline but if any of the values come back as null then I want it to reject moving to the next activity.
Is there any way to detect when Firebase has either retrieved a value from the database or retrieved null from the database? This way I could check each value consecutively and if any return null then reject changing activities.
The only other thing I could think to do is to nest the anonymous functions for each listener but that sounds like it would be a bad idea.
Any advice would be much appreciated. Thanks!

Well, i understand you want to check for values in your database before moving to the next activity. My advice is that
1) create an ArrayList of DatabaseReference that point to the data locations in firebase in your activity.
Arraylist<DatabaseReference> list = new ArrayList<>();
list.add(FirebaseDatabase.getInstance().getReference().child("my_child_node_1"):
list.add(FirebaseDatabase.getInstance().getReference().child("my_child_node_2"):
list.add(FirebaseDatabase.getInstance().getReference().child("my_child_node_3"):
2) create an interface and a call back method.
public static interface DataCallback {
static void onDataAdded(String string); //Use the necessary data type
}
3) use the interface to create a constructor for your Event listener.
public static class DataListener implements ValueEventListener {
private DataCallback dataCallback;
public DataListener(DataCallback dataCallback) {
this.dataCallback = dataCallback;
}
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String data = snapshot.getValue(String.class);
//This is step 4
if(dataCallback!=null) {
dataCallback.onContactAdded(data);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
}
4) Pass the value from the listener to your activity as follows inside onDataChange() method of the Listener
if(dataCallback!=null) {
dataCallback.onContactAdded(data);
}
5) implement the interface in your activity and override its callback method
public class MyActivity extends AppCompatActivity implements DataCallback{
private EditText edittext1;
private EditText edittext2;
private EditText edittext3;
private ArrayList<String> my_list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//do stuff...
}
#Override
public void onDataAdded(String data) {
}
}
6) Instantiate and add the listener in your activity's onResume() method
#Override
public void onResume(){
super.onResume();
DataListener listener = new DataListener(this);
list[0].addListenerForSingleValueEvent(listener);
list[1].addListenerForSingleValueEvent(listener);
list[2].addListenerForSingleValueEvent(listener);
}
7) get the values from firebase in your activity inside the Callback method as follows
#Override
public void onDataAdded(String data){
my_list.add(data);
}
8) you can check if the values gotten from firebase match the values gotten from the EditText inside the OnClicklistener for you button.
if (button != null) {
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(my_list.contains(edittext1.getText().toString)){
if(my_list.contains(edittext.getText().toString)){
if(my_list.contains(edittext.getText().toString)){
startActivity(new intent(MyActivity.this, NextActivity.class))
}
}
}
}
});
}
9) remove the listeners inside the onPause() method.
#Override
public void onPause(){
super.onPause();
list[0].removeEventListener(listener);
list[1].removeEventListener(listener);
list[2].removeEventListener(listener);
}

Related

get value of a variable “outside” of a callback in android

There is a String message as a parameter in an interface method:
public class Home extends AppCompatActivity {
private String globalStringResult = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_activity);
getMediaInfo();
Log.d("Result: ", globalStringResult); // Here result is Null
}//TODO OnCreate End
private void getMediaInfo() {
FFmpeg.getInstance(this).execute(new String[]{"-version"},
new ExecuteBinaryResponseHandler() {
#Override
public void onSuccess(String message) {
globalStringResult = message;
}
});
}
}
Here is this problem that I've faced many times but always ran from it. Now I want to deal with it if you help me.
I am executing getMediaInfo() method inside onCreate. When I log the result inside onCreate after getMediaInfo() execution , the result would be null. But if I run it inside an onClick button or something I get my desired result.
Is there any way that I could return callback message anywhere that I want?
Sounds like your function getMediaInfo is asynchronous which means it could take some time before the onSuccess block is called and your value is set. Instead of relying on a variable I would suggest to use a callback function. That way your message will be passed to the callback function and you could use it anywhere.
public interface MyCallback{
void success(String message);
}
Then you would need to modify your function as follows. Then where ever the callback is implemented you will receive the message and you can act on the value.
public void getMediaInfo(MyCallback callback){
FFmpeg.getInstance(this).execute(cmdArray, new ExecuteBinaryResponseHandler() {
#Override
public void onSuccess(String message) {
callback.success(message);
}
});
If your further actions depend on the value set in onSuccess callback then simply call a function from this callback method. You need to provide more info on what exactly you are trying to do with this variable.
Asynchronous calls can be tricky but you have to wait until it is finished before the variable is available. This means calling any methods that rely on the variable in the callback of the async call. There's really no way around it. You may want to make two version of the call; one for onCreate and one when you need to call it from other places.
public class Home extends AppCompatActivity {
private String globalStringResult = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_activity);
getMediaInfoOnCreate();
// move any code below this line into the new method
}//TODO OnCreate End
private void getMediaInfo() {
FFmpeg.getInstance(this).execute(new String[]{"-version"},
new ExecuteBinaryResponseHandler() {
#Override
public void onSuccess(String message) {
globalStringResult = message;
codeThatNeedsToBeRunDuringOnCreateButAfterSettingGlobalStringResult();
}
});
}
private void getMediaInfoOnCreate() {
FFmpeg.getInstance(this).execute(new String[]{"-version"},
new ExecuteBinaryResponseHandler() {
#Override
public void onSuccess(String message) {
globalStringResult = message;
}
});
}
private void codeThatNeedsToBeRunDuringOnCreateButAfterSettingGlobalStringResult() {
// put your moved code from onCreate here
Log.d("Result: ", globalStringResult); // Here correct result will be logged
}
}

Getting values from multiple fragments to activity by pressing on a submit button

I just started shortly to programming an android app. I already searched on Google and on Stackoverflow for the answer but I couldn't find the right answer for my problem so I hope you guys can help me.
So what I have now are multiple fragments in a Activity with many EditText fields. When the user filled all fields, I want to get all these values by pressing on a "submit" Button on the Activity. I search for something what would recognize that I press the Button of the Activity (so out of the fragments) and then would save the values of all fragments for example with bundles. The best way (but I think it is not possible) would be to fetch the data by views of the fragments with getstring.
Can you help me?
public interface ActivityFragmentCallback {
String getData();
String getTAG();
}
public class FragmentA extends Fragment implements ActivityFragmentCallback {
#Override
public String getData() {
return editText.getText().toString();
}
#Override
public String getTAG() {
return "FragmentA";
}
}
public class ActivityA extends BaseActivity {
private ActivityFragmentCallback callback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(/* your layout id */);
View button = view.findViewById(R.id.btn_next);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String tag = callback.getTAG();
String data = callback.getData();
//depends on tag set your data
}
})
}
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
try {
callback = (ActivityFragmentCallback) fragment;
} catch (ClassCastException e) {
L.e(TAG, e);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
callback = null;
}
}

Get data from viewpager fragments

I have an Activity which has a Save details button and a Viewpager which contains 4 fragments. The Fragments contains User details form. Once the Save button is clicked I need to get the data from all fragments and save the details. How to get the data entered by the user in all 4 fragments when Save button is clicked in the activity?
I just worked on an app that had the same use case. In addition, I had to save the data on a back navigation as well. The problem was a bit more difficult that I though it should have been. The problems came from the fact that not all the fragments in the ViewPager are guaranteed to be alive. They either may not have been started yet, or destroyed when the user paged off of them.
To solve the problem, I took inspiration from this blog post about handing back-press events. I had to modify it a bit to allow for any fragments that may be running and not just one.
public abstract class BackHandledFragment extends Fragment {
protected BackHandlerInterface backHandlerInterface;
public abstract String getTagText();
public abstract boolean onBackPressed();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!(getActivity() instanceof BackHandlerInterface)) {
throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
} else {
backHandlerInterface = (BackHandlerInterface) getActivity();
}
}
#Override
public void onResume() {
super.onResume();
backHandlerInterface.addRunningFragment(this);
}
#Override
public void onPause() {
super.onPause();
backHandlerInterface.removeRunningFragment(this);
}
public interface BackHandlerInterface {
public void addRunningFragment(BackHandledFragment backHandledFragment);
public void removeRunningFragment(BackHandledFragment backHandledFragment);
}
}
The Activity implements the interface and tracks the active fragments:
public class EditActivity implements BackHandledFragment.BackHandlerInterface
{
private List<BackHandledFragment> listActiveFragments = new ArrayList<>
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Notify each active fragment that the back was pressed, this will allow
// them to save any data.
for (BackHandledFragment bf : listActiveFragments) {
bf.onBackPressed();
}
}
#Override
public void addRunningFragment(BackHandledFragment backHandledFragment) {
listActiveFragments.add(backHandledFragment);
}
#Override
public void removeRunningFragment(BackHandledFragment backHandledFragment) {
listActiveFragments.remove(backHandledFragment);
}();
}
Each fragment must extend BackHandledFragment:
public class DetailNetworkFragment extends BackHandledFragment {
#Override
public void onPause() {
super.onPause();
EventBus.getDefault().unregister(this);
saveDataFields();
}
#Override
public void onResume() {
super.onResume();
EventBus.getDefault().register(this);
}
#Override
public String getTagText() {
return TAG;
}
#Override
public boolean onBackPressed() {
saveDataFields();
return false;
}
}
The saveDataFields() is not too interesting. It just copies the data out of the UI views and saves them back to an object in the Activity.

How to control other View in different Activity?

I have created the customAdapter of ListView. when I click the Button in the ListView,I want to control the TextView outside the ListView from different layout. Notice ! There are not in the same Java code.
here is what I want to do:
viewHolder.plus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
TextView tx=(TextView) txt.findViewById(R.id.moneytext);
//find the textview outside listview (now In the same java code and xml)
totalmoney= (Integer.parseInt(tx.getText().toString()))+(Integer.parseInt(price[position]));
//get the textview integer and add to my present number
tx.setText(""+totalmoney);
//post on the textview
}
});
There is couple ways to do that I remember 3 of :
1) Add TextView to your ListView adapter constructor's parameters
2) Add your Activity to constructor
3) Use Event based solution like EventBus [This can be used even your TextView and ListView in different Activity and you don't need parameters]
There is example of 1 and 2:
public class YourListAdapter{
TextView strangerTextView;
YourActivity yourActivity;
public YourListAdapter(TextView strangerTextView,Activity yourActivity){
//using one of them is enough
this.strangerTextView=strangerTextView;
this.yourActivity=yourActivity;
}
//when changing text use like
strangerTextView.setText("Class Board");
//or (make sure you have global public textview attribute in your YourActivity class)
yourActivity.textview.setText("Class Board");
}
For EventBus :
1) Create a class that holds event information :
public class TextChangeEvent{
public String newtext;
public TextChangeEvent(String newtext){
this.newtext=newtext;
}
}
2) Then in your relevant Activity :
#Override
protected void onStart() {
super.onStart();
if(!EventBus.getDefault().isRegistered(this))EventBus.getDefault().register(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
#Subscribe
public void onEvent(TextChangeEvent event) {
this.textView.setText(event.newtext)
}
3) Post events like that, anywhere you want :
EventBus.getDefault().post(new TextChangeEvent("newtext"));
Are your TextView in the same activity? If yes, then you should put
tx = (TextView) findViewById(R.id.moneytext);
in onCreate() method and then access it from listener you have:
tx.setText("" + totalmoney);
If it is not in the same activity, you can make something like this:
public class ClassA extends Activity
{
public TextView textView;
public void onCreate()
{
super.onCreate();
textView = (TextView) findViewById(R.id.moneytext);
}
}
public class ClassB extends Activity
{
...
viewHolder.plus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//classA is a global variable that you get for example from contructor
tx = classA.textView;
totalmoney= (Integer.parseInt(tx.getText().toString()))+(Integer.parseInt(price[position]));
//get the textview integer and add to my present number
tx.setText(""+totalmoney);
//post on the textview
}
});
...
}

Android - does defining a button static leak my activity? Can I avoid it?

I need the button to be static so I can enable it/ disable it form my services in case the activity is shown. Still I setOnClickListener and anyway static views are considered dangerous. Do I leak ? Can I avoid it ?
public class MonitorActivity extends FragmentActivity implements
OnClickListener {
private static Button updateButton; // static??
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitor);
// button
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this); // oops ?
}
public static void onDataUpdated(Context ctx) {
if (updateButton != null) { //that's why I need it static
updateButton.setEnabled(true); // + set the text etc
}
}
public static void onUpdating() {
if (updateButton != null) {
updateButton.setEnabled(false);
}
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.update_data_button:
serviceIntent.putExtra(MANUAL_UPDATE_INTENT_KEY, true);
this.startService(serviceIntent);
}
}
#Override
protected void onResume() {
super.onResume();
Boolean isUpdating = AccessPreferences.get(this, updateInProgressKey,
false);
// set the button right
updateButton.setText((isUpdating) ? defaultUpdatingText
: getResources().getString(R.string.update_button_text));
updateButton.setEnabled(!isUpdating);
}
}
I think You can create BroadcastReceiver in MonitorActivity. And send extras message from Service to enable/disable button.
I suggest you use LocalBroadcastManager
In your Activity define a BroadcastReceiver and register the Broadcast in onStart()onResume() and unregister it in onStop()onPause().
From your Service send the Broadcast to the Activity if the Activity is active it will receive the Broadcast and update the UI, if not nothing will happen.
Define another BroadcastReceiver in your Service, Register the Broadcast in onCreate() and Unregister it in onDestroy().
When your Activity is started send a Broadcast to the Service and let the Service reply to the Activity using the first Broadcast to update the UI.
UPDATE
After doing some investigation I found you're correct "sticky broadcasts are discouraged", but if you check the date of that post it's on 2008 - before Google implemented the LocalBroadcastManager.
And I have checked the source code of LocalBroadcastManager, it's not a real Broadcast it's an interface, Singleton with a list of BroadcastReceivers (not global and no IPC communication).
I really hate public static and I always avoid them. every body should.
So yes - the static button would leak my activity. I came up with callback below but it is ugly. I finally solved it by making the Activity extend OnSharedPreferenceChangeListener
public final class MonitorActivity extends FragmentActivity implements
OnClickListener, OnSharedPreferenceChangeListener {
private Button updateButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this); //no need to unregister methinks
}
#Override
public synchronized void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if (updateInProgressKey.equals(key)) {
final Boolean isUpdating = AccessPreferences.get(this,
updateInProgressKey, false);
// set the button right
updateButton.setText((isUpdating) ? sDefaultUpdatingText
: sUpdateButtonTxt);
updateButton.setEnabled(!isUpdating);
}
}
#Override
protected void onStart() {
super.onStart();
AccessPreferences.registerListener(this, this);
AccessPreferences.callListener(this, this, updateInProgressKey);
}
#Override
protected void onStop() {
// may not be called (as onStop() is killable), but no leak,
// see: http://stackoverflow.com/a/20493608/281545
AccessPreferences.unregisterListener(this, this);
super.onStop();
}
}
Callback
onPause() is guaranteed to run - so I null the static fields there and populate them on onResume(). I only do a read from default shared preferences so it should not take long in the synchronized blocks (I have to synchronize cause the service might call onUpdating() or onDataUpdated() any odd time). Not sure about unregistering the listener though
public class MonitorActivity extends FragmentActivity implements
OnClickListener {
private static TextView dataTextView; //null this onPause() to avoid a leak
private static Button updateButton; // null this onPause() to avoid a leak
// ...
public static synchronized void onDataUpdated(Context ctx) {
if (updateButton != null) {
updateButton.setEnabled(true); // + set the text etc
}
}
public static synchronized void onUpdating() {
if (updateButton != null) {
updateButton.setEnabled(false);
}
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.update_data_button:
serviceIntent.putExtra(MANUAL_UPDATE_INTENT_KEY, true);
this.startService(serviceIntent);
}
}
#Override
protected void onResume() {
super.onResume();
synchronized (MonitorActivity.class) {
Boolean isUpdating = AccessPreferences.get(this,
updateInProgressKey, false);
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this);
// set the button right
updateButton.setText((isUpdating) ? defaultUpdatingText
: getResources().getString(R.string.update_button_text));
updateButton.setEnabled(!isUpdating);
}
}
#Override
protected void onPause() {
synchronized (MonitorActivity.class) {
updateButton.setOnClickListener(null); // TODO : needed ??
dataTextView = updateButton = null; // to avoid leaking my activity
}
super.onPause();
}
}
There are 3 solutions for you:
Set button = null when context is destroyed(onStop);
Use WeakReference for button field, Example:
private static WeakReference<Button> updateButton;
Not creating static button. It's always hold the context.

Categories

Resources