salam
is it possible to get all channels that authenticate user is access to it?
I want to show user documents in the categories of channels
add "channels" peroperty in documents and then :
com.couchbase.lite.View channelView = _database.getView("channels");
channelView.setMap(new Mapper() {
#Override
public void map(Map<String, Object> document, Emitter emitter) {
ArrayList<String> channel = (List) document.get("channel");
String name = (String) document.get("ch_name");
emitter.emit(channel, name);
}
}, "2");
private void startLiveQuery(com.couchbase.lite.View view) throws Exception {
if (_liveQuery == null) {
_liveQuery = view.createQuery().toLiveQuery();
_liveQuery.addChangeListener(new LiveQuery.ChangeListener() {
public void changed(final LiveQuery.ChangeEvent event) {
new Thread(new Runnable() {
#Override
public void run() {
for (final Iterator<QueryRow> it = event.getRows(); it.hasNext(); ) {
QueryRow query = it.next();
_channel = (String) query.getKey();
_name = (String) query.getValue();
}
}
}).start();
}
});
_liveQuery.start();
}
}
Related
I have a method here which loads data from Firestore and populates in My View. Here i Am trying to use a progressbar. I kept the logic for progress inside my Thread but it seems like something is wrong here as it wont come out of loop. This code is working fine in other place.
pStatus is declared as of type int. Please help as m stuck on it since whole day
private void loadDataFromFirebase() {
progressBar.setVisibility(View.VISIBLE);
final Handler handler2 = new Handler();
new Thread(new Runnable() {
#Override
public void run() {
while (pStatus <= 100) {
handler2.post(new Runnable() {
#Override
public void run() {
Log.e("Thread","Running Thread Load Data");
Log.e("Thread",String.valueOf(pStatus));
progressBar.setProgress(pStatus);
if(pStatus==100){
progressBar.setVisibility(View.INVISIBLE);
}
}
});
}
}
}).start();
firebaseFirestore.collection("Merchant").document(id)
.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Log.e("Thread","Query");
Map<String, Object> temp = documentSnapshot.getData();
Map<String, Object> temp2 = (Map<String, Object>) temp.get("Items");
Iterator<Map.Entry<String, Object>> it = temp2.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
String Item_name = entry.getKey();
Map<String, String> items = new HashMap<>();
items = (Map<String, String>) entry.getValue();
Iterator<Map.Entry<String, String>> it2 = items.entrySet().iterator();
ArrayList<String> list = new ArrayList<>();
String a = "";
String b = "";
String c = "";
String d = "";
while (it2.hasNext()) {
Map.Entry<String, String> entry2 = it2.next();
System.out.println("Key = " + entry2.getKey() +
", Value = " + entry2.getValue());
if (entry2.getKey().equals("Name")) {
a = entry2.getValue();
} else if (entry2.getKey().equals("Metric")) {
b = entry2.getValue();
} else if (entry2.getKey().equals("Price")) {
c = entry2.getValue();
} else if (entry2.getKey().equals("Quantity")) {
d = entry2.getValue();
}
}
list.add(a);
list.add(b);
list.add(c);
list.add(d);
System.out.println(list);
OrderItemModel order = new OrderItemModel(list.get(0), list.get(1),
Integer.parseInt(list.get(2)), Integer.parseInt(list.get(3)));
orderItemModelArrayList.add(order);
}
adapter = new RecyclerViewAdapter(OrderItemList.this, orderItemModelArrayList);
recyclerView.setAdapter(adapter);
pStatus=pStatus+100;
}
});
}
It seems that the thread you handle data is the main thread(UI thread),you can print a log to verify this. But your intention is to put it in the child thread.
Below is my logic:
private static final int HANDLE_DATA = 1;
private Handler uiHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
List<String> data = msg.obj;
// set adapter data and refresh recyclerview
...
progressBar.setViesibility(View.INVISIBLE);
}
}
private HandlerThread thread = new HandlerThread("worker_thread");
private Handler handler = new Handler(thread.getLooper()) {
#Override
public void handleMessage(Message msg) {
if (msg.what == HANDLE_DATA) {
DocumentsSnapShot data = (DocumentsSnapShot)msg.obj;
// handle data
...
Message msg = uiHandler.obtainMessage(0, handleResultData);
uiHandler.postMsg(msg);
}
}
};
public void onCreate() {
...
thread.start();
...
}
private void loadDataFromFirebase() {
progressBar.setVisibility(View.VISIBLE);
firebaseFirestore.collection("Merchant").document(id)
.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Message msg = handler.obtainMessage(HANDLE_DATA, documentSnapshot);
handler.postMessage(msg);
}
}
}
..
Hello My app is freezes ui for some seconds while it is fetching data from network and stores it in db and then shows it in recyclerview. For fetching data from network I am using retrofit and for storing it and fetching form db Room library. Both with the help of MVVM pattern. Is there a way to remoove the UI freeze?
Here is my code:
In the Mainactivity when clicking download btn
downloadBtn.setOnClickListener(v ->
eventsViewModel.insertEvents(this));
Viewmodel class:
public void insertEvents(Context context){
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String token = preferences.getString("token", "");
final Map<String,String> queryData = new HashMap<>();
queryData.put("token", token);
Call<EventsResponse> call = RetrofitClient.getmInstance().getApi().getEvents(queryData);
call.enqueue(new Callback<EventsResponse>() {
#Override
public void onResponse(Call<EventsResponse> call, Response<EventsResponse> response) {
if (response.code() == 401){
String email = preferences.getString("email", "");
String password = preferences.getString("password", "");
Call<LoginResponse> call1 = RetrofitClient.getmInstance().getApi().loginuser(email, password);
call1.enqueue(new Callback<LoginResponse>() {
#Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if (response.code() == 200){
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); // 0 - for private mode
SharedPreferences.Editor editor = pref.edit();
editor.putString("token", response.body().getToken());
editor.apply();
insertEvents(context);
}
else {
}
}
#Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
}
});
}
if (response.code() == 200){
eventList = response.body().getData();
EventsTable eventsTable = new EventsTable();
TicketDatesTable ticketDatesTable = new TicketDatesTable();
for (int i = 0; i < eventList.size(); i++) {
eventsTable.setEvent_id(eventList.get(i).getId());
eventsTable.setTitle_tk(eventList.get(i).getTitle_tk());
eventsTable.setTitle_ru(eventList.get(i).getTitle_ru());
eventsTable.setImageURL("https://bilettm.com/" + eventList.get(i).getImage_url());
eventsTable.setStart_date(eventList.get(i).getStart_date());
eventsTable.setEnd_date(eventList.get(i).getEnd_date());
eventsTable.setSales_volume(eventList.get(i).getEnd_date());
eventsTable.setOrganiser_fees_volume(eventList.get(i).getOrganiser_fees_volume());
eventsTable.setViews(eventList.get(i).getViews());
eventsTable.setSales_volume(eventList.get(i).getSales_volume());
eventsTable.setIs_live(eventList.get(i).getIs_live());
if (!eventList.get(i).getTicket_dates().isEmpty()) {
showTimeList = eventList.get(i).getTicket_dates();
int b = 0;
while (b < showTimeList.size()) {
ticketDatesTable.setEvent_id(showTimeList.get(b).getEvent_id());
ticketDatesTable.setTicket_date(showTimeList.get(b).getTicket_date());
insertTicketDates(ticketDatesTable);
try {
Thread.sleep(150);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
b++;
}
}
insert(eventsTable);
try {
Thread.sleep(150);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
#Override
public void onFailure(Call<EventsResponse> call, Throwable t) {
}
});
}
public void insert(EventsTable data){
repository.insertEvents(data);
}
public void insertTicketDates(TicketDatesTable ticketDatesTable){
repository.insertTicketDates(ticketDatesTable);
Here is my repository :
public void insertEvents(EventsTable data){
new EventInsertion(eventsDAO).execute(data);
}
private static class EventInsertion extends AsyncTask<EventsTable, Void, Void> {
private EventsDAO eventsDAO;
private EventInsertion(EventsDAO eventsDAO) {
this.eventsDAO = eventsDAO;
}
#Override
protected Void doInBackground(EventsTable... eventsTables) {
eventsDAO.insertEvents(eventsTables[0]);
return null;
}
}
public void insertTicketDates(TicketDatesTable data){
new TicketDatesInsertion(eventsDAO).execute(data);
}
private static class TicketDatesInsertion extends AsyncTask<TicketDatesTable, Void, Void> {
private EventsDAO eventsDAO;
private TicketDatesInsertion(EventsDAO eventsDAO) {
this.eventsDAO = eventsDAO;
}
#Override
protected Void doInBackground(TicketDatesTable... ticketDatesTables) {
eventsDAO.insertTicketDates(ticketDatesTables[0]);
return null;
}
}
Here is my DAO:
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertEvents(EventsTable data);
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertTicketDates(TicketDatesTable datesTable);
I think it freezes when it is storing it into sqlite db
I found my problem. It was initializing entity before starting for loop:
BEFORE:
EventsTable eventsTable = new EventsTable();
for (int i = 0; i < eventList.size(); i++) {
INSERT();
}
AFTER:
for (int i = 0; i < eventList.size(); i++) {
EventsTable eventsTable = new EventsTable();
INSERT();
}
A better solution would be to collect all your required objects in an ArrayList and then pass it on to the AsyncTask and from there to DAO for bulk insertion.
And remove all Thread.sleep(150) statements as they serve no purpose.
why you are using this Thread.sleep(150);Call is already a background task in retrofit
I have used rxjava and retrofit to load data from backend and update the UI.
But there is no data displayed on section view. I have tested it, and the backend data load successful and the UI can be updated using fake data.
Are there something wrong when I use Rxjava?
private void retrieveCardInfo(String stripeId, String userToken) {
subscriptions.add(NetworkUtil.getRetrofit(userToken).getCustomerInfo(new GetCustomer(stripeId))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleCustomerResponse, this::handleError));
}
private void handleCustomerResponse(CreateStripeCustomerResponse response) {
if (response.getSuccess()) {
updateCardList(response);
bankSection.setState(Section.State.EMPTY);
} else {
Utils.toast(this,"Get credit card failed");
}
}
private void updateCardList(CreateStripeCustomerResponse response) {
List<CardInfo> cardList = response.getCustomer().getSources().getData();
if (cardList == null || cardList.size() == 0) {
cardSection.setState(Section.State.EMPTY);
} else {
list = new ArrayList<>();
for (int i = 0; i < cardList.size(); i++) {
CardInfo cardInfo = cardList.get(i);
String brand = cardInfo.getBrand();
String subTitle = cardInfo.getFunding() + "****" + cardInfo.getLast4();
list.add(new PaymentAccountItem(brand, subTitle, cardDrawable.get(brand)));
}
list.add(new PaymentAccountItem("title", "subtitle", R.drawable.ic__credit_amex_svg));
cardSection.swipeData(list);
}
}
private void handleError(Throwable throwable) {
}
// works fine without sectionedAdapter.notifyDataSetChanged(); when using fake data,
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_payment);
ButterKnife.bind(this);
subscriptions = new CompositeSubscription();
initialToolbar();
initialRecyclerView();
initialPaymentData();
}
private void initialPaymentData() {
stripeIdAndToken = Utils.getStripeIdAndToken(this);
if (TextUtils.isEmpty(stripeIdAndToken.first)) {
cardSection.setState(Section.State.EMPTY);
bankSection.setState(Section.State.EMPTY);
} else {
initialCardDrawableResource();
retrieveCardInfo(stripeIdAndToken.first, stripeIdAndToken.second);
}
// fake data here
// initialCardDrawableResource();
// list = new ArrayList<>();
// list.add(new PaymentCreditCardItem("Visa", "123456", 10, 2018, cardDrawable.get("Visa")));
// cardSection.swipeData(list);
}
private void initialCardDrawableResource() {
cardDrawable = new HashMap<>();
cardDrawable.put("Visa", R.drawable.ic_visa_svg);
cardDrawable.put("Discover", R.drawable.ic_discover_svg);
cardDrawable.put("American Express", R.drawable.ic__credit_amex_svg);
cardDrawable.put("Mastercard", R.drawable.cio_ic_mastercard);
}
private void retrieveCardInfo(String stripeId, String token) {
subscriptions.add(NetworkUtil.getRetrofit(token).getCustomerInfo(new GetCustomer(stripeId))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleCustomerResponse, this::handleError));
}
private void handleCustomerResponse(CreateStripeCustomerResponse response) {
if (response.getSuccess()) {
updateCardList(response);
} else {
Utils.toast(this, "Get credit card failed");
}
}
private void updateCardList(CreateStripeCustomerResponse response) {
List<CardInfo> cardList = response.getCustomer().getSources().getData();
if (cardList == null || cardList.size() == 0) {
cardSection.setState(Section.State.EMPTY);
} else {
list = new ArrayList<>();
for (int i = 0; i < cardList.size(); i++) {
CardInfo cardInfo = cardList.get(i);
String brand = cardInfo.getBrand();
String cardNum = cardInfo.getFunding() + "****" + cardInfo.getLast4();
list.add(new PaymentCreditCardItem(brand, cardNum, cardInfo.getExpMonth(), cardInfo.getExpYear(), cardDrawable.get(brand)));
}
cardSection.swipeData(list);
sectionedAdapter.notifyDataSetChanged();
}
}
private void handleError(Throwable throwable) {
}
private void initialRecyclerView() {
sectionedAdapter = new SectionedRecyclerViewAdapter();
cardSection = new PaymentCardAndAccountSection(this, R.layout.header_card, R.layout.payment_card_empty_view);
bankSection = new PaymentCardAndAccountSection(this, R.layout.header_bank, R.layout.payment_account_empty_view);
sectionedAdapter.addSection(cardSection);
sectionedAdapter.addSection(bankSection);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(sectionedAdapter);
bankSection.setState(Section.State.EMPTY);
}
private void initialToolbar() {
toolbar.setTitle("Payment");
toolbar.setNavigationIcon(R.drawable.ic_back_svg);
setSupportActionBar(toolbar);
}
#Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
#OnClick(R.id.fab_add_payment)
void launchAddPaymentDialog() {
AddPaymentDialogFragment addPaymentDialogFragment = AddPaymentDialogFragment.newInstance();
addPaymentDialogFragment.setStyle(DialogFragment.STYLE_NO_FRAME, 0);
addPaymentDialogFragment.show(getSupportFragmentManager(), "dialog");
}
#Override
public void onPause() {
super.onPause();
if (subscriptions != null) {
subscriptions.clear();
}
}
Do you use an adapter? In this case adapter.notifyDataSetChanged();
Also, print yout error: throwable.printStackTrace(); in order to see if something goes wrong.
You need to add .observeOn(AndroidSchedulers.mainThread()) after you write subsribeOn() to tell your observable to perform your onNext callback on UI thread/MainThread.
in my android application I create an activity which contains a ListView which is populated with data from Firebase Database.
The JSON Tree of the structure of the database is the following:
{
"companies" : {
"companyX" : {
"address" : "50th avenue, NY",
"name" : "Spare-Tools Ltd."
},
"companyZ" : {
"address" : "50th Broadway, NY",
"name" : "Burgers and Burgers"
}
},
"company-requests" : {
"companyX" : {
"req1" : true
"req2" : true
}
},
"requests" : {
"req1" : {
"destination" : "Upper Tooting 122, Bronx",
"origin" : "Philadelphia",
"time" : "1473593287",
...
}
"req2" : {
...
}
}
}
I want to populate the ListView with the list of requests from the requests node. But I first need to know all requests that belong to a specific company so I first go to the company-requests node and retrieve all the request-keys belonging to the specific company.
The problem I am facing is that the ListView is created before the final data from the database arrived:
public class RequestsListActivity extends AppCompatActivity {
private ListView rListView;
DatabaseReference rootNode = FirebaseDatabase.getInstance().getReference();
#Override
protected void onCreate(Bundle savedInstanceState) {
...
rListView = (ListView) findViewById(R.id.result_list_view);
//First I retrieve all the requests of a specific company
DatabaseReference companyRequests = rootNode.child("company-requests/companyX");
companyRequests.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Then I retrieve all the keys of these requests
...
while (iterator.hasNext()) {
String key = iterator.next().getKey();
//For each key I retrieve its details from the requests node
DatabaseReference currRequest = rootNode.child("requests/" + key);
currRequest.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String time;
time = (String) dataSnapshot.child("time").getValue();
Request request = new Request(time);
allRequests.add(request);
}
...onCancelled...
});
}
//THIS CODE IS EXECUTED TO EARLY: BEFORE WE HAVE ANY DATA FROM FIREBASE
RequestAdapter adapter = new RequestAdapter(RequestsListActivity.this, allRequests);
rListView.setAdapter(adapter);
}
...onCancelled...
});
}
}
How can I insert a wait (spinner?) that waits until the values are loaded from Firebase?
You can use a simple counter to keep track of the number of pending loads:
companyRequests.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
// at the start we need to still load all children
final long[] pendingLoadCount = { dataSnapshot.getChildrenCount() };
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
//For each key I retrieve its details from the requests node
DatabaseReference currRequest = rootNode.child("requests/" + childSnapshot.getKey());
currRequest.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
String time;
time = (String) dataSnapshot.child("time").getValue();
Request request = new Request(time);
allRequests.add(request);
// we loaded a child, check if we're done
pendingLoadCount[0] = pendingLoadCount[0] - 1;
if (pendingLoadCount[0] == 0) {
RequestAdapter adapter = new RequestAdapter(RequestsListActivity.this, allRequests);
rListView.setAdapter(adapter);
}
}
...onCancelled...
});
}
}
});
I solved this using a java.util.concurrent.CountDownLatch:
In this example, replace EquityTotalListener with your implementation of ValueEventListener.
private void recalculate() {
final AtomicLong sumUpAll = new AtomicLong();
final CountDownLatch cnt = new CountDownLatch(mapUid2GeoLocation.keySet().size());
for (final String uid : mapUid2GeoLocation.keySet()) {
EquityTotalListener el = mapUid2EquityListener.get(uid);
if (el != null) {
if (logger.isDebugEnabled()) {
logger.debug("Listener for " + uid + " already set up");
cnt.countDown();
}
} else {
el = new EquityTotalListener(database.getDatabase(), uid) {
#Override
public void onCancelled(final DatabaseError databaseError) {
super.onCancelled(databaseError);
cnt.countDown();
}
#Override
protected void valueChanged(final String key, final Object value) {
if (value != null) {
sumUpAll.getAndAdd(Long.parseLong(value.toString()));
cnt.countDown();
}
};
}.attach();
mapUid2EquityListener.put(uid, el);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Waitung for countdown..");
}
try {
final boolean allGood = cnt.await(10, TimeUnit.SECONDS);
if (allGood) {
if (logger.isDebugEnabled()) {
logger.debug("Done waiting, " + uid + " owns " + sumUpAll.get() + " equity");
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("Waiting for read operations ran into timeout");
}
}
} catch (final InterruptedException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getLocalizedMessage(), e);
}
}
}
I am having a hard time trying to figure out how to add more items dynamically to a List in Firebase. As of now I am able to add just one item at the correct firebase location. The user needs to be able to add more items to the list. I am using a custom model class for the data. I would greatly appreciate any help, Thanks.
FloatingActionButton floatSave = (FloatingActionButton) rootView.findViewById(R.id.fabSave);
floatSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myFirebaseRef = new Firebase("https://you.firebaseio.com/");
myFirebaseRef = new Firebase("https://you.firebaseio.com/" + "/users/" + myFirebaseRef.getAuth().getUid());
String partyname = partyName.getText().toString();
String when = fromDateEtxt.getText().toString();
String timeOf = fromTimeEtxt.getText().toString();
String userItems1 = addThisItem.getText().toString();
userItems.add(userItems1);
Map<String,Object> values = new HashMap<>();
values.put("partyname", partyname);
values.put("When", when);
values.put("timeOf", timeOf);
values.put("userItems", userItems);
myFirebaseRef.push().setValue(values);
}
});
//Here is how I try to add additional items to the "userItems" List
final Button addItem = (Button) rootView.findViewById(R.id.buttonAddItem);
addItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SharedPreferences pref = getActivity().getSharedPreferences("MyPref", 0);
SharedPreferences.Editor editor = pref.edit();
String savedParty = pref.getString("thisPostKey", null);
myFirebaseRef = new Firebase("https://you.firebaseio.com/users/8d5d9915-54d8-4fc1-b92f-b45569e8089b/"+ savedParty + "/userItems");
String additem = addThisItem.getText().toString();
userItems.add(additem);
myFirebaseRef.push().setValue(additem);
System.out.println("There are " + thisKey + savedParty);
}
});
public class PartyPost {
private String partyname;
private String timeOf;
private String when;
private List userItems;
public PartyPost(String partyname, String timeOf, String when, List userItems) {
// empty default constructor, necessary for Firebase to be able to deserialize blog posts
this.partyname = partyname;
this.timeOf = timeOf;
this.when = when;
this.userItems = userItems;
}
public void setPartyname(String partyname) {
this.partyname = partyname;
}
public void setTimeOf(String timeOf) {
this.timeOf = timeOf;
}
public void setWhen(String when) {
this.when = when;
}
public void setUserItems(List<String> userItems) {
this.userItems = userItems;
}
public String getPartyname() {
return partyname;
}
public String getTimeOf() {
return timeOf;
}
public String getWhen() {
return when;
}
public List getUserItems() {
return userItems;
}
}
{
"users" : {
"8d5d9915-54d8-4fc1-b92f-b45569e8089b" : {
"-KDcHcfvc3CM-d8TWPE9" : {
"When" : "2-2-2017",
"partyname" : "Super Bowl",
"timeOf" : "5:00PM",
"userItems" : [ "Beer" ]
},
"-KDcHcjRbxXzCvRFa-No" : {
"userItems" : {
"-KDcLXIJ7I9TUFEDyyrA" : "Chips"
}
}
}
}
}
Your /userItems node has child node and per the question it has one child.
"userItems" : {
"-KDcLXIJ7I9TUFEDyyrA" : "Chips"
}
It appears you want to add additional children to that node. To add another child, you will need the path to that specific userItems node, here is pseudo-code
thisUsersUserItemsRef = /users/8d5d9915-54d8-4fc1-b92f-b45569e8089b/-KDcHcjRbxXzCvRFa-No/userItems
then push() the values
values.put("another_user_item", "docs ftw");
thisUsersUserItemsRef.push().setValue(values);
This will result in
"-KDcHcjRbxXzCvRFa-No" : {
"userItems" : {
"-KDcLXIJ7I9TUFEDyyrA" : "Chips",
"-JHoijoiqjodj8jkadiQ" {
"another_user_item": "docs ftw"
}
}
}