I developed an application to my school and one of the features is principal's updates. For this, I'm using the Firebase realtime database. I need that the app will send automatically notification(background) when a new child(message) is added.
this is my read code(swift):
var messages = [Message]()
override func viewDidLoad() {
super.viewDidLoad()
observeMessages()
}
func observeMessages() {
let dbRef = FIRDatabase.database().reference().child("messageS")
dbRef.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let text = Message()
text.setValuesForKeys(dictionary)
self.messages.insert(text, at: 0)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}, withCancel: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let message = messages[indexPath.row]
cell.textLabel?.text = message.title
cell.textLabel?.textAlignment = .center
cell.detailTextLabel?.text = message.message
cell.detailTextLabel?.textAlignment = .right
// Configure the cell...
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let message = messages[indexPath.row]
let title = message.title
let msg = message.message
let controller = UIAlertController(title: title, message: msg, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "סגור", style: .cancel) { (handler) in
// Any action when you cancel
}
controller.addAction(cancelAction)
self.present(controller, animated: true, completion: nil)
}
this is my write code(swift):
#IBOutlet weak var MessageTitle: UITextField!
#IBOutlet weak var MessageText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func post() {
let title: String = MessageTitle.text!
let message: String = MessageText.text!
let post: [String: AnyObject] = ["title": title as AnyObject,
"message": message as AnyObject]
let dbRef = FIRDatabase.database().reference()
dbRef.child("messageS").childByAutoId().setValue(post)
}
#IBAction func Publish(_ sender: AnyObject) {
post()
let MessageSent = UIAlertController(title: "נשלח", message: "ההודעה נשלחה בהצלחה", preferredStyle: UIAlertControllerStyle.alert)
MessageSent.addAction(UIAlertAction(title: "תודה", style: UIAlertActionStyle.default, handler: nil))
self.present(MessageSent, animated: true, completion: nil)
}
this is my read code(android):
private Firebase mRef;
public ArrayList<String> messages = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_updates);
mRef = new Firebase("https://ksharet-d6a24.firebaseio.com");
mRef.child("messageS");
// Get ListView object from xml
final ListView listView = (ListView) findViewById(R.id.ListView);
// Create a new Adapter
final ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, messages);
// Assign adapter to ListView
listView.setAdapter(adapter);
mRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
Message message = postSnapshot.getValue(Message.class);
messages.add(0, message.getTitle() + '\n' + message.getMessage());
}
adapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
this is my write code(android):
private Button sendData;
private Firebase mRef;
private EditText messageBox = (EditText) findViewById(R.id.MessageText);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_publish);
Firebase.setAndroidContext(this);
mRef = new Firebase("https://ksharet-d6a24.firebaseio.com/");
mRef.child("message");
sendData = (Button) findViewById(R.id.sendMessage);
sendData.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String message = messageBox.getText().toString();
//Firebase mRefChild = mRef.child("message");
//mRefChild.setValue(message);
mRef.push().setValue(message);
}
});
}
what should I create or change in the code(if you can, for both iOS and Android).
I recently purchased the Apple Developer Program membership and It'll probably be activated soon.
thanks for the helpers.
so, after a lot of time that I've been searching for solutions, I found a really easy way to do it, and it is by using cloud kit. that's the brian advent's tutorial:
https://youtu.be/BXl5o5_2aEU
I suggest you to go to his first video: https://youtu.be/XQ3nLV2778U and just do few of those things.
EDIT: 2017
Now we can use Firebase cloud functions in order to send a push notification when the database changes.
Related
I was following a tutorial online on how to create an android chat app with Xamarin that was implemented in Visual Studio 2015 and I was using Visual Studio 2017. I also tried to InitializeApp to see if that would fix it, but it did not work. What could be the problem? There was a couple of changes that I implemented, but I have one problem. I am getting an error "Java.Lang.IllegalStateException: " on line FirebaseDatabase.Instance.GetReference("chats").AddValueEventListener(this);
This is how I implented the OnCreate method:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
FirebaseApp.InitializeApp(this);
firebase = new FirebaseClient(FirebaseURL);
//firebase = new FirebaseClient(GetString(Resource.String.firebase_url)); //not working
FirebaseDatabase.Instance.GetReference("chats").AddValueEventListener(this);
fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
edtChat = FindViewById<EditText>(Resource.Id.input);
lstChat = FindViewById<ListView>(Resource.Id.list_of_messages);
fab.Click += delegate
{
PostMessage();
};
if (FirebaseAuth.Instance.CurrentUser == null)
{
StartActivityForResult(new Android.Content.Intent(this, typeof(Signin)), MyResultCode);
}
else
{
Toast.MakeText(this, "Welcome " + FirebaseAuth.Instance.CurrentUser.Email, ToastLength.Short).Show();
DisplayChatMessage();
}
}
private async void PostMessage()
{
var items = await firebase.Child("chats").PostAsync(new MessageContent(FirebaseAuth.Instance.CurrentUser.Email, edtChat.Text));
edtChat.Text = "";
}
public void OnCancelled(DatabaseError error)
{
}
public void OnDataChange(DataSnapshot snapshot)
{
DisplayChatMessage();
}
private async void DisplayChatMessage()
{
lstMessage.Clear();
var items = await firebase.Child("chats")
.OnceAsync<MessageContent>();
foreach (var item in items)
lstMessage.Add(item.Object);
ListViewAdapter adapter = new ListViewAdapter(this, lstMessage);
lstChat.Adapter = adapter;
}
I am making a demo project to change color of a simple TextView using Firebase Remote Config. But the function mRemoteConfig.fetch(cacheExpiration) .addOnCompleteListener... does not trigger at all. The text color does not change.
Here is my code :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text);
initRemoteConfig();
}
private void initRemoteConfig() {
mRemoteConfig = FirebaseRemoteConfig.getInstance();
HashMap<String, Object> defaults = new HashMap<>();
defaults.put("color_primary", getString(R.string.color_primary));
mRemoteConfig.setDefaults(defaults);
FirebaseRemoteConfigSettings remoteConfigSettings = new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(true)
.build();
mRemoteConfig.setConfigSettings(remoteConfigSettings);
fetchRemoteConfigValues();
}
private void fetchRemoteConfigValues() {
long cacheExpiration = 3600;
//expire the cache immediately for development mode.
if (mRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled()) {
cacheExpiration = 0;
}
mRemoteConfig.fetch(cacheExpiration)
.addOnCompleteListener(this, new OnCompleteListener<Void>() {
#Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
// task successful. Activate the fetched data
mRemoteConfig.activateFetched();
setupView();
} else {
//task failed
}
}
});
}
private void setupView() {
setTextColor();
}
private void setTextColor() {
boolean isPromoOn = true;
int color = isPromoOn ? Color.parseColor(mRemoteConfig.getString("color_primary")) :
ContextCompat.getColor(this, R.color.color_primary);
text.setTextColor(color);
}
Am I missing something ? Thanks for help :)
If you have set Firebase Console correctly, I suggest you check whether the newest Google Play Services is available on your phone and work.
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"
}
}
}
I am trying to bind the following class to a relaycommand.
public class UserAuth
{
public string UserName { get; set; }
public string Password { get; set; }
}
This is my MainActivity Class:
public partial class MainActivity : ActivityBaseEx
{
private Binding<string, UserAuth> _userInformation;
private Binding<string, UserAuth> _cool;
public LoginViewModel LoginViewModel
{
get
{
return App.Locator.Login;
}
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
_userInformation = this.SetBinding(()=> **....... WHAT GOES HERE!! I can do this for a simple string, but cannot figure it out for a class!**
// Get our button from the layout resource and attach an event to it
var signInButton = FindViewById<Button>(Resource.Id.btnSingIn);
signInButton.SetCommand("Click", LoginViewModel.LoginCommand, _userInformation);
}
}
This is my RelayCommand in my View Model
public RelayCommand<UserAuth> LoginCommand
{
get
{
return _loginCommand ?? (_loginCommand = new RelayCommand<UserAuth>(
async (userAuth) =>
{
_isLoading = true;
try
{
// var loggedIn = await _loginService.AuthenticateUser("emediaqa1", "p098765");
var loggedIn = await _loginService.AuthenticateUser(userAuth.UserName, userAuth.Password);
_isLoading = false;
}
catch (Exception ex)
{
var dialog = ServiceLocator.Current.GetInstance<IDialogService>();
dialog.ShowError(ex, "Error Authenticating", "OK", null);
}
_isLoading = false;
}));
}
}
My problem is with this line:
_userInformation = this.SetBinding(()=> // WHAT GOES HERE!! I can do this for a simple
//string, but cannot figure it out for a class!
Please help!
Thanks!!
I use something like this in the MainActivity.OnCreate:
_usernameBinding = this.SetBinding(() => Vm.userAuth.Username, () => Username.Text, BindingMode.TwoWay);
_passwordBinding = this.SetBinding(() => Vm.userAuth.Password, () => Password.Text, BindingMode.TwoWay);
So you need 2 bindings, one for userName and one for Password.