Associating tables using Room database in Android Studio - android

How can I associate two tables so that I can create a database that sort of matches the image attached.
I understand that the experiment ID, Date, Name has to be part of the parent table, with the Trial #, Variable, and Result be the child table associated to the parent table. However, I'm not very sure how to implement this in Room Database persistence using Entity and Dao. Can someone show me how?
I want the results to be that if the user selects the experiment in a recyclerview, it will be lead to another activity showing inputs of Variable and Result to add trials at the top while displaying the results of just that experiment below using a Recyclerview.

I believe the following Working Example is one of the ways that you can achieve what you want.
The Code
The Experiment Entity (table) Experiment.java
#Entity
public class Experiment {
#PrimaryKey(autoGenerate = true)
private long experimentId;
private String experimentName;
private String experimentDate;
public Experiment() {
}
#Ignore
public Experiment(String name, String date) {
this.experimentName = name;
this.experimentDate = date;
}
public long getExperimentId() {
return experimentId;
}
public void setExperimentId(long experimentId) {
this.experimentId = experimentId;
}
public String getExperimentName() {
return experimentName;
}
public void setExperimentName(String experimentName) {
this.experimentName = experimentName;
}
public String getExperimentDate() {
return experimentDate;
}
public void setExperimentDate(String experimentDate) {
this.experimentDate = experimentDate;
}
}
Nothing special except perhaps the #Ignore'd constructor (for convenience)
The Trial Entity Trial.java
#Entity
public class Trial {
#PrimaryKey(autoGenerate = true)
private long trialId;
#ForeignKey(entity = Experiment.class, parentColumns = {BaseColumns._ID},childColumns = "parentExperiment", onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE)
private long parentExperiment;
private String trialVariable;
private String trialResult;
public Trial() {
}
#Ignore
public Trial(long parentExperimentId, String variable, String result) {
this.parentExperiment = parentExperimentId;
this.trialVariable = variable;
this.trialResult = result;
}
public long getTrialId() {
return trialId;
}
public void setTrialId(long trialId) {
this.trialId = trialId;
}
public long getParentExperiment() {
return parentExperiment;
}
public void setParentExperiment(long parentExperiment) {
this.parentExperiment = parentExperiment;
}
public String getTrialVariable() {
return trialVariable;
}
public void setTrialVariable(String trialVariable) {
this.trialVariable = trialVariable;
}
public String getTrialResult() {
return trialResult;
}
public void setTrialResult(String trialResult) {
this.trialResult = trialResult;
}
}
Nothing special except perhaps the #Ignore'd constructor (for convenience)
Dao.java (combined for convenience)
public interface Dao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
long[] insertExperiments(Experiment... experiments);
#Insert(onConflict = OnConflictStrategy.IGNORE)
long insertExperiment(Experiment experiment);
#Insert(onConflict = OnConflictStrategy.IGNORE)
long[] insertTrials(Trial... trials);
#Insert(onConflict = OnConflictStrategy.IGNORE)
long insertTrial(Trial trial);
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateExperiments(Experiment... experiments);
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateExperiment(Experiment experiment);
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateTrials(Trial... trials);
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateTrial(Trial trial);
#Delete
int deleteExperiments(Experiment... experiments);
#Delete
int deleteExperiment(Experiment experiment);
#Delete
int deleteTrials(Trial... trials);
#Delete
int deleteTrial(Trial trial);
#Query("SELECT * FROM Experiment")
List<Experiment> getAllexperiments();
#Query("SELECT * FROM Experiment WHERE experimentDate BETWEEN :startdate AND :enddate")
List<Experiment> getExperimentsInDateRange(String startdate, String enddate);
#Query("SELECT * FROM Trial")
List<Trial> getAllTrials();
#Query("SELECT * FROM Experiment JOIN Trial ON parentExperiment = experimentId")
List<TrialWithExperiment> getExperimentsWithTrials();
public class TrialWithExperiment {
private long experimentId;
private String experimentName;
private String experimentDate;
private long trialId;
private String trialVariable;
private String trialResult;
public long getExperimentId() {
return experimentId;
}
public void setExperimentId(long experimentId) {
this.experimentId = experimentId;
}
public String getExperimentName() {
return experimentName;
}
public void setExperimentName(String experimentName) {
this.experimentName = experimentName;
}
public String getExperimentDate() {
return experimentDate;
}
public void setExperimentDate(String experimentDate) {
this.experimentDate = experimentDate;
}
public void setTrialId(long trialId) {
this.trialId = trialId;
}
public long getTrialId() {
return trialId;
}
public String getTrialVariable() {
return trialVariable;
}
public void setTrialVariable(String trialVariable) {
this.trialVariable = trialVariable;
}
public String getTrialResult() {
return trialResult;
}
public void setTrialResult(String trialResult) {
this.trialResult = trialResult;
}
}
}
Note the TrialWithExperiment class, this defines a combination of a Trial and it's owning Experiment.
Note how column names are distinct e.g. no just id but experimentId and trialId differentiates them.
Note the last #Query getExperimentsWithTrials() this will return a list of Trials with their experiments.
The #Database ExperimentDatabase.java
#Database(entities = {Experiment.class, Trial.class}, version = 1,exportSchema = false)
public abstract class ExperimentDatabase extends RoomDatabase {
public abstract Dao getDao();
}
Finally an activity that utilises the above :-
public class MainActivity extends AppCompatActivity {
ExperimentDatabase mDB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDB = Room.databaseBuilder(this,ExperimentDatabase.class,"experimentdb").allowMainThreadQueries().build();
mDB.getDao().insertExperiments(new Experiment[]{
new Experiment("Experiment 1","2019-01-01"),
new Experiment("Experiment 2", "2019-02-01")
});
List<Experiment> experiments = mDB.getDao().getExperimentsInDateRange("2019-01-01","2019-12-31");
for (Experiment e: experiments) {
Log.d("EXPERIMENTS", "Experiment is " + e.getExperimentName() + " on " + e.getExperimentDate());
}
experiments = mDB.getDao().getAllexperiments();
for (Experiment e: experiments) {
Log.d("EXPERIMENTS", "Experiment is " + e.getExperimentName() + " on " + e.getExperimentDate());
}
for (Experiment e: experiments) {
mDB.getDao().insertTrial(
new Trial(
e.getExperimentId(),
"Variable for " + e.getExperimentName(),
"Result for Experiment on " + e.getExperimentDate()
)
);
}
List<Trial> trials = mDB.getDao().getAllTrials();
for (Trial t: trials ) {
Log.d("TRIAL ",
"Trial is for ExperimentID " + String.valueOf(t.getParentExperiment()) +
"\n\tVariable = " + t.getTrialVariable() +
"Result = " + t.getTrialResult()
);
}
List<Dao.TrialWithExperiment> trialsWithExperiments = mDB.getDao().getExperimentsWithTrials();
for (Dao.TrialWithExperiment te:trialsWithExperiments) {
Log.d(
"TRIALWITHEXPERIMENT",
"Experiment Name = " + te.getExperimentName() +
"\n\tExperiment Date = " + te.getExperimentDate() +
"\n\t\tTrial Variable = " + te.getTrialVariable() +
"\n\t\tTrial Result = " + te.getTrialResult()
);
}
}
}
This :-
Adds 2 experiments (every run)
Retrieves Experiments in a date range (this assumes an SQLite recognised date format) and outputs them to the log.
Retrieves all Experiments and outputs them to the log.
Uses the List of experiments to add a Trial to each experiment.
Retrieves all Trials and outputs them to the log.
Retrieves all Trials along with the parent Experiment and outputs them to the log.
The Result
05-28 10:19:42.770 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 1 on 2019-01-01
05-28 10:19:42.770 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 2 on 2019-02-01
05-28 10:19:42.776 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 1 on 2019-01-01
05-28 10:19:42.776 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 2 on 2019-02-01
05-28 10:19:42.786 5750-5750/? D/TRIAL: Trial is for ExperimentID 1
Variable = Variable for Experiment 1Result = Result for Experiment on 2019-01-01
05-28 10:19:42.786 5750-5750/? D/TRIAL: Trial is for ExperimentID 2
Variable = Variable for Experiment 2Result = Result for Experiment on 2019-02-01
05-28 10:19:42.787 5750-5750/? D/TRIALWITHEXPERIMENT: Experiment Name = Experiment 1
Experiment Date = 2019-01-01
Trial Variable = Variable for Experiment 1
Trial Result = Result for Experiment on 2019-01-01
05-28 10:19:42.787 5750-5750/? D/TRIALWITHEXPERIMENT: Experiment Name = Experiment 2
Experiment Date = 2019-02-01
Trial Variable = Variable for Experiment 2
Trial Result = Result for Experiment on 2019-02-01
Alternative/Additional
Another approach could be to utilise #Relation to create an object per Experiment that includes a list of the related/associated Trial(s) in that object.
Expanding upon the above then the following could be added to Dao.java
#Query("SELECT * FROM Experiment")
List<ExperimentWithTrials> getExperimentsAndTrials();
class ExperimentWithTrials {
private long experimentId;
private String experimentName;
private String experimentDate;
#Relation(parentColumn = "experimentId", entityColumn = "parentExperiment")
List<Trial> trials;
public long getExperimentId() {
return experimentId;
}
public void setExperimentId(long experimentId) {
this.experimentId = experimentId;
}
public String getExperimentName() {
return experimentName;
}
public void setExperimentName(String experimentName) {
this.experimentName = experimentName;
}
public String getExperimentDate() {
return experimentDate;
}
public void setExperimentDate(String experimentDate) {
this.experimentDate = experimentDate;
}
public List<Trial> getTrials() {
return trials;
}
public void setTrials(List<Trial> trials) {
this.trials = trials;
}
}
and then the following could be added to MainActivity.java
List<Dao.ExperimentWithTrials> experimentsWithTrials = mDB.getDao().getExperimentsAndTrials();
for (Dao.ExperimentWithTrials et: experimentsWithTrials ) {
Log.d(
"EXPERIMENTANDTRIALS",
"Experiment Name = " + et.getExperimentName() +
"\n\tExperiment Date = " + et.getExperimentDate()
);
for (Trial t: et.getTrials()) {
Log.d(
"TRIALFOREXPERIMENT",
"\t\tVariable = " + t.getTrialVariable() +
"\n\t\tResult = " + t.getTrialResult()
);
}
}
}
Note how the Trials are obtained via the looping through the list of Trials embedded with the ExperimentWithTrials object, as opposed to the list of combined Experiment and Trial data from the previous.
This probably to purer OO way.
However, SQLite wise this appears to be cumbersome/inefficient as it appears to run multiple queries. One to get the Experiments and then another to get the underlying Trials for each experiment.
Resultant additional output
05-28 13:05:35.052 6524-6524/? D/EXPERIMENTANDTRIALS: Experiment Name = Experiment 1
Experiment Date = 2019-01-01
05-28 13:05:35.052 6524-6524/? D/TRIALFOREXPERIMENT: Variable = Variable for Experiment 1
Result = Result for Experiment on 2019-01-01
05-28 13:05:35.052 6524-6524/? D/EXPERIMENTANDTRIALS: Experiment Name = Experiment 2
Experiment Date = 2019-02-01
05-28 13:05:35.052 6524-6524/? D/TRIALFOREXPERIMENT: Variable = Variable for Experiment 2
Result = Result for Experiment on 2019-02-01
Note that for convenience all of the above has been run on the main thread (i.e .allowMainThreadQueries() has been used). If you follow recommendations the all database access would be via another thread, in which case the #Transaction annotation is advised for queries.

Related

Room Data-base how to implement item that needs columns from 2 entities

I've created database with Room with MVVM, and I'm facing a problem I hope you could help me solve this.
I have a database containing 3 entities Player, Group and Standings, where Standings is the relationship between Player and Group.
The things is that I want to present standings, but Standings only contains the IDs of Group and Player, and I want it to also show the name of the player which is in Player, and I'm using LiveData, adapters and ViewModels, so when I return the list of LiveData<List<Standings>> to observe, it doesn't contain the name of the player.
Does someone know how I can pass the name as well?
The only solution that I could think of is to create new class that has a Standing and the name(String) as the instances and then return it to observe.
But it doesn't feel natural so I thought I could find here a better, more elegant solution.
groupStandingsViewModel = ViewModelProviders.of(this, new GroupStandingsViewModelFactory(this.getApplication(), 0)).get(GroupStandingsViewModel.class);
groupStandingsViewModel.getAllStandings().observe(this, new Observer<List<Standings>>() {
#Override
public void onChanged(List<Standings> standings) {
adapter.setStanding(standings);
}
});
I'm expecting to be able to have both the standings and the names as given in the onChanged function of the observe.
The only solution that I could think of is to create new class that
has a Standing and the name(String) as the instances and then return
it to observe.
But it doesn't feel natural so I thought I could find here a better,
more elegant solution.
It may sound unnatural but you will need some new code, so what else? (P.S. that's rhetorical).
I'd suggest a new class is the way to but something like:-
public class PlayerGroupStanding {
#Embedded
Standings standing;
String playerName;
long playerId;
String groupName;
long groupId;
public PlayerGroupStanding() {
}
public Standings getStanding() {
return standing;
}
public void setStanding(Standings standing) {
this.standing = standing;
}
public long getPlayerId() {
return playerId;
}
public void setPlayerId(long playerId) {
this.playerId = playerId;
}
public String getPlayerName() {
return playerName;
}
public void setPlayerName(String playerName) {
this.playerName = playerName;
}
public long getGroupId() {
return groupId;
}
public void setGroupId(long groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupname(String groupname) {
this.groupName = groupname;
}
}
This could be used in conjunction with a Dao Query along the lines of :-
#Query("SELECT * FROM standings JOIN player ON mapToPlayer = playerId JOIN `group` ON mapToGroup = groupId")
List<PlayerGroupStanding> getAllStandingsWithPlayerAndGroupDetails();
Note the above makes many assumptions as to names, although the given names should be self-explantory.
Note that the names of the variables e.g. playerName should match the column name as returned from the query.
Additional
Re comment
what object does the SELECT returns in a query. I understand that if I use a SELECT * then the object will be of the class that is in the FROM. but when I returns columns, what will be the object that I need to mention in the LiveData>? Where can if find information about it? Thank you very much in advance:D
The SELECT actually returns a Cursor, it's the annotation that then writes the code that extracts the columns as per the definition of the method mapping the columns according to the class's member names to return the object, if additional columns exist they are ignored. The FROM clause doesn't determine the resultant object returned the method after the #Query does according to the type returned from that method.
The actual code can be found after building (Ctrl + F9) in the generated code of the project e.g.
So for the example above then the respective method that is generated in the Dao's code (i.e. Dao suffixd with _impl) is :-
#Override
public List<PlayerGroupStandings> getAllPlayerGroupStandings() {
final String _sql = "SELECT * FROM standings JOIN player ON mapToPlayer = playerId JOIN `group` ON mapToGroup = groupId";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
final Cursor _cursor = DBUtil.query(__db, _statement, true, null);
try {
final int _cursorIndexOfMapToPlayer = CursorUtil.getColumnIndexOrThrow(_cursor, "mapToPlayer");
final int _cursorIndexOfMapToGroup = CursorUtil.getColumnIndexOrThrow(_cursor, "mapToGroup");
final LongSparseArray<ArrayList<Player>> _collectionPlayers = new LongSparseArray<ArrayList<Player>>();
final LongSparseArray<ArrayList<Group>> _collectionGroup = new LongSparseArray<ArrayList<Group>>();
while (_cursor.moveToNext()) {
if (!_cursor.isNull(_cursorIndexOfMapToPlayer)) {
final long _tmpKey = _cursor.getLong(_cursorIndexOfMapToPlayer);
ArrayList<Player> _tmpPlayersCollection = _collectionPlayers.get(_tmpKey);
if (_tmpPlayersCollection == null) {
_tmpPlayersCollection = new ArrayList<Player>();
_collectionPlayers.put(_tmpKey, _tmpPlayersCollection);
}
}
if (!_cursor.isNull(_cursorIndexOfMapToGroup)) {
final long _tmpKey_1 = _cursor.getLong(_cursorIndexOfMapToGroup);
ArrayList<Group> _tmpGroupCollection = _collectionGroup.get(_tmpKey_1);
if (_tmpGroupCollection == null) {
_tmpGroupCollection = new ArrayList<Group>();
_collectionGroup.put(_tmpKey_1, _tmpGroupCollection);
}
}
}
_cursor.moveToPosition(-1);
__fetchRelationshipplayerAsarmAndroidroommigrationsPlayer(_collectionPlayers);
__fetchRelationshipgroupAsarmAndroidroommigrationsGroup(_collectionGroup);
final List<PlayerGroupStandings> _result = new ArrayList<PlayerGroupStandings>(_cursor.getCount());
while(_cursor.moveToNext()) {
final PlayerGroupStandings _item;
final Standings _tmpStandings;
if (! (_cursor.isNull(_cursorIndexOfMapToPlayer) && _cursor.isNull(_cursorIndexOfMapToGroup))) {
_tmpStandings = new Standings();
final Long _tmpMapToPlayer;
_tmpMapToPlayer = _cursor.getLong(_cursorIndexOfMapToPlayer);
_tmpStandings.setMapToPlayer(_tmpMapToPlayer);
final Long _tmpMapToGroup;
_tmpMapToGroup = _cursor.getLong(_cursorIndexOfMapToGroup);
_tmpStandings.setMapToGroup(_tmpMapToGroup);
} else {
_tmpStandings = null;
}
ArrayList<Player> _tmpPlayersCollection_1 = null;
if (!_cursor.isNull(_cursorIndexOfMapToPlayer)) {
final long _tmpKey_2 = _cursor.getLong(_cursorIndexOfMapToPlayer);
_tmpPlayersCollection_1 = _collectionPlayers.get(_tmpKey_2);
}
if (_tmpPlayersCollection_1 == null) {
_tmpPlayersCollection_1 = new ArrayList<Player>();
}
ArrayList<Group> _tmpGroupCollection_1 = null;
if (!_cursor.isNull(_cursorIndexOfMapToGroup)) {
final long _tmpKey_3 = _cursor.getLong(_cursorIndexOfMapToGroup);
_tmpGroupCollection_1 = _collectionGroup.get(_tmpKey_3);
}
if (_tmpGroupCollection_1 == null) {
_tmpGroupCollection_1 = new ArrayList<Group>();
}
_item = new PlayerGroupStandings();
_item.standings = _tmpStandings;
_item.players = _tmpPlayersCollection_1;
_item.group = _tmpGroupCollection_1;
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}

How can I invert a boolean value inside an SQL query?

I am making an alarm app and I would to write a query that would toggle some alarm properties, here is my alarm class.
#Entity
data class Alarm(
val label: String?,
var isEnabled: Boolean,
val ringTime: LocalTime,
val occursOn: MutableSet<Day>,
var isVibrationEnabled: Boolean,
#PrimaryKey(autoGenerate = true) var id: Int? = null
)
Here is my query that should invert vibration value
#Query("UPDATE Alarm SET isVibrationEnabled = NOT isVibrationEnabled WHERE id = :alarmId")
internal abstract fun toggleVibration(alarmId: Int): Completable
The app compiles fine, so it seems that the query is valid, however after I execute it, the value is not inverted and it stays the same
I believe that your issue is not the toggling, that works.
Rather that the issue is that the WHERE clause is not selecting a row (or the expected row) and thus that the value passed as the parameter when calling the toggleVibration function is not the id of an Alarm or the expected Alarm.
Example
Consider the following example (in Java rather than Kotlin for convenience) :-
The AlarmEntity Alarm.java
public class Alarm {
#PrimaryKey(autoGenerate = true)
private long id;
private String label;
private boolean isEnabled;
private boolean isVibrationEnabled;
public Alarm() {
}
public Alarm(String label, boolean isEnabled, boolean isVibrationEnabled) {
this.label = label;
this.isEnabled = isEnabled;
this.isVibrationEnabled = isVibrationEnabled;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public boolean isEnabled() {
return isEnabled;
}
public void setEnabled(boolean enabled) {
isEnabled = enabled;
}
public boolean isVibrationEnabled() {
return isVibrationEnabled;
}
public void setVibrationEnabled(boolean vibrationEnabled) {
isVibrationEnabled = vibrationEnabled;
}
}
reduced set of columns for conveneience
AlarmDao.java
#Dao
public interface AlarmDao {
#Insert
long[] insertAlarm(Alarm... alarms);
#Insert
long insertAlarm(Alarm alarm);
#Query("SELECT * FROM Alarm")
List<Alarm> getAllAlarms();
#Query("UPDATE Alarm SET isVibrationEnabled = NOT isVibrationEnabled WHERE id = :id")
int toggleVibration(long id);
}
Code from an Activity to test :-
alarmDao = mDB.getAlarmDao();
// Add a copule of alarms
alarmDao.insertAlarm(
new Alarm("Alarm001",true,true),
new Alarm("Alarm002",false,false)
);
// Get the Alarms and output them to the log
List<Alarm> myalarms = alarmDao.getAllAlarms();
for (Alarm a: myalarms) {
Log.d("ALARMBEFORE","Label = " + a.getLabel() + " Enabaled = " + String.valueOf(a.isEnabled()) + " Vibration = " + a.isVibrationEnabled());
}
//<<<<<<<<<< Toggle the first alarm >>>>>>>>>>
alarmDao.toggleVibration(myalarms.get(0).getId());
// Again get all the alarams and output them to the log
myalarms = alarmDao.getAllAlarms();
for (Alarm a: myalarms) {
Log.d("ALARMAFTER","Label = " + a.getLabel() + " Enabaled = " + String.valueOf(a.isEnabled()) + " Vibration = " + a.isVibrationEnabled());
}
Result
05-30 10:34:33.853 D/ALARMBEFORE: Label = Alarm001 Enabaled = true Vibration = true
05-30 10:34:33.853 D/ALARMBEFORE: Label = Alarm002 Enabaled = false Vibration = false
05-30 10:34:33.856 D/ALARMAFTER: Label = Alarm001 Enabaled = true Vibration = false
05-30 10:34:33.856 D/ALARMAFTER: Label = Alarm002 Enabaled = false Vibration = false
i.e. for Alarm001 the isVibrationEnabaled value has been toggled from true to false

How to store string integer pair other than hash map?

This is my recycler view in which I am storing my expense related information . I want to sum the amount paid of each corresponding user . For example , sadaf : Rs (50+10+5)=65 , Gulatiji: (50)Rs =50 , Amen :(50+5)= Rs 55 .
I thought of using hash map but the problem is that the user names in my recycler view are not unique . So , how should I store the sum of each corresponding user .
I have also tried this but this is giving me the wrong answer .
I am able to successfully sum the total amount column but for the amount paid , it is giving me the wrong answer. For the time being , I was just trying to calculate the total amount of a single user by using an integer variable my_total .I am getting the answer for sadaf as 110 but it should be 65.
calcuationAdap = new CalcuationAdap(Calculation.this,data);
for (int i = 0 ;i<data.size();i++) {
System.out.println(data.get(i).getItem());
main_total = main_total + Integer.parseInt(data.get(i).getTot_amt());
}
for(int i=0;i<data.size();i++)
{
for( int j=i+1;j<data.size()-1;j++)
{
if(data.get(i).getUser_name().equals(data.get(j).getUser_name()))
{
System.out.println(data.get(i).getUser_name());
my_total = my_total+Integer.parseInt(data.get(i).getMy_amt());
}
}
}
Toast.makeText(getApplicationContext(), String.valueOf(main_total ), Toast.LENGTH_SHORT).show();
Toast.makeText(getApplicationContext(), String.valueOf(my_total), Toast.LENGTH_SHORT).show();
If you have user id then use user id. other wise use below code to get each total amount
calcuationAdap = new CalcuationAdap(Calculation.this,data);
HashMap<String,Integer> result =new HashMap<>();
for (int i = 0 ;i<data.size();i++) {
System.out.println(data.get(i).getItem());
main_total = main_total + Integer.parseInt(data.get(i).getTot_amt());
}
for(int i=0;i<data.size();i++) {
if (result.containsKey(data.get(i).getUser_name())){
Integer addTotal=result.get(data.get(i).getUser_name());
addTotal= addTotal + Integer.parseInt(data.get(i).getMy_amt());
result.put(data.get(i).getUser_name(),addTotal);
}else {
result.put(data.get(i).getUser_name(),Integer.parseInt(data.get(i).getMy_amt()));
}
}
Toast.makeText(getApplicationContext(), String.valueOf(main_total ), Toast.LENGTH_SHORT).show();
for (Map.Entry<String, Integer> entry:result.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
Toast.makeText(getApplicationContext(), entry.getKey() + "/" + entry.getValue(), Toast.LENGTH_SHORT).show();
}
create a model class like
public class MyDataClass {
String userName;
String date;
String item;
int totoalAmount;
int amountPaid;
public int getAmountPaid() {
return amountPaid;
}
public int getTotoalAmount() {
return totoalAmount;
}
public String getDate() {
return date;
}
public String getItem() {
return item;
}
public String getUserName() {
return userName;
}
public void setAmountPaid(int amountPaid) {
this.amountPaid = amountPaid;
}
public void setDate(String date) {
this.date = date;
}
public void setItem(String item) {
this.item = item;
}
public void setTotoalAmount(int totoalAmount) {
this.totoalAmount = totoalAmount;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
and whenever you need to store data as a list of such type create array list of this type like
ArrayList list =new ArrayList<>()
and create object of this class to store data with getter methods like
MyDataClass data =new MyDataClass()
data.setUserName("userName")
....
list.add(data);
and whenever you need to get data use getter method to get data like
list.get(index).getUserName();

Android/Java, Cloud Firestore .toObject method not working

I develop an Android app which interacts with Google Firebase Cloud Firestore. To get data from Firestore, I use addOnCompleteListener with DocumentSnapshot.toObject() method. However, method toObject() seems not to work properly because it doesn't transmit data from snapshot to object.
Goal.class
#Entity
public class Goal {
// private variables
#PrimaryKey (autoGenerate = true)
private int id;
private String remoteId;
private int goalPos;
private String goalName;
private int goalCategory;
private String goalDescription;
private int goalColor;
private int goalFrequency;
private long goalFrequencyCode;
private boolean goalRewardType;
private double goalReward;
private int activated;
private boolean isSynced;
// constructor
public Goal() {
remoteId = "";
goalPos = 0;
goalName = "";
goalCategory = 15;
goalDescription = "";
goalColor = R.color.cat_Black;
goalFrequency = 0; // 0=Daily, 1=Weekly, 2=Monthly
goalFrequencyCode = 1111111111; // 1111111.111 - Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday; first of month, middle of month, last of month
goalRewardType = false; // false = standard, true = individual
activated = 1; // 0=No, 1=Yes
isSynced = false;
}
// getter and setter
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRemoteId() {
return remoteId;
}
public void setRemoteId(String remoteId) {
this.remoteId = remoteId;
}
public int getGoalPos() {
return goalPos;
}
public void setGoalPos(int goalPos) {
this.goalPos = goalPos;
}
public String getGoalName() {
return goalName;
}
public void setGoalName(String goalName) {
this.goalName = goalName;
}
public int getGoalCategory() {
return goalCategory;
}
public void setGoalCategory(int goalCategory) {
this.goalCategory = goalCategory;
}
public String getGoalDescription() {
return goalDescription;
}
public void setGoalDescription(String goalDescription) {
this.goalDescription = goalDescription;
}
public int getGoalColor() {
return goalColor;
}
public void setGoalColor(int goalColor) {
this.goalColor = goalColor;
}
public int getGoalFrequency() {
return goalFrequency;
}
public void setGoalFrequency(int goalFrequency) {
this.goalFrequency = goalFrequency;
}
public long getGoalFrequencyCode() {
return goalFrequencyCode;
}
public void setGoalFrequencyCode(long goalFrequencyCode) {
this.goalFrequencyCode = goalFrequencyCode;
}
public LinkedList<Integer> getGoalFrequencyCodeAsList() {
LinkedList<Integer> stack = new LinkedList<>();
long number = goalFrequencyCode;
while (number > 0) {
long longTempMod = number % 10;
int intTempMod = (int) longTempMod;
stack.push(intTempMod);
number = number / 10;
}
return stack;
}
public void setGoalFrequencyCodeFromList(LinkedList<Integer> stack) {
double number = 0;
for (int j = 0; j < stack.size(); j++) {
Log.d(String.valueOf(j), String.valueOf(stack.get(j)));
}
if (stack.size() <= 1) {
goalFrequencyCode = 1111111111;
} else {
for (int i = 0; i < stack.size(); i++) {
Log.d(String.valueOf(stack.get(i)), String.valueOf(number));
number = number + (stack.get(i) * Math.pow(10, (stack.size() - 1 - i)));
}
Log.d("Sent from Goal - number", String.valueOf(number));
goalFrequencyCode = (long) number;
Log.d("Sent from Goal - long", String.valueOf(goalFrequencyCode));
}
}
public boolean getGoalRewardType() {
return goalRewardType;
}
public void setGoalRewardType(boolean goalRewardType) {
this.goalRewardType = goalRewardType;
}
public double getGoalReward() {
return goalReward;
}
public void setGoalReward(double goalReward) {
this.goalReward = goalReward;
}
public int getActivated() {
return activated;
}
public void setActivated(int activated) {
this.activated = activated;
}
public boolean getIsSynced() {
return isSynced;
}
public void setIsSynced(boolean isSynced) {
this.isSynced = isSynced;
}
#Override
public String toString() {
return "Goal{" +
"id=" + id +
", remoteId='" + remoteId + '\'' +
", goalPos=" + goalPos +
", goalName='" + goalName + '\'' +
", goalCategory=" + goalCategory +
", goalDescription='" + goalDescription + '\'' +
", goalColor=" + goalColor +
", goalFrequency=" + goalFrequency +
", goalFrequencyCode=" + goalFrequencyCode +
", goalRewardType=" + goalRewardType +
", goalReward=" + goalReward +
", activated=" + activated +
", isSynced=" + isSynced +
'}';
}
}
FirebaseService.class
public LiveData<ApiResponse<List<Goal>>> getGoals() {
final MutableLiveData<ApiResponse<List<Goal>>> list = new MutableLiveData<>();
if (mAuth.getCurrentUser() != null) {
userId = mAuth.getCurrentUser().getUid();
colRefGoals = firestore.collection(userId).document("data").collection("goals");
colRefGoals
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<Goal> goalsList = new ArrayList<Goal>();
for (DocumentSnapshot documentSnapshot : task.getResult()) {
Log.d("firebaseService", "DocumentSnapshop.getData: " + documentSnapshot.getData());
Goal goal = documentSnapshot.toObject(Goal.class);
Log.d("firebaseService", "DocumentSnapshot.toObject(Goal.class): " + goal.toString());
goalsList.add(goal);
}
ApiResponse<List<Goal>> apiResponse = new ApiResponse<List<Goal>>(goalsList);
list.setValue(apiResponse);
}
}
});
} else {
Throwable error = new Throwable("No user logged in");
ApiResponse<List<Goal>> apiResponse = new ApiResponse<List<Goal>>(error);
list.setValue(apiResponse);
return list;
}
return list;
}
Following my debug log comparison between .getData and .toObject:
12-05 19:53:42.663 11470-11470/com.benjaminsommer.dailygoals D/firebaseService: DocumentSnapshop.getData: {goals={goalRewardType=true, remoteId=10L44s0EcvTTzGajzhidgoals, id=2, goalFrequencyCode=1001111111, activated=1, goalColor=-4365347, goalCategory=8, goalDescription=Description Test, goalReward=1, goalPos=1, goalFrequency=2, goalName=Test}}
12-05 19:53:42.663 11470-11470/com.benjaminsommer.dailygoals D/firebaseService: DocumentSnapshot.toObject(Goal.class): Goal{id=0, remoteId='', goalPos=0, goalName='', goalCategory=15, goalDescription='', goalColor=2131558437, goalFrequency=0, goalFrequencyCode=1111111111, goalRewardType=false, goalReward=0.0, activated=1, isSynced=false}
.toObject doesn't transform the datasnapshot to my class. I already checked the documentation:
Important: Each custom class must have a public constructor that takes
no arguments. In addition, the class must include a public getter for
each property.
I have a constructor with no args and public getters for each property. I tried it with an empty constructor, but not working either. Is anything wrong with my getters? I use it in combination with Room, maybe there can be an issue?
Really appreciate your help and support.
Thanks, Ben

Using simple XML in android, find a way to capture order of elements?

I've been looking for a way to capture the order of element's listed within a tag, but haven't been very successful..
EDIT NEW:
<android>
<leags>
<leag name = "someName">
<headlines>
<pageType>headlines</pageType>
<story>someStoryURL</story>
<fullStory>someFullStoryURL</fullStory>
</headlines>
<scores></scores>
<statistics></statistics>
</leag>
<leags>
</android>
-Want to capture the order of elements in leag as 1)headlines 2)scores 3)statistics. If the xml changes and scores is listed before headlines it would be 1)scores 2)headlines 3)statistics.
I parse only android - Like this:
#Root(name = "android", strict = false)
public class android
{
#ElementList(name = "leags", required = false)
private List<Leag> leags;
public Leag getLeagByName(String name)
{ // go through list of leags and return leag with matching name}
}
So in my "leag" object I'd want to capture the order of elements - Is there a way to do that?
I'm assuming you'd need to set new AnnotionStrategy() like this:
tAndroid android = null;
Serializer serial = new Persister(new AnnotationStrategy());
android = serial.read(android .class, source);
League x= android.getLeagueByName("oeoe");
for(String s: x.getOrder())
{
Log.i("Order", s);
}
BEFORE EDIT:
Supposing the xml above is what's being pased by the following code:
#Element(name="headlines")
public class Headlines
{
#Element(name="pageType", required = false)
private String pageType;
#Element(name="story", required = false)
private String storiesURL;
#Element(name="fullStory", required = false)
private String fullStoryURL;
public String getStoriesURL()
{
return storiesURL;
}
public String getFullStoryURL()
{
return fullStoryURL;
}
#Override
public String toString()
{
return "PageType: " + this.pageType +
"\nStoriesURL: " + this.storiesURL +
"\nFullStoryURL: " + this.fullStoryURL;
}
}
Is there a way to somehow return the order in which the elements get parsed?
Like a method that will return a string of some sort with the correct order like:
pageType
story
fullStory
You can use a Converter to get the order. But you can't return the order from it (or better: you can, but better don't do it).
It's relatively easy to get the order - the trick is getting it out from the Converter. On way is to add a list to your class and store it there.
Implementation:
#Root(name = "headlines")
#Convert(value = Headlines.HeadlinesConverter.class)
public class Headlines
{
#Element(name="pageType", required = false)
private String pageType;
#Element(name="story", required = false)
private String storiesURL;
#Element(name="fullStory", required = false)
private String fullStoryURL;
private List<String> order; // Here we save the order of the elements
public String getPageType()
{
return pageType;
}
public String getStoriesURL()
{
return storiesURL;
}
public String getFullStoryURL()
{
return fullStoryURL;
}
public List<String> getOrder()
{
return order;
}
#Override
public String toString()
{
return "Headlines{" + "pageType=" + pageType
+ ", storiesURL=" + storiesURL
+ ", fullStoryURL=" + fullStoryURL + '}';
}
// You can implement the converter as an extra class too
static class HeadlinesConverter implements Converter<Headlines>
{
#Override
public Headlines read(InputNode node) throws Exception
{
Headlines h = new Headlines();
h.order = new ArrayList<>(3);
InputNode next = node.getNext();
while( next != null )
{
final String value = next.getValue();
/*
* You can use reflection (= slower) instead the switch, or
* remove the switch:
*
* h.order.add(next.getName());
*
* and do this after the while loop:
*
* h.pageType = node.getNext("pageType");
* ...
*/
switch(next.getName())
{
case "pageType":
h.pageType = value;
break;
case "story":
h.storiesURL = value;
break;
case "fullStory":
h.fullStoryURL = value;
break;
default:
/* Maybe some error-handling here?! */
break;
}
h.order.add(next.getName()); // add 'value' if you want the order of the values
next = node.getNext();
}
return h;
}
#Override
public void write(OutputNode node, Headlines value) throws Exception
{
throw new UnsupportedOperationException("Not supported yet.");
}
}
}
Note: I didn't use setter here - but it's better you do so.
Example code:
final File f = new File("test.xml");
Serializer ser = new Persister(new AnnotationStrategy()); /* ! */
Headlines h = ser.read(Headlines.class, f);
int i = 1;
for( String s : h.getOrder() )
{
System.out.println((i++) + ". " + s);
}
and finally the output:
1. pageType
2. story
3. fullStory
You need to use #Order annotation. Here can see an example http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php#xpath

Categories

Resources