Listener for Realm Query changes - android

I have the following code in my activity where I am trying to fetch data from realm and then display it in a list. I have used a listener which works perfectly.
private RealmResults<Data> mData = repo.fetchData();
// internally the call is handled by
realm.where(Data.class).findAllAsync();
mData.addChangeListener(data -> {
// access to the data stored locally
// receive updates in case data changes
// set the data to the list
});
At a later point in the app, I want to also be able to filter the above data based on a user entered search string and receive the results in the same listener. I have tried the following code.
mData = poiRepo.fetchData("query");
That doesn't seem to work, I'm guessing because it returns a new list to which the listener is not attached. Is there a way I can listen for changes in the result of a realm query when the underlying data has not changed or any other way?
What I am trying to achieve.
mData.addChangeListener(data -> {
// single place where filtered data is delivered and sent to recycler view
});
function a(){
repo.fetchData( //filter parameters )
}
function b(){
repo.fetchData( //filter parameters )
}
function c(){
repo.fetchData( //filter parameters )
}

Related

ViewModel not updating the view on postValue

I used the lifecycle callback onCreate to fetch data like below
mWeOutViewModel.getPlaceListLiveData()
.observe(this, weOutItemViewModels -> {
AppLogger.i(getCustomTag() + "adding items " + weOutItemViewModels.size());
if (weOutItemViewModels != null && weOutItemViewModels.size() > 0)
mWeOutListAdapter.addToExisting(weOutItemViewModels);
});
As you can see the AppLogger output the initial size which is 0 when the fragment is displayed, then I fetch the data and call postValue (setValue crashes the app and it expected because I fetch data from the internet using a background thread). So I call post value like below :
private void updatePlaces(List<WeOutGroupedViewModels> weOutGroupedViewModels) {
List<WeOutGroupedViewModels> oldList = placeMutableLiveData.getValue();
oldList.addAll(weOutGroupedViewModels);
AppLogger.i(TAG +" updating places "+oldList.size());
placeMutableLiveData.postValue(oldList);
}
As you can see the other AppLogger before postValue, the size of the list is displayed(not empty), but nothing happens until the app crashes and nothing is shown in the logs. I have no ways of debugging since even on debug mode nothing happens. The post value doesn't trigger the observer.
I initialize the mutableLivedata like this :
private final MutableLiveData<List<WeOutGroupedViewModels>> placeMutableLiveData = new MutableLiveData<>();
and access like this :
public LiveData<List<WeOutGroupedViewModels>> getPlaceListLiveData() {
return placeMutableLiveData;
}
Event when I make the livedata public to access directly the livedata, there is no change (just in case someone thinks that's is where the issue comes from)
Instead of placeMutableLiveData.postValue(oldList);
I recommend using
placeMutableLiveData.postValue(Collections.unmodifiableList(new ArrayList<>(newList));
That way, the next time you access this list, you won't be able to mutate it in place, which is a good thing. You're not supposed to mutate the list inside a reactive state holder (MutableLiveData).
So theoretically it should look like this:
private void updatePlaces(List<WeOutGroupedViewModels> weOutGroupedViewModels) {
List<WeOutGroupedViewModels> newList = new ArrayList<>(placeMutableLiveData.getValue());
newList.addAll(weOutGroupedViewModels);
AppLogger.i(TAG +" updating places "+newList.size());
placeMutableLiveData.postValue(Collections.unmodifiableList(newList));
}

Check if firebase query (for firebase options) returns empty (does not exist)

I am using a filter query for a firebaseRecyclerAdapter (firebaseOptions).
Since I am using a searchView the recycler updates with every typed letter, if the query exists.
The problem is that I would like to clear the recycler if the query does not exist.
How can I add a check to see if the query is successful or not?
I am implementing a search, if the query exists I populate the recyclerview, if the query does not exist I want to clear the recyclerview.
public void fireSearch(String queryInput) {
String start = queryInput.toUpperCase(), end = queryInput.toLowerCase() + "\uf8ff";
Log.d("myTag", start + " " + end);
firebaseQuery = databaseReference.orderByKey().startAt(start).endAt(end);
searchRecyclerView.setVisibility(View.VISIBLE);
FirebaseRecyclerOptions<BusinessDetails> options =
new FirebaseRecyclerOptions.Builder<BusinessDetails>()
.setQuery(firebaseQuery, BusinessDetails.class)
.setLifecycleOwner(this)
.build();
}
If the query has no results, the FirebaseRecyclerViewAdapter will already clear the existing data. There is no need for you to handle this case yourself.
If you want to do some additional work when there is no data, you can override the onDataChanged method in your adapter class.
FirebaseRecyclerAdapter adapter = new FirebaseRecyclerAdapter<Chat, ChatHolder>(options) {
// ...
#Override
public void onDataChanged() {
// Called each time there is a new data snapshot. You may want to use this method
// to hide a loading spinner or check for the "no documents" state and update your UI.
// ...
}
#Override
public void onError(DatabaseError e) {
// Called when there is an error getting data. You may want to update
// your UI to display an error message to the user.
// ...
}
};
Also see the FirebaseUI documentation on data and error events, where I got the above code from.

Iterate through list of Optional object

In the below posted code, I would like to know how can I iterate through a list of Optional objects?
According to the code:
OptionalsUtils.toOptional(this.getUser_3())
.flatMap(user->user.getOptUserFavoriteTvList()
.filter(list-> list.get(???) != Optional.empty())
);#
Now i want to refer to each index in the list? how can i achieve it?
code:
OptionalsUtils.toOptional(this.getUser_3())
.flatMap(user->user.getOptUserFavoriteTvList()
.filter(list-> list.get(0) != Optional.empty())
);
private User getUser_3() {
List<Optional<String>> list = new ArrayList<>(5);
list.add(Optional.of("espn"));
list.add(Optional.of("Cnn"));
list.add(Optional.empty());
list.add(Optional.of("deutsch welle"));
User user = new User();
user.setUserName("johannas");
user.setUserEmailAddres("joha90#gmail.com");
user.setUserId("2005");
user.setUserFavoritesTvList(list);
return user;
}
public Optional<List<String>> getOptUserFavoriteTvList() {
return OptionalsUtils.toOptional(this.getUserFavoriteTvList());
}
after flatMap, extract the list from the optional if present via orElse and then apply your logic...
OptionalsUtils.toOptional(this.getUser_3())
.flatMap(user-> user.getOptUserFavoriteTvList())
.orElse(Collections.emptyList());
after the call to orElse, you're now working with a List<String> which you can call stream() upon to perform more complex operations or iterate through it using a typical for loop etc...
you could even go with ifPresent if it's more suitable for the task at hand.
OptionalsUtils.toOptional(this.getUser_3())
.flatMap(user-> user.getOptUserFavoriteTvList())
.ifPresent(list -> list.forEach(e -> ....));

LiveData - Observe objects in list

I have a list ofFunctions that is retrieved from a local SQLite database using Room and I want to observe every function in that list. At the moment I'm doing the following:
public List<MutableLiveData<Function>> getLiveFunctions() {
if (liveFunctions == null) {
liveFunctions = new ArrayList<>();
for (Function function : functions) {
//Livedata with a default value on background thread (postValue)
liveFunctions.add(new DefaultLiveData<>(function, true));
}
}
return liveFunctions;
}
After a local fetch from the database, I can request the status of a given function using an RPC to my server. When I receive a response, I can set the new value for that function and I want my UI to observe the changes in that function.
Just some clarifications:
The difference between LiveData<List<Function>>> and List<LiveData<Function>> is that the first will only observe whether an object was added, updated or removed in the list, correct? It's not that LiveData<List<Function>>> also listens to changes on their items?
I'm using a MediatorLiveData to combine my observers to a "FunctionObserver". Is this the correct approach to handle all my function callbacks?
[code]
MediatorLiveData<LiveData<Function>> mediator = new MediatorLiveData<>();
List<MutableLiveData<Function>> functions = //functions
for (LiveData<Function> function : functions) {
mediator.addSource(function, functionObserver);
}
mediator.observe(MainActivity.this, new Observer<LiveData<Function>>() {
#Override
public void onChanged(#Nullable LiveData<Function> functionLiveData) {
Log.i(TAG, "Live data change: ");
}
});
Can my code logic be improved? I know that I can request a LiveData<List<Function>>> from Room but I'm having trouble with my parent class having a #Relation annotation which needs the type to be a List or Set (and not LiveData)

Android: Can't update UI after modifying database

my project involves the following:
1) An EditText view (inputText) in which the user is supposed to type a name
2) A Button that, when pressed, creates a Person object whose name is in inputText and saves that object to the realm database. Then it refreshes the textLog to include the new Person's name.
3) A 'TextView' (textLog) that shows a list of all the names of the Person objects in the realm database.
My problem is that clicking on the button refreshes the text log before it saves the person object to the database. This means the new person's name doesn't show up until I click the button again to create a newer Person object. I want the UI to refresh after the object is saved to the database, so it is always up to date. The following code is from my MainActivity class. Earlier I had done Handler handler = new Handler(Looper.getMainLooper());.
// called when button is clicked
private void submit(View view)
{
final String input = inputText.getText()
.toString()
.trim();
// asynchronous transaction
realm.executeTransactionAsync(realm -> {
realm.copyToRealm(new Person(input));
handler.post(this::refresh);
});
}
private void refresh()
{
textLog.setText(realm.where(Person.class)
.findAll()
.stream()
.map(Person::getName)
.collect(Collectors.joining("\n")));
}
Add Realm.Transaction.OnSuccess() and Realm.Transaction.OnError() callbacks to your async transaction. When those methods are called, you know the transaction is complete and you can refresh your UI.
It looks like you have a race condition. You do realm.executeTransactionAsync and then immediately do handler.post(this::refresh); - there's no guarantee that they'll execute in the order you want them to.

Categories

Resources