Retaining fragment state after receiving activity result - android-fragments

I have an Activity A that consists of Fragment A. Inside Fragment A, I start Activity B with startActivityForResult(). When I receive the result from Activity B, all views values in Fragment A that had already been set before return to their default values. How to retain the all views values in Fragment A?
Below is the implementation:
public class MainFragment extends Fragment {
public MainFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listView = (ListView) getActivity().findViewById(R.id.xlistview);
xItemArrayList = new ArrayList<XItem>();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id){
case R.id.menu_item_add:
initialiseList();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void initialiseList(){
xListAdapter = new xListAdapter(getContext(), R.layout.item_list, xItemArrayList);
xListAdapter.setxListListener(new xListListener() {
#Override
public void onClickStart(View view) {
openAutocompleteActivity(Constant.VIEW_START);
}
});
xListView.setAdapter(xListAdapter);
}
private void openAutocompleteActivity(int selectedView) {
this.selectedView = selectedView;
try {
// The autocomplete activity requires Google Play Services to be available. The intent
// builder checks this and throws an exception if it is not the case.
Intent intent = new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_FULLSCREEN).build(getActivity());
startActivityForResult(intent, Constant.REQUEST_CODE_AUTOCOMPLETE);
} catch (GooglePlayServicesRepairableException e) {
// Indicates that Google Play Services is either not installed or not up to date. Prompt the user to correct the issue.
GoogleApiAvailability.getInstance().getErrorDialog(getActivity(), e.getConnectionStatusCode(), 0 ).show();
} catch (GooglePlayServicesNotAvailableException e) {
// Indicates that Google Play Services is not available and the problem is not easily resolvable.
String message = "Google Play Services is not available: " + GoogleApiAvailability.getInstance().getErrorString(e.errorCode);
Log.e(Constant.TAG_ERROR, message);
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Check that the result was from the autocomplete widget.
if (requestCode == Constant.REQUEST_CODE_AUTOCOMPLETE) {
if (resultCode == Constant.RESULT_OK) {
// Get the user's selected place from the Intent.
Place place = PlaceAutocomplete.getPlace(getActivity(), data);
if (selectedView == Constant.VIEW_START){
start = place;
((TextView)xListView.getChildAt(0).findViewById(R.id.textview_start)).setText(start.getName());
}else if (selectedView == Constant.VIEW_LAST){
last = place;
((TextView)xListView.getChildAt(0).findViewById(R.id.textview_last)).setText(last.getName());
}
} else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
Status status = PlaceAutocomplete.getStatus(getActivity(), data);
Log.e(Constant.TAG_ERROR, "Error: Status = " + status.toString());
} else if (resultCode == Constant.RESULT_CANCELED) {
// Indicates that the activity closed before a selection was made. For example if
// the user pressed the back button.
}
}
}
There are two views in R.layout.item_list, R.id.textview_start and R.id.textview_last. On select each of the view, Activity B will start and on finish Activity B, the result will be displayed on the view itself. However, every time Activity B starts and finishes, previous values of the two views disappear and return to default. I have tried SavedInstanceState, but it does not work. It seems when Activity B returns to Activity A (with Fragment A in it), system goes to OnResume() of Fragment A without going to onCreatedView() of Fragment A.

You can use 2 method:
1st: use sharedpreferences to store the data. Now this data is accessible next time also the app is used. So after displaying the old data, just reset the data in sharepreferences to blank.
2nd: use bundle to transfer data to the activity and then just retrieve the same back.
Use a bundle to transfer data from one activity to another activity
Bundle bundle = new Bundle();
bundle.putString("KEY_NAME", "Abrakadabra");
Intent i = new Intent(this, MyActivityName.class);
i.putExtras(bundle);
startActivity(i) <-- new activity started
Then in the receiving activity: Put this code in the onCreate method
Bundle bundle = getIntent().getExtras();
String stringdata = bundle.getString("KEY_NAME");
To pass data from activity to fragment: Put this code anywhere
Bundle bundle = new Bundle();
bundle.putString("KEY_NAME", "Abrakadabra");
MyFragment myfragment = new MyFragment();
myfragment.setArguments(bundle);
Then in the onCreateView method of the fragment add this code
Bundle args = getArguments();
String stringdata = args.getString("KEY_NAME");

Since Fragment A is waiting for Activity B's result, Activity A (where Fragment A is) will go to pause state. When Activity B returns results, Fragment A will resumes without going through onActivityCreated(). Thus, saving instance state will not work. Currently, the only solution I can think of is as below.
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Check that the result was from the autocomplete widget.
if (requestCode == Constant.REQUEST_CODE_AUTOCOMPLETE) {
if (resultCode == Constant.RESULT_OK) {
// Get the user's selected place from the Intent.
Place place = PlaceAutocomplete.getPlace(getActivity(), data);
if (selectedView == Constant.VIEW_START){
start = place;
}else if (selectedView == Constant.VIEW_LAST){
last = place;
}
} else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
Status status = PlaceAutocomplete.getStatus(getActivity(), data);
Log.e(Constant.TAG_ERROR, "Error: Status = " + status.toString());
} else if (resultCode == Constant.RESULT_CANCELED) {
// Indicates that the activity closed before a selection was made. For example if
// the user pressed the back button.
}
}
if(start!=null)((TextView)xListView.getChildAt(0).findViewById(R.id.textview_start)).setText(start.getName());
if(last!=null)((TextView)xListView.getChildAt(0).findViewById(R.id.textview_last)).setText(last.getName());
}
All the views values are re-set in onActivityResult(). When Activity A/Fragment A goes into pause state, it retains global variables values. Thus, for this implementation to work, start and last must be declared as global variables in Fragment A. Please suggest a better solution, if any.

Related

Firebase Query not being updated

I want my firebase database link to be updated depending on what the user keys in inside the searchview but the link is not updated unless I open another activity and jump back to it.I have attacked my code in the bottom. So how do I refresh it automatically ?
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener()
{
#Override
public boolean onQueryTextSubmit(String query) {
query = sv.getQuery().toString();
Toast.makeText(MainMenu.this,query, Toast.LENGTH_SHORT).show();
makeItem();
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
public void makeItem ()
{
lv = findViewById(R.id.listView);
db = FirebaseDatabase.getInstance().getReferenceFromUrl("https://vsem-inventory.firebaseio.com/ItemList").orderByChild("ProductName").startAt(query).endAt(query+"\uf8ff");
FirebaseListOptions<ItemObject> options = new FirebaseListOptions.Builder<ItemObject>()
.setLayout(R.layout.content_main_menu_list)
.setQuery(db,ItemObject.class)
.build();
mAdapter = new FirebaseListAdapter<ItemObject>(options) {
#Override
protected void populateView(#NonNull View v, #NonNull ItemObject model, int position) {
final TextView tvAmount = v.findViewById(R.id.amount);
final TextView tvName = v.findViewById(R.id.name);
final TextView tvSerial = v.findViewById(R.id.serialNo);
final TextView tvSupplier = v.findViewById(R.id.supplierName);
final ImageView more = v.findViewById(R.id.more);
ImageView statusimg = v.findViewById(R.id.status);
Drawable paidIcon = v.getResources().getDrawable(R.drawable.succes);
Drawable lateIcon = v.getResources().getDrawable(R.drawable.late);
tvName.setText(model.getProductName());
tvSerial.setText(model.getSerialNo());
tvAmount.setText(model.getQuantity());
tvSupplier.setText(model.getModel());
final String Remarks = model.getRemarks();
final String cat = model.getCategory();
if(model.getQuantity().equals("0"))
statusimg.setImageDrawable(lateIcon);
else
statusimg.setImageDrawable(paidIcon);
more.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v) {
String serialNo = tvSerial.getText().toString();
String itemName = tvName.getText().toString();
String quan = tvAmount.getText().toString();
String supplier = tvSupplier.getText().toString();
showMenu(itemName,more,serialNo,quan,supplier,cat,Remarks);
}
});
}
};
lv.setAdapter(mAdapter);
}
The standard way is to call notifyDataSetChanged() after setting your adapter to your list view
Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.
mAdapter.notifyDataSetChanged();
Although I have seen some situations where only using this does not work and must be followed by these 2 commands.
lv.invalidateViews();
lv.scrollBy(0, 0);
And if all else comes to fail falling back on destroying and redrawing the list view might be your only viable option.
lv.destroyDrawingCache();
lv.setVisibility(ListView.INVISIBLE);
lv.setVisibility(ListView.VISIBLE);
EDIT : After looking at it a while more I just noticed you're missing listeners for your firebase. I assume you already have them somewhere as you already have the list but failing your refresh functions, what you can try is restarting the listeners whenever you're done with a query.
lv.setAdapter(mAdapter);
mAdapter.stopListening();
mAdapter.startListening();

Method checks for lock, runs after lock released

Im facing a potential chase error. Im using javafx TableView to display my data, and I'm periodically receiving an update request externally which calls my update function. I also have some listeners which does stuff such as handle mousedrag events etc. What i want to do is to do something like this:
private void handleEvent(){
TableView.setRowFactory(new Callback<TableView<MyModel>, TableRow<MyModel>>(){
public TableRow<MyModel> call(TableView<MyModel> p) {
final TableRow row = new TableRow();
row.setOnDragDetected(new EventHandler<MouseEvent>(){
public void handle(){
//implement some kind of lock to prevent receiving data update
}
}
row.setOnMouseDragExited(new EventHandler<MouseDragEvent>(){
//release lock to accept update
}
}
}
//this method is being called externally periodically
public void updateModel(MyModel model){
//this won't work because it will skip entirely if it's locked,
//I want it to instead run later when lock is released
if (!locked){
this.model = model;
}
}
I did a quick workaround by using a Boolean to lock and unlock as shown in updateModel Method, problem with that is it will lose some updated data because it's skipped entirely.. instead, I want it to run later when lock is released.. how can I implement this kind of lock mechanism and run later feature?
Edit: why I suspect this is because my listeners are manipulating and getting table data.. while the data is constantly updated, I'm not sure if this is causing my table to break.
Just write some logic that collects everything you tried to do in a locked state and executes it on unlocking.
The following code assumes you're using Platform.runLater or similar code that makes the update run on the application thread.
public class UpdateSynchronizer {
private final List<Runnable> pendingUpdates = new ArrayList<>();
private boolean locked = false;
public void lock() {
if (locked) {
throw new IllegalStateException("double lock");
} else {
locked = true;
}
}
public void runUpdate(Runnable updater) {
if (updater == null) {
throw new IllegalArgumentException();
}
if (locked) {
pendingUpdates.add(updater);
} else {
updater.run();
}
}
public void unlock() {
for (Runnable r : pendingUpdates) {
try {
r.run();
} catch(Exception ex) {
ex.printStackTrace(); // print but ignore
}
}
pendingUpdates.clear();
locked = false;
}
}
If the last update always overwrites all the data from previous updates, simply keeping a single Runnable instead of a list of them would be more performant.
private final UpdateSynchronizer synchronizer = new UpdateSynchronizer();
// why did all the keywords start with uppercase letters (compile time error)
private void handleEvent(){
TableView.setRowFactory(new Callback<TableView<myModel>, TableRow<myModel>>(){
public TableRow<myModel> call(TableView<myModel> p) {
final TableRow row = new TableRow();
row.setOnDragDetected(new EventHandler<MouseEvent>(){
public void handle(){
synchronizer.lock();
//implement some kind of lock to prevent receiving data update
}
}
row.setOnMouseDragExited(new EventHandler<MouseDragEvent>(){
//release lock to accept update
synchronizer.unlock();
}
}
}
//this method is being called externally periodically
public void updateModel(myModel model){
synchronizer.runUpdate(() -> {
// this is just an assignment and won't have any side effects
// updates to the scene may only happen, if the model is accessed in some event handler or animation
this.model = model;
});
}

How to send push notification to a specific group of user in firebase

I want to send push notifications to a specific group of user in firebase.For example, in the sign up form of a app in the pet field a user put pet type as dog.now I want to send push notification to the user who have dog.
anybody please suggest me how can I do this with firebase?
First you have to specify audience in analytics who's pet contains dog.
this screenshot is example of mine
Firebase Cloud Messaging (FCM) offers three different types of targets, which allows you to send push notifications to a specified user group. The three types of targets are: user segment, topic, and single device.
this screenshot is example of mine
In User segment, you can choose an app for sending out push notifications. Click "AND" to add more filter rules.
In audience - you can send push notifications to the users who's pet have dog.
try with this code :
public class NotifyListAdapter extends BaseAdapter implements Constants {
private Activity activity;
private LayoutInflater inflater;
private List<Notify> notifyList;
ImageLoader imageLoader = App.getInstance().getImageLoader();
public NotifyListAdapter(Activity activity, List<Notify> notifyList) {
this.activity = activity;
this.notifyList = notifyList;
}
#Override
public int getCount() {
return notifyList.size();
}
#Override
public Object getItem(int location) {
return notifyList.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
static class ViewHolder {
public TextView notifyTitle;
public TextView notifyTimeAgo;
public CircularImageView notifyAuthor;
public CircularImageView notifyType;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (inflater == null) {
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (convertView == null) {
convertView = inflater.inflate(R.layout.notify_list_row, null);
viewHolder = new ViewHolder();
viewHolder.notifyAuthor = (CircularImageView) convertView.findViewById(R.id.notifyAuthor);
viewHolder.notifyType = (CircularImageView) convertView.findViewById(R.id.notifyType);
viewHolder.notifyTitle = (TextView) convertView.findViewById(R.id.notifyTitle);
viewHolder.notifyTimeAgo = (TextView) convertView.findViewById(R.id.notifyTimeAgo);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (imageLoader == null) {
imageLoader = App.getInstance().getImageLoader();
}
viewHolder.notifyTitle.setTag(position);
viewHolder.notifyTimeAgo.setTag(position);
viewHolder.notifyAuthor.setTag(position);
viewHolder.notifyType.setTag(position);
viewHolder.notifyAuthor.setTag(R.id.notifyAuthor, viewHolder);
final Notify notify = notifyList.get(position);
viewHolder.notifyType.setVisibility(View.GONE);
if (notify.getType() == NOTIFY_TYPE_LIKE) {
viewHolder.notifyTitle.setText(activity.getText(R.string.label_gcm_like));
viewHolder.notifyType.setImageResource(R.drawable.notify_like);
viewHolder.notifyAuthor.setImageResource(R.drawable.notify_like);
} else if (notify.getType() == NOTIFY_TYPE_COMMENT) {
viewHolder.notifyTitle.setText(activity.getText(R.string.label_gcm_comment));
viewHolder.notifyType.setImageResource(R.drawable.notify_comment);
viewHolder.notifyAuthor.setImageResource(R.drawable.notify_comment);
} else if (notify.getType() == NOTIFY_TYPE_COMMENT_REPLY) {
viewHolder.notifyTitle.setText(activity.getText(R.string.label_gcm_comment_reply));
viewHolder.notifyType.setImageResource(R.drawable.notify_reply);
viewHolder.notifyAuthor.setImageResource(R.drawable.notify_reply);
} else {
viewHolder.notifyTitle.setText(activity.getText(R.string.label_follow_you));
viewHolder.notifyType.setImageResource(R.drawable.notify_follower);
viewHolder.notifyAuthor.setImageResource(R.drawable.notify_follower);
}
viewHolder.notifyTimeAgo.setText(notify.getTimeAgo());
return convertView;
}
}
You'd typically use topic messaging for that. With topics your user would subscribe to the topic pet_type_dog. On Android this would look like this:
FirebaseMessaging.getInstance().subscribeToTopic("pet_type_dog");
Then you send a message to all users with pet type equal to dog. With the JavaScript Admin SDK that would look like:
admin.messaging().sendToTopic("pet_type_dog", {
notification: {
title: "Monday message for dog lovers",
body: "Today's special dog is Dee Dee from SF."
}
}).then(function(response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
})
.catch(function(error) {
console.log("Error sending message:", error);
})

Android Loadermanager onloadfinished method was not called on Orientation Change when it is declared outside on create method

i am initializing the loader in action bar navigation list item callback method. By default first item will be selected in action bar navigation list. based on navitem selection i am initializing the loader. at the launch of application the loader call back methods are calling fine. but when i change the orientation the loader callback methods are not getting called.
but if i initialize the loader in oncreate method the loader callback methods are getting called after orientation change also.
My Code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
getActionBar().setListNavigationCallbacks(adapter, new ActionBar.OnNavigationListener() {
#Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
if(itemPosition == 0 || itemPosition ==1)
{
Bundle b = new Bundle();
b.putString(Constatnts.Url, serviceurl +"/getgroups/"+shared.getString("StationID", "null")+"/0");
b.putInt(Constatnts.selection, itemPosition);
b.putString(Constatnts.xmlroles, session.getUserDetails().get(SessionManager.KEY_NAME).get(1));
getLoaderManager().initLoader(0, b, MainActivity.this);
}
else if(itemPosition == 2)
{
Bundle b = new Bundle();
b.putString(Constatnts.Url, serviceurl +"/getchanneldetails/"+shared.getString("StationID", "null")+"/1/0");
b.putInt(Constatnts.selection, itemPosition);
b.putString(Constatnts.xmlroles, session.getUserDetails().get(SessionManager.KEY_NAME).get(1));
getLoaderManager().initLoader(itemPosition, b, MainActivity.this);
}
else
{
Bundle b = new Bundle();
b.putString(Constatnts.Url, serviceurl+"/Getcategories/"+shared.getString("StationID", "null")+"");
b.putInt(Constatnts.selection, itemPosition);
b.putString(Constatnts.xmlroles, session.getUserDetails().get(SessionManager.KEY_NAME).get(1));
getLoaderManager().initLoader(itemPosition, b, MainActivity.this);
}
return true;
}
});
}
The loader callback methods are not getting called after orientation change with the above method.
*but if i place the below code outside the navigationlistcallbacks and inside oncreate method the loader callback methods are getting called *
Bundle b = new Bundle();
b.putString(Constatnts.Url, serviceurl +"/getgroups/"+shared.getString("StationID", "null")+"/0");
b.putInt(Constatnts.selection, itemPosition);
b.putString(Constatnts.xmlroles, session.getUserDetails().get(SessionManager.KEY_NAME).get(1));
getLoaderManager().initLoader(0, b, MainActivity.this);
how can i reload the data after orientation change if initloader is in setListNavigationCallbacks methods.
The callbacks are only invoked after the loader is initiated (initLoader)or restarted (restartLoader). With initLoader the cached result can be used after an orientation change but it has to be called explicitly to deliver the data in onLoadFinished. Hence, you have to call it in your onCreate method with the itemPosition as the loader id, but only if an itemPosition is already set.
I've stripped your example code to illustrate what I mean:
// static so that it survives orientation change.
private static int mSelectedItemPosition = -1; // -1 = Not selected
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mSelectedItemPosition != -1) {
getLoaderManager().initLoader(mSelectedItemPosition, null, MainActivity.this);
}
getActionBar().setListNavigationCallbacks(adapter, new ActionBar.OnNavigationListener() {
#Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
if(itemPosition == 0 || itemPosition ==1) {
mSelectedItemPosition = 0;
Bundle b = ...
getLoaderManager().initLoader(mSelectedItemPosition, b, MainActivity.this);
}
else if(itemPosition == 2) {
mSelectedItemPosition = itemPosition;
Bundle b = ...
getLoaderManager().initLoader(mSelectedItemPosition, b, MainActivity.this);
}
else {
mSelectedItemPosition = itemPosition;
Bundle b = ...
getLoaderManager().initLoader(mSelectedItemPosition, b, MainActivity.this);
}
...
}

WF4: Bookmark with NonBlocking option

Has anyone used CreateBookmark() with BookmarkOptions.NonBlocking?
I'm trying to use it with MultipleResume option but seems I cannot even resume.
Bookmark activity:
public InArgument<string> BookmarkName { get; set; }
public InArgument<BookmarkOptions> BookmarkOptions { get; set; }
protected override void Execute(NativeActivityContext context)
{
var options = BookmarkOptions.Get(context);
context.CreateBookmark(BookmarkName.Get(context),
ReadCompleteCallback,options);
}
Test Code:
[TestMethod]
public void TestMethod1()
{
InitWorkflow();
wfat = WorkflowApplicationTest.Create(sm);
wfat.TestActivity();
Assert.IsTrue(wfat.WaitForIdleEvent());
var res = wfat.TestWorkflowApplication.ResumeBookmark("First", "data");
Assert.IsTrue(res == BookmarkResumptionResult.Success, "Resumption fail with result:" + res);
Assert.IsTrue(wfat.Bookmarks.Contains("First"), "No first bkmk");
}
private void InitWorkflow()
{
sm = new StateMachine()
{
States =
{ //First state with non blocking bookmark
new State(){
DisplayName = "First",Entry = new BookmarkActivity(){BookmarkName = "First",BookmarkOptions =
BookmarkOptions.NonBlocking | BookmarkOptions.MultipleResume},
Transitions =
{
new Transition(){ }
}
}, //Second state with blocking bookmark
new State(){
DisplayName = "Second",Entry = new BookmarkActivity(){BookmarkName = "Second",BookmarkOptions =
BookmarkOptions.None},
Transitions =
{
new Transition(){ }
}
},
new State(){
DisplayName = "End",
IsFinal = true
}
}
};
sm.InitialState = sm.States[0];
sm.InitialState.Transitions[0].To = sm.States[1];
sm.States[1].Transitions[0].To = sm.States[2];
}
Result of ResumeBookmark in above test code is 'NotFound'
I would appreciate any working code that demonstrates NonBlocking option.
Even NonBlocking bookmarks are removed when the activity that created it is completed. They allow the activity to continue execution but that's it.
Bottom line you've to maintain an activity in a not completed state (usually the outside activity) and everything inside it will execute even when a NonBlocking bookmark is found.
That's why you're getting a NotFound error. The activity that created the bookmark has ended and the bookmark no longer exists.
P.S.: A somehow usual use case for NonBlocing bookmarks is, for example, when you've a long running activity, that might throw exceptions while executing, and that way you've the possibility to resume the workflow at a previous state.

Resources