I followed a tutorial on how to update items in firebase, but when I try, it only adds a new item with the new edited name. It doesnt delete the one I was going to update.
Here is my code for the alertdialog (that opens when I long click an item in the listview):
private void updateDialog(final String verktøynavn, final String verktøynr, final String verktøytype, final String verktøystatusibil, final String utlånttil, final String verktøystatusutlant) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
LayoutInflater inflater = getLayoutInflater();
final View dialogView = inflater.inflate(R.layout.endreslette_dialog, null);
dialogBuilder.setView(dialogView);
final TextView tvverktøynavn = (TextView) dialogView.findViewById(R.id.tvverktøynavn);
final EditText etverktøynavn = (EditText) dialogView.findViewById(R.id.etverktøynavn);
final EditText etverktøynr = (EditText) dialogView.findViewById(R.id.etverktøynr);
final Spinner spverktøytype = (Spinner) dialogView.findViewById(R.id.spverktøytype);
final ImageView ivoppdater = (ImageView) dialogView.findViewById(R.id.ivoppdater);
final ImageView ivslettverktøy = (ImageView) dialogView.findViewById(R.id.ivslettverktøy);
dialogBuilder.setTitle(verktøynavn);
final AlertDialog alertDialog = dialogBuilder.create();
alertDialog.show();
ivoppdater.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String navn = etverktøynavn.getText().toString().trim();
String nr = etverktøynr.getText().toString().trim();
String type = spverktøytype.getSelectedItem().toString();
if(TextUtils.isEmpty(verktøynavn)){
etverktøynavn.setError("Navn mangler");
return;
}
updateverktoy(navn, nr, type, verktøystatusibil, verktøystatusutlant, utlånttil);
}
});
}
private boolean updateverktoy(String id, String navn, String nr, String type, String statusibil, String statusutlant) {
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("Verktøy").child(id);
Verktoy verktoy = new Verktoy(id, navn, nr, type, statusibil, statusutlant);
databaseReference.setValue(verktoy);
Toast.makeText(this, "Verktøy endret", Toast.LENGTH_LONG).show();
return true;
This is happening because you are using setValue() method instead of updateChildren() method. If the id is different every time when you call the updateverktoy(), then a new child will be added, if the id the is the same, that child will be overridden. So in order to update a child, you need to use the same id, a Map that contains the new values that you want to update and then call updateChildren() on that particular reference.
Related
I am using a recyclerView to display a list of items that can be deleted when user clicks on delete button of each item. In order to do that, i've used a recyclerview adapter.
What i want: From that recycleView adapter, when user clicks on delete button for an item, a dialog fragment is shown and user has to enter a code when that code is valid then the item is removed from both the database and the recyclerView
What i've tried: After calling the show() method on the dialogFragment in the adapter, i'm calling removeItem(getPostion()).
What i get: When the dialog shows up, the item is removed from the recyclerView before the user enters the code but is still in the database
I 've tried other things but none of them worked.
Please help!!!
RecyclerView Adapter (EBAdapter)
public EBViewHolder(View itemView) {
super(itemView);
contact_row = (LinearLayout) itemView.findViewById(R.id.contact_row);
contact_row.setOnClickListener(this);
contact_photo = (ImageView) itemView.findViewById(R.id.contact_photo);
contact_photo.setOnClickListener(this);
contact_fullname = (TextView) itemView.findViewById(R.id.contact_fullname);
contact_relationship = (TextView) itemView.findViewById(R.id.contact_relationship);
contact_action = (ImageView) itemView.findViewById(R.id.contact_action);
contact_action.setOnClickListener(this);
contact_discard = (ImageView) itemView.findViewById(R.id.discard_action);
contact_discard.setOnClickListener(this);
}
#Override
public void onClick(View view){
//Handling recyclerView action clicked
String status;
if(view instanceof ImageView) {
if (view == contact_photo) {
Log.d("Contact", contact_photo.getId() + "photo " + getPosition() + "clicked");
ShowContactPhoto dialog = new ShowContactPhoto();
dialog.setStyle(DialogFragment.STYLE_NO_TITLE,0);
dialog.show(manager, "ScP_dialog");
}
if (view == contact_action) {
Log.d("Contact", contact_fullname.getText() + "action " + getPosition() + "clicked");
status = "active";
Date created_at = new Date();
Date updated_at = new Date();
AddInEmergencyBase dialog = new AddInEmergencyBase(((BitmapDrawable)contact_photo.getDrawable()).getBitmap(), contact_fullname.getText().toString(),contact_relationship.getText().toString(),status,created_at.toString(),updated_at.toString());
dialog.show(manager, "AiB_dialog");
Log.d("Contact", "Dialog Dismissed now");
}
//REGION OF INTEREST:
if (view == contact_discard) {
Log.d("Contact", contact_discard.getId() + "photo " + getPosition() + "clicked");
status = "inactive";
dialog = new DiscardEmergencyPers(contact_fullname.getText().toString(),status,mModels,getPosition(),rc);
position = getPosition();
dialog.show(manager, "DeP_dialog");
//dialog.getDialog().setOnDismissListener(this);
//onDismiss(dialog);
removeItem(getPosition());
Log.d("Contact", dialog.getDialog()+"item removed from data actually updated so hihihi....");
/*ShowContactPhoto dialog = new ShowContactPhoto();
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
dialog.show(manager, "ScP_dialog");*/
}
}
else{
Log.d("Contact", contact_row.getId() + "row " + getPosition() + "clicked");
}
DialogFragment (DiscardEmergencyPers)
public class DiscardEmergencyPers extends DialogFragment implements View.OnClickListener, AdapterView.OnItemSelectedListener,DialogInterface.OnDismissListener, DialogInterface {
private final String fullname;
private final List<EmBaseInfo> mModels;
private final String status;
private final int position;
private final RecyclerView rc;
//private final String updated_at;
EditText pin_code;
Button cancel, done;
Communicator com;
private EmergenciaDBAdapter emergencia_helper;
private Context context;
private RecyclerView recycler_view;
public DiscardEmergencyPers(String fullname, String status,List<EmBaseInfo> mModels, int position, RecyclerView rc){
this.fullname=fullname;
this.status = status;
this.mModels = mModels;
this.position = position;
this.rc =rc;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
com = (Communicator) activity;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_discardemperson, null);
context = getActivity().getApplicationContext();
getDialog().setTitle("Remove emergency contact");
//Spinner that contains relationships with the current contact
pin_code = (EditText) view.findViewById(R.id.pin);
/*ArrayAdapter adapter = ArrayAdapter.createFromResource(context, R.array.relationship, android.R.layout.simple_spinner_dropdown_item);
relationship_spinner.setAdapter(adapter);
relationship_spinner.setOnItemSelectedListener(this);*/
//End of Spinner
//
emergencia_helper = new EmergenciaDBAdapter(context);
//spinner = (Spinner)view.findViewById(R.id.spinner);
cancel = (Button) view.findViewById(R.id.cancel);
done = (Button) view.findViewById(R.id.done);
cancel.setOnClickListener(this);
done.setOnClickListener(this);
setCancelable(false);
return view;
}
#Override
public void onClick(View view) {
if(view.getId()==R.id.cancel){
dismiss();
com.onDialogMessage("Cancel clicked");
}
else{
com.onDialogMessage("Done clicked");
if(pin_code.getEditableText().toString().equals("12345")){
dismiss();
emergencia_helper.update(status, fullname/*,updated_at*/);
//EBAdapter n_adapter = new EBAdapter(getActivity(),removeItem(this.position),getFragmentManager(),rc);
//rc.setAdapter(n_adapter);
//rc.getAdapter().(this.position);
rc.getAdapter().notifyDataSetChanged();
rc.getAdapter().notifyItemRemoved(0);
//Bundle savedInstanceState=null;
//getActivity().onCreate(savedInstanceState);
CharSequence text = "Good Pin "+rc.getAdapter();
Log.d("Contacts", rc.getClass()+"");
Log.d("Contacts", rc.getAdapter()+" "+getActivity());
int duration = Toast.LENGTH_SHORT;
Toast.makeText(context, text, duration).show();
}
else{
CharSequence text = "Wrong Pin";
//Log.d("Contacts", mModels.toString());
int duration = Toast.LENGTH_SHORT;
Toast.makeText(context, text, duration).show();
}
//long id=emergencia_helper.insert(photo,fullname,phone,relationship, status,created_at,updated_at);
/*if(id<0){
CharSequence text = "Row insertion unsuccessful ";
//Log.d("Contacts", mModels.toString());
int duration = Toast.LENGTH_SHORT;
Toast.makeText(context, text, duration).show();
}
else{
CharSequence text = "Row insertion successful ";
//Log.d("Contacts", mModels.toString());
int duration = Toast.LENGTH_SHORT;
Toast.makeText(context, text, duration).show();
}*/
}
}
Your code is quite complex!
But from what you want: delete item from db and recyclerview after user input right code. So, basically you can reach it by some steps:
Create listener for each recyclerview item (or button on item), when user click that -> open a dialog.
After user input the right code (you have to check if it right) then do two things:
-> Remove view from recyclerview via adapter:
dataSource.remove(index); // remember to remove it from your adapter data source
notifyItemRemoved(index);
-> Remove data from database (how to remove depend on your)
If the data was not removed from your db, please debug your delete function if was called, if it work, or any mistake!
Hope it help!
I used the answer to this question to get the basic value of the TextView clicks into a single TextView .This code gives a per click value, I want to know how to add the existing value of TextView.
Note: m1aa and m1ab are number variables in a previous activity and received through intent on this one.
UPDATE This question should have been "how do you convert a string to a decimal."
//Original non working code.
private int productTotal1;
private int productTotal2;
private int overallTotalproduct;
private TextView m1a,
private TextView m1aa,
//and then way down the page
productTotal1 = 0;
productTotal2 = 0;
overallTotalproduct = 0;
final TextView textViewtotalproduct = (TextView) findViewById(R.id.total);
final TextView textView1 = (TextView) findViewById(R.id.m1aa);
final TextView textView2 = (TextView) findViewById(R.id.m1ab);
textView1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
productTotal1++;
overallTotalproduct = productTotal1 + productTotal2;
textView1.setText(String.valueOf(productTotal1));
textViewtotalproduct.setText(String.valueOf(overallTotalproduct));
}
});
textView2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
productTotal2++;
overallTotalproduct = productTotal1 + productTotal2;
textView2.setText(String.valueOf(productTotal2));
textViewtotalproduct.setText(String.valueOf(overallTotalproduct));
}
});
***UPDATE***WORKS FINE :)
AFTER GETTING HELP AND "THINKING" ABOUT WHAT I WAS WRITING I END UP WITH...
//Working version
private double overallTotalproduct;
final TextView textViewtotalproduct = (TextView) findViewById(R.id.total);
final TextView textView1 = (TextView) findViewById(R.id.m1aa);
final String stringm1aa = textView1.getText().toString();
final double intm1aa = Double.parseDouble(stringm1aa);
final TextView textView2 = (TextView) findViewById(R.id.m1ab);
final String stringm1ab = textView2.getText().toString();
final double intm1ab = Double.parseDouble(stringm1ab);
You can use following code to get a value of TextView and convert it to integer
String s = textView.getText().toString();
int n = Integer.parseInt(s);
Remember NumberFormatException will be thrown if the string does not contain a parsable integer.
I have a custom SimpleCursorAdapter and a list view. Each row of the list have a name and a button. When I press the button for each name, a dialog appears with a description.
Inside the custom SimpleCursorAdapter I set the onclick method for the button. When I have a large list, my listView gets a scroll bar. And I dont know why, when I scroll down, the last rows of my list doesnt show the correct description for each row. This is my code:
public class listServicesCursorAdapter extends SimpleCursorAdapter{
private Context context;
private int layout;
private String[] from;
private int[] to;
public listServicesCursorAdapter (Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
this.context = context;
this.layout = layout;
this.from = from;
this.to = to;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
//Column of BD that we want to recover
String column = null;
//Index of the column of DB
int nameCol = 0;
//Result of obtain the index of the column of DB
String nombre = null;
//Name of the textView in the Layout where we want to show the result
TextView name_text= null;
String description = null;
String nameService = null;
//For each value of DB, we show it in the text view.
for (int i=0; i<from.length; i++){
column= from[i];
nameCol = cursor.getColumnIndex(column);
name = cursor.getString(nameCol);
//the values to[i] equals to 0 indicates values that we need but
//that we are not showing in the list directly
//0 -> description
if(to[i] == 0){
description = name;
}else{
nameService = name;
name_text = (TextView) v.findViewById(to[i]);
if (name_text != null) {
name_text.setText(name);
}
}
}
ImageButton buttonDescription = (ImageButton) v.findViewById(R.id.imageButtonDescription);
//we store in a bundle the name and description of the service, so we can use it in
// the setOnClickListener method.
final Bundle mArguments = new Bundle();
mArguments.putString("name", nameService);
mArguments.putString("description", description);
buttonDescription .setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setMessage(mArguments.getString("description"))
.setTitle(mArguments.getString("name"))
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}});
return v;
}
}
This is where I call the adapter:
ServiceSqliteDao serviceDao = new ServiceSqliteDao();
//get the services for DB
Cursor mCursorServices = serviceDao.listServices(getActivity());
if(mCursorServices.getCount()>0){
//indicate the fields we want to show (from) and where (to)
String[] from = new String[] { "name", "description"};
int[] to = new int[] { R.id.checkBoxService,0};
ListView lvServices = (ListView) v.findViewById (R.id.listViewServices);
ListServicesCursorAdapter notes = new ListServicesCursorAdapter (getActivity(), R.layout.activity_file_service, mCursorServices, from, to, 0);
lvServices.setAdapter(notes);
Why do I get this behavior?. I get all the names in the list right but when I press the button in horizontal way (I mean a put the tablet horizontally) and get the scroll bar in my list, I dont get the right description. By the other hand, if I use the tablet vertically, I dont get the scroll bar in my list and I get the right description in each button.
This is my layout:
<ListView
android:id="#+id/listViewServices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" >
</ListView>
SOLUTION:
newView should look like this:
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
return v;
}
and bindView should look like this:
#Override
public View bindView(View v, Context context, Cursor cursor) {
//Column of BD that we want to recover
String column = null;
//Index of the column of DB
int nameCol = 0;
//Result of obtain the index of the column of DB
String nombre = null;
//Name of the textView in the Layout where we want to show the result
TextView name_text= null;
String description = null;
String nameService = null;
//For each value of DB, we show it in the text view.
for (int i=0; i<from.length; i++){
column= from[i];
nameCol = cursor.getColumnIndex(column);
name = cursor.getString(nameCol);
//the values to[i] equals to 0 indicates values that we need but
//that we are not showing in the list directly
//0 -> description
if(to[i] == 0){
description = name;
}else{
nameService = name;
name_text = (TextView) v.findViewById(to[i]);
if (name_text != null) {
name_text.setText(name);
}
}
}
/********************************NEW CODE ************************************/
String uniMedition = cursor.getString(cursor.getColumnIndex("unitMedition"));
if(uniMedition.equals("none")){
EditText etMedida = (EditText) v.findViewById(R.id.editTextMedida);
etMedida.setVisibility(View.INVISIBLE);
TextView tvUniMedition = (TextView) v.findViewById(R.id.textViewUniMedition);
tvUniMedition .setVisibility(View.INVISIBLE);
}else{
EditText etMedida = (EditText) v.findViewById(R.id.editTextMedida);
etMedida.setVisibility(View.VISIBLE);
TextView tvUniMedition = (TextView) v.findViewById(R.id.textViewUniMedition);
tvUniMedition .setVisibility(View.VISIBLE);
tvUniMedition .setText(uniMedition);
}
/********************************END NEW CODE ************************************/
ImageButton buttonDescription = (ImageButton) v.findViewById(R.id.imageButtonDescription);
//we store in a bundle the name and description of the service, so we can use it in
// the setOnClickListener method.
final Bundle mArguments = new Bundle();
mArguments.putString("name", nameService);
mArguments.putString("description", description);
buttonDescription .setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setMessage(mArguments.getString("description"))
.setTitle(mArguments.getString("name"))
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}});
}
}
Now everything works fine!.
Why do I get this behavior?. I get all the names in the list right but
when I press the button in horizontal way (I mean a put the tablet
horizontally) and get the scroll bar in my list, I dont get the right
description.
When your ListView doesn't have space to show all of the rows it will recycle the row view for performance reasons. The problem is that in your SimpleCursorAdapter you override the newView() method which will be called only when the ListView doesn't have a recycled view. Override bindView() to do the work as that method is called for each row, in the newView() method just inflate/build the row layout.
I am writing an App in which i am trying to allow user to edit a quantity of an Item already in Cart, by Tap on Item in List View..
I want to show Quantity entered by user in EditText, when do Tap on an Item in Cart
How can i show description of an Item, when do Tap on an Item in Cart
[like : still i am just getting description word, not getting description of an
Item which i am showing in Screen Number:2]
I am using below code in getView() method in CartAdapter.java:
public static final String KEY_TITLE = "title";
public static final String KEY_COST = "cost";
public static final String KEY_TOTAL = "total";
public static final String KEY_QTY = "qty";
public static final String KEY_DESCRIPTION = "description";
public static final String KEY_THUMB_URL = "imageUri";
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View vi = convertView;
if (convertView == null)
vi = myinflater.inflate(R.layout.listrow_cart_edit, null);
vi.setClickable(true);
vi.setFocusable(true);
vi.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v)
{
HashMap<String, String> item = Session.item_data.get(position);
Intent intent= new Intent(myactivity, com.easy.shoppingcart.SingleItem.class);
intent.putExtra(KEY_TITLE, item.get(com.easy.shoppingcart.SingleItem.KEY_TITLE));
intent.putExtra(KEY_COST, item.get(com.easy.shoppingcart.SingleItem.KEY_COST));
intent.putExtra(KEY_TOTAL, item.get(com.easy.shoppingcart.SingleItem.KEY_TOTAL));
intent.putExtra(KEY_QTY, item.get(com.easy.shoppingcart.SingleItem.KEY_QTY));
intent.putExtra(KEY_DESCRIPTION, (com.easy.shoppingcart.SingleItem.KEY_DESCRIPTION));
intent.putExtra(KEY_THUMB_URL, com.easy.shoppingcart.SingleItem.KEY_THUMB_URL);
myactivity.startActivity(intent);
}
});
Note: To get Description of an Item, i also tried with below code, but not worked, i know here is very small change need to do, but really i don't know what is that change:
intent.putExtra(KEY_DESCRIPTION, item.get(com.easy.shoppingcart.SingleItem.KEY_DESCRIPTION));
Intent intent= new Intent(myactivity, com.easy.shoppingcart.SingleItem.class);
intent.putExtra("position", position);
startActivity(intent);
on the other side of activity(SingleItem) inside oncreate()
int position;
if(this.getIntent().getExtras() != null) {
Bundle bundle = this.getIntent().getExtras();
position = Integer.parseInt(bundle.getString("position"));
}
Use this position to get the values.
HashMap<String, String> item = Session.item_data.get(position);
I have created a screen with 5 rows of information. each row has 2 values. (value1 and value2)
At the end of each row is a button. For each row I have created a xml file that I am looping though and filling in the values from a database
what I am looking at doing is when the button is pressed a dialog is opened with two editviews filled in with the values from that row
Then when the button is pressed on the dialog the values are updated in database.
I am having problems with the createdialog / preparedialog methods and passing the values to them.
UPDATE with Code
public void createLayout(LinearLayout pMainlayout, String pMajorValue) {
DatabaseTools db = new DatabaseTools(this);
majorValue= pMajorValue;
nameInfo = db.getNames(pMajorValue);
name = returnName(pMajorValue);
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (int i = 0; i < nameInfo.size(); i++) {
// Get views
View view = (View) inflater.inflate(R.layout.mainlayout, null);
TextView nameView = (TextView) sightmarkView.findViewById(R.id.name_title);
TextView Value1View = (TextView) sightmarkView.findViewById(R.id.value1);
TextView ValueView = (TextView) sightmarkView.findViewById(R.id.value2);
Button updateButton = (Button) sightmarkView.findViewById(R.id.updatebutton);
// Get info from Database
nameValue = nameInfo.get(i).toString();
value1v = db.getMark(majorValue, nameInfo.get(i).toString());
value2v = db.getMark(majorValue, nameInfo.get(i).toString());
// populate info into fields
nameView.setText(nameValue);
Value1View.setText(String.valueOf(value1));
Value2View.setText(String.valueOf(value2));
updateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Bundle bundle = new Bundle();
//Code here to add values to a bundle
showDialog(SIGHTMARK_DIALOG_ID, bundle);
}
});
pMainlayout.addView(view);
}
}
All this work correctly. The values are displayed. I have looked into using a bundle to pass them into the dialog
#Override
protected Dialog onCreateDialog(int id, Bundle bundle) {
switch(id) {
case DIALOG_ID:
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View layout = inflater.inflate(R.layout.update, (ViewGroup) findViewById(R.id.update));
final EditText value1Edit = (EditText) layout.findViewById(R.id.value1_current);
final EditText value2Edit = (EditText) layout.findViewById(R.id.value2_current);
//next pull values from bundle to populate into fields
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(layout);
// Now configure the AlertDialog
builder.setTitle(R.string.sight_update_title);
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
this.removeDialog(DIALOG_ID);
}
});
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
value1New = Float.parseFloat(value1Edit.getText().toString());
value2New = Float.parseFloat(value2Edit.getText().toString());
// method run to update the database with the new values passed to it
updateMarks(value1, value2);
this.removeDialog(DIALOG_ID);
}
});
// Create the AlertDialog and return it
AlertDialog Dialog = builder.create();
return Dialog;
}
return null;
}
This part has issues and is not getting the values correctly or passing them to the update method
i think the problem is here:
If you use showDialog(int), the activity will call through to this
method the first time, and hang onto it thereafter. Any dialog that is
created by this method will automatically be saved and restored for
you, including whether it is showing.
remove references
removeDialog(SIGHTMARK_DIALOG_ID);
showDialog(SIGHTMARK_DIALOG_ID, bundle);
documentation