From 9452f1910d4ea6d2434a16e469074cadfc6f35b8 Mon Sep 17 00:00:00 2001 From: Rakantor Date: Sun, 19 Jun 2022 14:03:39 +0200 Subject: [PATCH] Refactor --- app/src/main/AndroidManifest.xml | 3 +- .../example/iubhgamerapp/ui/ChatFragment.java | 2 +- .../example/iubhgamerapp/ui/HomeFragment.java | 188 +++---- ...{RateFragment.java => RatingFragment.java} | 477 +++++++++--------- app/src/main/res/layout/fragment_home.xml | 4 +- ...{fragment_rate.xml => fragment_rating.xml} | 266 +++++----- .../main/res/navigation/mobile_navigation.xml | 4 +- 7 files changed, 479 insertions(+), 465 deletions(-) rename app/src/main/java/com/example/iubhgamerapp/ui/{RateFragment.java => RatingFragment.java} (95%) mode change 100755 => 100644 rename app/src/main/res/layout/{fragment_rate.xml => fragment_rating.xml} (97%) mode change 100755 => 100644 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8a98a90..663a295 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - + diff --git a/app/src/main/java/com/example/iubhgamerapp/ui/ChatFragment.java b/app/src/main/java/com/example/iubhgamerapp/ui/ChatFragment.java index 8bd4480..c83baae 100755 --- a/app/src/main/java/com/example/iubhgamerapp/ui/ChatFragment.java +++ b/app/src/main/java/com/example/iubhgamerapp/ui/ChatFragment.java @@ -142,7 +142,7 @@ public class ChatFragment extends Fragment { // Convert epoch timestamp to formatted date string Date date = new Date(timestamp * 1000L); - this.date = new SimpleDateFormat("dd.MM.YYYY HH:mm", Locale.getDefault()).format(date); + this.date = new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.getDefault()).format(date); // Check if the sender's user id still exist in the database. // If so, extract their nickname diff --git a/app/src/main/java/com/example/iubhgamerapp/ui/HomeFragment.java b/app/src/main/java/com/example/iubhgamerapp/ui/HomeFragment.java index ab75518..70ff578 100755 --- a/app/src/main/java/com/example/iubhgamerapp/ui/HomeFragment.java +++ b/app/src/main/java/com/example/iubhgamerapp/ui/HomeFragment.java @@ -35,7 +35,6 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; public class HomeFragment extends Fragment { private View root; @@ -44,7 +43,6 @@ public class HomeFragment extends Fragment { private DataSnapshot dsUsers, dsGames; private ProgressBar progressBar; private TextView welcome, nextDate, nextHost; - private Button btnSignOut, btnVote, btnSuggest; private Spinner spinnerGames; private Map games = new HashMap<>(); private String userNickname, nextDateID; @@ -55,11 +53,11 @@ public class HomeFragment extends Fragment { welcome = root.findViewById(R.id.text_home); nextDate = root.findViewById(R.id.text_home2); nextHost = root.findViewById(R.id.text_home5); - btnSignOut = root.findViewById(R.id.logout); - btnVote = root.findViewById(R.id.vote); - btnSuggest = root.findViewById(R.id.suggest); spinnerGames = root.findViewById(R.id.spinner_games); progressBar = root.findViewById(R.id.progressBar); + Button btnSignOut = root.findViewById(R.id.logout); + Button btnVote = root.findViewById(R.id.vote); + Button btnSuggest = root.findViewById(R.id.suggest); // Set button listeners btnSignOut.setOnClickListener(v -> signOutUser()); @@ -76,28 +74,41 @@ public class HomeFragment extends Fragment { refGames = FirebaseDatabase.getInstance().getReference("spiele"); refUsers = FirebaseDatabase.getInstance().getReference("spieler"); - // Get list of registered users from database - refUsers.addValueEventListener(new ValueEventListener() { + // Get a list of all players from the database and store as DataSnapshot, + // then check whether a new event needs to be added to the database. + refUsers.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { dsUsers = dataSnapshot; - setValues(); + updateUI(); + checkUpcomingEvent(); } - @Override public void onCancelled(@NonNull DatabaseError databaseError) { Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); } }); - // Get list of available games from database + // Update the UI whenever the list of players changes in the database + refUsers.addValueEventListener(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot dataSnapshot) { + dsUsers = dataSnapshot; + updateUI(); + } + @Override + public void onCancelled(@NonNull DatabaseError databaseError) { + Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); + } + }); + + // Update the UI whenever the list of games changes in the database refGames.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { dsGames = dataSnapshot; updateGamesList(); } - @Override public void onCancelled(@NonNull DatabaseError databaseError) { Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); @@ -116,15 +127,90 @@ public class HomeFragment extends Fragment { if(isActive) { progressBar.setVisibility(View.VISIBLE); // Disable user interaction while progress bar is visible - Objects.requireNonNull(getActivity()).getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, + requireActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); } else if(progressBar.getVisibility() == View.VISIBLE) { progressBar.setVisibility(View.GONE); // Re-enable user interaction when progress bar is gone - Objects.requireNonNull(getActivity()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + requireActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); } } + /** + * + * Then get the most recent event next and check if . + * If so, a new event is added. + */ + private void checkUpcomingEvent() { + refEventDates.orderByKey().limitToLast(1).addValueEventListener(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot dataSnapshot) { + for(DataSnapshot ds : dataSnapshot.getChildren()) { + nextDateID = ds.getKey(); + long epoch = Long.parseLong(nextDateID); + + if(epoch < (System.currentTimeMillis() / 1000L)) { + addUpcomingEvent(epoch); + } + + Date date = new Date(epoch * 1000L); + String s = new SimpleDateFormat("dd. MMMM yyyy", Locale.getDefault()).format(date); + nextDate.setText(s); + + String sNextHostUID = (String) ds.child("gastgeber").getValue(); + String sNextHost = (String)dsUsers.child(sNextHostUID).child("nickname").getValue(); + nextHost.setText(sNextHost); + + if(ds.hasChild("abstimmung_spiele")) { + games = new HashMap<>(); + for(DataSnapshot dataSnapshot1 : ds.child("abstimmung_spiele").getChildren()) { + long curInt = (long)dataSnapshot1.getValue(); + + if(games.containsKey(curInt)) games.put(curInt, games.get(curInt)+1); + else games.put(curInt, 1); + } + } + } + setProgressBar(false); + } + @Override + public void onCancelled(@NonNull DatabaseError databaseError) { + Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); + } + }); + } + + /** + * Determines the date and host of the next event and writes the details to the database. + * If this was a real life app and not just an example project, it would be advisable to handle + * event setup automatically with scheduled functions running on the server (a possible + * solution could be Google Cloud Functions for Firebase). + * @param prevEventTimestamp Unix epoch time of the previous event + */ + private void addUpcomingEvent(long prevEventTimestamp) { + // Programmatically determine the host of the upcoming event by iterating through the list + // of registered users and comparing the epoch timestamps of their most recently hosted event. + // The lowest timestamp will determine the new host. + String nextHostUID = null; + long ll = 0; + for(DataSnapshot dataSnapshot : dsUsers.getChildren()) { + long ts = (long)dataSnapshot.child("zuletzt_gehostet").getValue(); + if(ll == 0 || ts < ll) { + ll = ts; + nextHostUID = dataSnapshot.getKey(); + } + } + + // Calculate epoch timestamp of upcoming event + // It will take place exactly 1 week after the last event. + // 7 days * 24 hours * 60 minutes * 60 seconds = 604800 + long nextEventTimestamp = prevEventTimestamp + 604800; + + // Write to database + refEventDates.child(String.valueOf(nextEventTimestamp)).child("gastgeber").setValue(nextHostUID); + refUsers.child(nextHostUID).child("zuletzt_gehostet").setValue(nextEventTimestamp); + } + /** * Displays a popup dialog with an text input field. The user is supposed to type in the title * of a game that he would like to add to the database. @@ -167,90 +253,18 @@ public class HomeFragment extends Fragment { spinnerGames.setAdapter(adapter); } - private void setValues() { - // Get details of upcoming event from database - refEventDates.orderByKey().limitToLast(1).addValueEventListener(new ValueEventListener() { - @Override - public void onDataChange(@NonNull DataSnapshot dataSnapshot) { - for(DataSnapshot ds : dataSnapshot.getChildren()) { - nextDateID = ds.getKey(); - long epoch = Long.parseLong(nextDateID); - - if(epoch < (System.currentTimeMillis() / 1000L)) { - addUpcomingEvent(epoch); - return; - } - - Date date = new Date(epoch * 1000L); - String s = new SimpleDateFormat("dd. MMMM YYYY", Locale.getDefault()).format(date); - nextDate.setText(s); - - String sNextHostUID = (String) ds.child("gastgeber").getValue(); - String sNextHost = (String)dsUsers.child(sNextHostUID).child("nickname").getValue(); - nextHost.setText(sNextHost + "'s place"); - - if(ds.hasChild("abstimmung_spiele")) { - games = new HashMap<>(); - for(DataSnapshot dataSnapshot1 : ds.child("abstimmung_spiele").getChildren()) { - long curInt = (long)dataSnapshot1.getValue(); - - if(games.containsKey(curInt)) games.put(curInt, games.get(curInt)+1); - else games.put(curInt, 1); - } - } - } - updateGamesList(); - setProgressBar(false); - } - - @Override - public void onCancelled(@NonNull DatabaseError databaseError) { - Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); - } - }); - + private void updateUI() { userNickname = (String)dsUsers.child(mUser.getUid()).child("nickname").getValue(); - String sWelcome = userNickname + getString(R.string.welcome_back); welcome.setText(sWelcome); } - /** - * Determines the date and host of the next event and writes the details to the database. - * If this was a real life app and not just an example project, it would be advisable to handle - * event setup automatically with scheduled functions running on the server (a possible - * solution could be Google Cloud Functions for Firebase). - * @param prevEventTimestamp Unix epoch time of the previous event - */ - private void addUpcomingEvent(long prevEventTimestamp) { - // Programmatically determine the host of the upcoming event by iterating through the list - // of registered users and comparing the epoch timestamps of their most recently hosted event. - // The lowest timestamp will determine the new host. - String nextHostUID = null; - long ll = 0; - for(DataSnapshot dataSnapshot : dsUsers.getChildren()) { - long ts = (long)dataSnapshot.child("zuletzt_gehostet").getValue(); - if(ll == 0 || ts < ll) { - ll = ts; - nextHostUID = dataSnapshot.getKey(); - } - } - - // Calculate epoch timestamp of upcoming event - // It will take place exactly 1 week after the last event. - // 7 days * 24 hours * 60 minutes * 60 seconds = 604800 - long nextEventTimestamp = prevEventTimestamp + 604800; - - // Write to database - refEventDates.child(String.valueOf(nextEventTimestamp)).child("gastgeber").setValue(nextHostUID); - } - /** * Signs out the current Firebase user and switches to the login interface. */ private void signOutUser() { FirebaseAuth.getInstance().signOut(); startActivity(new Intent(getActivity(), LoginActivity.class)); - Objects.requireNonNull(getActivity()).finish(); + requireActivity().finish(); } } \ No newline at end of file diff --git a/app/src/main/java/com/example/iubhgamerapp/ui/RateFragment.java b/app/src/main/java/com/example/iubhgamerapp/ui/RatingFragment.java old mode 100755 new mode 100644 similarity index 95% rename from app/src/main/java/com/example/iubhgamerapp/ui/RateFragment.java rename to app/src/main/java/com/example/iubhgamerapp/ui/RatingFragment.java index 5e36ab6..6333d85 --- a/app/src/main/java/com/example/iubhgamerapp/ui/RateFragment.java +++ b/app/src/main/java/com/example/iubhgamerapp/ui/RatingFragment.java @@ -1,240 +1,239 @@ -package com.example.iubhgamerapp.ui; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.RatingBar; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - -import com.example.iubhgamerapp.R; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.auth.FirebaseUser; -import com.google.firebase.database.DataSnapshot; -import com.google.firebase.database.DatabaseError; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.ValueEventListener; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -public class RateFragment extends Fragment { - private EventDate selectedEvent; - private FirebaseUser mUser; - private DatabaseReference refUsers, refEvents; - private View root; - private List eventDates; - private Map users; - private Spinner spinnerPastEvents; - private RatingBar rbOverall, rbFood, rbHost; - private TextView tvHost; - private TextView tvOverallRatings, tvFoodRatings, tvHostRatings; - private Button btnRate; - - public View onCreateView(@NonNull LayoutInflater inflater, - ViewGroup container, Bundle savedInstanceState) { - root = inflater.inflate(R.layout.fragment_rate, container, false); - spinnerPastEvents = root.findViewById(R.id.spinner_rating); - rbOverall = root.findViewById(R.id.ratingBar_overall); - rbFood = root.findViewById(R.id.ratingBar_food); - rbHost = root.findViewById(R.id.ratingBar_host); - tvHost = root.findViewById(R.id.textView_rateHost); - tvOverallRatings = root.findViewById(R.id.textView_rate1); - tvFoodRatings = root.findViewById(R.id.textView_rate2); - tvHostRatings = root.findViewById(R.id.textView_rate3); - btnRate = root.findViewById(R.id.rate); - - // Set rate button listener - btnRate.setOnClickListener(v -> ratePastEvent()); - - // Set spinner listener - spinnerPastEvents.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - selectedEvent = eventDates.get(position); - - rbOverall.setRating(selectedEvent.ownOverallRating); - rbFood.setRating(selectedEvent.ownFoodRating); - rbHost.setRating(selectedEvent.ownHostRating); - - tvHost.setText("Host (" + selectedEvent.hostName + ")"); - - String sNoRatings = getString(R.string.zero_ratings); - - String str1 = getString(R.string.other_ratings, selectedEvent.avgOverallRating, selectedEvent.overallRatingsCount); - tvOverallRatings.setText(selectedEvent.overallRatingsCount > 0 ? str1 : sNoRatings); - - String str2 = getString(R.string.other_ratings, selectedEvent.avgFoodRating, selectedEvent.foodRatingsCount); - tvFoodRatings.setText(selectedEvent.foodRatingsCount > 0 ? str2 : sNoRatings); - - String str3 = getString(R.string.other_ratings, selectedEvent.avgHostRating, selectedEvent.hostRatingsCount); - tvHostRatings.setText(selectedEvent.hostRatingsCount > 0 ? str3 : sNoRatings); - } - - @Override - public void onNothingSelected(AdapterView parent) { - // Nothing to do here - } - }); - - // Connect to Firebase realtime database - mUser = FirebaseAuth.getInstance().getCurrentUser(); - refUsers = FirebaseDatabase.getInstance().getReference().child("spieler"); - refEvents = FirebaseDatabase.getInstance().getReference().child("termine"); - - // Get list of registered users from Firebase database - refUsers.addValueEventListener(new ValueEventListener() { - @Override - public void onDataChange(@NonNull DataSnapshot dataSnapshot) { - // Save users in HashMap object (key = UID; value = nickname) - users = new HashMap<>(); - for(DataSnapshot ds : dataSnapshot.getChildren()) { - users.put(ds.getKey(), (String)ds.child("nickname").getValue()); - } - - getPastEvents(); - } - - @Override - public void onCancelled(@NonNull DatabaseError databaseError) { - Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); - } - }); - - return root; - } - - /** - * Writes the user's rating of the selected event to database. - */ - private void ratePastEvent() { - long ownOverallRating = (long)rbOverall.getRating(); - long ownFoodRating = (long)rbFood.getRating(); - long ownHostRating = (long)rbHost.getRating(); - - // Write ratings to database IF the user rated every required item - if(ownOverallRating > 0 && ownFoodRating > 0 && ownHostRating > 0) { - DatabaseReference refRatings = refEvents.child(String.valueOf(selectedEvent.epochTimestamp)).child("bewertungen"); - refRatings.child("allgemein").child(mUser.getUid()).setValue(ownOverallRating); - refRatings.child("essen").child(mUser.getUid()).setValue(ownFoodRating); - refRatings.child("gastgeber").child(mUser.getUid()).setValue(ownHostRating); - } - // Otherwise, display error message - else { - Toast.makeText(getContext(), R.string.rate_error, Toast.LENGTH_SHORT).show(); - } - } - - /** - * Reads the most recent events from the database. The user can rate the overall experience, - * food & drinks as well as the event host. - * Only the last 3 events can be rated. - */ - private void getPastEvents() { - refEvents.orderByKey().limitToLast(4).addValueEventListener(new ValueEventListener() { - @Override - public void onDataChange(@NonNull DataSnapshot dataSnapshot) { - // Iterate through events to extract metadata - eventDates = new ArrayList<>(); - for(DataSnapshot ds : dataSnapshot.getChildren()) { - // Check if the event date is actually in the future. - // Skip it, as only past events are unlocked for rating. - long epochTimestamp = Long.parseLong(ds.getKey()); - if(epochTimestamp > (System.currentTimeMillis() / 1000L)) break; - - // Extract event host user id - String hostUID = (String)ds.child("gastgeber").getValue(); - - // Extract individual rating data - Map overallRatings = new HashMap<>(); - Map foodRatings = new HashMap<>(); - Map hostRatings = new HashMap<>(); - for(DataSnapshot dsOverallRatings : ds.child("bewertungen").child("allgemein").getChildren()) { - overallRatings.put(dsOverallRatings.getKey(), (long)dsOverallRatings.getValue()); - } - for(DataSnapshot dsFoodRatings : ds.child("bewertungen").child("essen").getChildren()) { - foodRatings.put(dsFoodRatings.getKey(), (long)dsFoodRatings.getValue()); - } - for(DataSnapshot dsHostRatings : ds.child("bewertungen").child("gastgeber").getChildren()) { - hostRatings.put(dsHostRatings.getKey(), (long)dsHostRatings.getValue()); - } - - // Add event to ArrayList to make data available to other methods - eventDates.add(new EventDate(epochTimestamp, hostUID, overallRatings, foodRatings, hostRatings)); - } - - // Update spinner; set selection to most recent event - List spinnerEntries = new ArrayList<>(); - for(int i = 0; i < eventDates.size(); i++) spinnerEntries.add(eventDates.get(i).getFormattedDate()); - ArrayAdapter adapter = new ArrayAdapter<>(root.getContext(), - android.R.layout.simple_spinner_item, spinnerEntries); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinnerPastEvents.setAdapter(adapter); - spinnerPastEvents.setSelection(adapter.getCount() - 1); - } - - @Override - public void onCancelled(@NonNull DatabaseError databaseError) { - Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); - } - }); - } - - class EventDate { - final long epochTimestamp; - final long ownOverallRating, ownFoodRating, ownHostRating; - final int overallRatingsCount, foodRatingsCount, hostRatingsCount; - final long avgOverallRating, avgFoodRating, avgHostRating; - final String hostName; - - EventDate(long epochTimestamp, String hostUID, Map overallRatings, Map foodRatings, Map hostRatings) { - final String ownUID = mUser.getUid(); - this.epochTimestamp = epochTimestamp; - this.hostName = users.get(hostUID); - - this.overallRatingsCount = overallRatings.size(); - this.foodRatingsCount = foodRatings.size(); - this.hostRatingsCount = hostRatings.size(); - - if(overallRatings.containsKey(ownUID)) ownOverallRating = overallRatings.get(ownUID); - else ownOverallRating = 0; - - if(foodRatings.containsKey(ownUID)) ownFoodRating = foodRatings.get(ownUID); - else ownFoodRating = 0; - - if(hostRatings.containsKey(ownUID)) ownHostRating = hostRatings.get(ownUID); - else ownHostRating = 0; - - long temp = 0; - for(Map.Entry entry : overallRatings.entrySet()) temp += entry.getValue(); - avgOverallRating = overallRatingsCount > 0 ? temp / overallRatingsCount : 0; - - temp = 0; - for(Map.Entry entry : foodRatings.entrySet()) temp += entry.getValue(); - avgFoodRating = foodRatingsCount > 0 ? temp / foodRatingsCount : 0; - - temp = 0; - for(Map.Entry entry : hostRatings.entrySet()) temp += entry.getValue(); - avgHostRating = hostRatingsCount > 0 ? temp / hostRatingsCount : 0; - } - - String getFormattedDate() { - Date date = new Date(this.epochTimestamp * 1000L); - return new SimpleDateFormat("EEEE, dd. MMMM YYYY", Locale.getDefault()).format(date); - } - } +package com.example.iubhgamerapp.ui; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.RatingBar; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import com.example.iubhgamerapp.R; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.ValueEventListener; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class RatingFragment extends Fragment { + private EventDate selectedEvent; + private FirebaseUser mUser; + private DatabaseReference refUsers, refEvents; + private View root; + private List eventDates; + private Map users; + private Spinner spinnerPastEvents; + private RatingBar rbOverall, rbFood, rbHost; + private TextView tvHost; + private TextView tvOverallRatings, tvFoodRatings, tvHostRatings; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + root = inflater.inflate(R.layout.fragment_rating, container, false); + spinnerPastEvents = root.findViewById(R.id.spinner_rating); + rbOverall = root.findViewById(R.id.ratingBar_overall); + rbFood = root.findViewById(R.id.ratingBar_food); + rbHost = root.findViewById(R.id.ratingBar_host); + tvHost = root.findViewById(R.id.textView_rateHost); + tvOverallRatings = root.findViewById(R.id.textView_rate1); + tvFoodRatings = root.findViewById(R.id.textView_rate2); + tvHostRatings = root.findViewById(R.id.textView_rate3); + Button btnRate = root.findViewById(R.id.rate); + + // Set rate button listener + btnRate.setOnClickListener(v -> ratePastEvent()); + + // Set spinner listener + spinnerPastEvents.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + selectedEvent = eventDates.get(position); + + rbOverall.setRating(selectedEvent.ownOverallRating); + rbFood.setRating(selectedEvent.ownFoodRating); + rbHost.setRating(selectedEvent.ownHostRating); + + tvHost.setText("Host (" + selectedEvent.hostName + ")"); + + String sNoRatings = getString(R.string.zero_ratings); + + String str1 = getString(R.string.other_ratings, selectedEvent.avgOverallRating, selectedEvent.overallRatingsCount); + tvOverallRatings.setText(selectedEvent.overallRatingsCount > 0 ? str1 : sNoRatings); + + String str2 = getString(R.string.other_ratings, selectedEvent.avgFoodRating, selectedEvent.foodRatingsCount); + tvFoodRatings.setText(selectedEvent.foodRatingsCount > 0 ? str2 : sNoRatings); + + String str3 = getString(R.string.other_ratings, selectedEvent.avgHostRating, selectedEvent.hostRatingsCount); + tvHostRatings.setText(selectedEvent.hostRatingsCount > 0 ? str3 : sNoRatings); + } + + @Override + public void onNothingSelected(AdapterView parent) { + // Nothing to do here + } + }); + + // Connect to Firebase realtime database + mUser = FirebaseAuth.getInstance().getCurrentUser(); + refUsers = FirebaseDatabase.getInstance().getReference().child("spieler"); + refEvents = FirebaseDatabase.getInstance().getReference().child("termine"); + + // Get list of registered users from Firebase database + refUsers.addValueEventListener(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot dataSnapshot) { + // Save users in HashMap object (key = UID; value = nickname) + users = new HashMap<>(); + for(DataSnapshot ds : dataSnapshot.getChildren()) { + users.put(ds.getKey(), (String)ds.child("nickname").getValue()); + } + + getPastEvents(); + } + + @Override + public void onCancelled(@NonNull DatabaseError databaseError) { + Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); + } + }); + + return root; + } + + /** + * Writes the user's rating of the selected event to database. + */ + private void ratePastEvent() { + long ownOverallRating = (long)rbOverall.getRating(); + long ownFoodRating = (long)rbFood.getRating(); + long ownHostRating = (long)rbHost.getRating(); + + // Write ratings to database IF the user rated every required item + if(ownOverallRating > 0 && ownFoodRating > 0 && ownHostRating > 0) { + DatabaseReference refRatings = refEvents.child(String.valueOf(selectedEvent.epochTimestamp)).child("bewertungen"); + refRatings.child("allgemein").child(mUser.getUid()).setValue(ownOverallRating); + refRatings.child("essen").child(mUser.getUid()).setValue(ownFoodRating); + refRatings.child("gastgeber").child(mUser.getUid()).setValue(ownHostRating); + } + // Otherwise, display error message + else { + Toast.makeText(getContext(), R.string.rate_error, Toast.LENGTH_SHORT).show(); + } + } + + /** + * Reads the most recent events from the database. The user can rate the overall experience, + * food & drinks as well as the event host. + * Only the last 3 events can be rated. + */ + private void getPastEvents() { + refEvents.orderByKey().limitToLast(4).addValueEventListener(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot dataSnapshot) { + // Iterate through events to extract metadata + eventDates = new ArrayList<>(); + for(DataSnapshot ds : dataSnapshot.getChildren()) { + // Check if the event date is actually in the future. + // Skip it, as only past events are unlocked for rating. + long epochTimestamp = Long.parseLong(ds.getKey()); + if(epochTimestamp > (System.currentTimeMillis() / 1000L)) break; + + // Extract event host user id + String hostUID = (String)ds.child("gastgeber").getValue(); + + // Extract individual rating data + Map overallRatings = new HashMap<>(); + Map foodRatings = new HashMap<>(); + Map hostRatings = new HashMap<>(); + for(DataSnapshot dsOverallRatings : ds.child("bewertungen").child("allgemein").getChildren()) { + overallRatings.put(dsOverallRatings.getKey(), (long)dsOverallRatings.getValue()); + } + for(DataSnapshot dsFoodRatings : ds.child("bewertungen").child("essen").getChildren()) { + foodRatings.put(dsFoodRatings.getKey(), (long)dsFoodRatings.getValue()); + } + for(DataSnapshot dsHostRatings : ds.child("bewertungen").child("gastgeber").getChildren()) { + hostRatings.put(dsHostRatings.getKey(), (long)dsHostRatings.getValue()); + } + + // Add event to ArrayList to make data available to other methods + eventDates.add(new EventDate(epochTimestamp, hostUID, overallRatings, foodRatings, hostRatings)); + } + + // Update spinner; set selection to most recent event + List spinnerEntries = new ArrayList<>(); + for(int i = 0; i < eventDates.size(); i++) spinnerEntries.add(eventDates.get(i).getFormattedDate()); + ArrayAdapter adapter = new ArrayAdapter<>(root.getContext(), + android.R.layout.simple_spinner_item, spinnerEntries); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerPastEvents.setAdapter(adapter); + spinnerPastEvents.setSelection(adapter.getCount() - 1); + } + + @Override + public void onCancelled(@NonNull DatabaseError databaseError) { + Toast.makeText(getContext(), R.string.db_comm_err, Toast.LENGTH_SHORT).show(); + } + }); + } + + class EventDate { + final long epochTimestamp; + final long ownOverallRating, ownFoodRating, ownHostRating; + final int overallRatingsCount, foodRatingsCount, hostRatingsCount; + final long avgOverallRating, avgFoodRating, avgHostRating; + final String hostName; + + EventDate(long epochTimestamp, String hostUID, Map overallRatings, Map foodRatings, Map hostRatings) { + final String ownUID = mUser.getUid(); + this.epochTimestamp = epochTimestamp; + this.hostName = users.get(hostUID); + + this.overallRatingsCount = overallRatings.size(); + this.foodRatingsCount = foodRatings.size(); + this.hostRatingsCount = hostRatings.size(); + + if(overallRatings.containsKey(ownUID)) ownOverallRating = overallRatings.get(ownUID); + else ownOverallRating = 0; + + if(foodRatings.containsKey(ownUID)) ownFoodRating = foodRatings.get(ownUID); + else ownFoodRating = 0; + + if(hostRatings.containsKey(ownUID)) ownHostRating = hostRatings.get(ownUID); + else ownHostRating = 0; + + long temp = 0; + for(Map.Entry entry : overallRatings.entrySet()) temp += entry.getValue(); + avgOverallRating = overallRatingsCount > 0 ? temp / overallRatingsCount : 0; + + temp = 0; + for(Map.Entry entry : foodRatings.entrySet()) temp += entry.getValue(); + avgFoodRating = foodRatingsCount > 0 ? temp / foodRatingsCount : 0; + + temp = 0; + for(Map.Entry entry : hostRatings.entrySet()) temp += entry.getValue(); + avgHostRating = hostRatingsCount > 0 ? temp / hostRatingsCount : 0; + } + + String getFormattedDate() { + Date date = new Date(this.epochTimestamp * 1000L); + return new SimpleDateFormat("EEEE, dd. MMMM yyyy", Locale.getDefault()).format(date); + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 67f4bac..8d37849 100755 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -67,7 +67,7 @@ android:id="@+id/text_home4" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="The next event will be held on" + android:text="The next event will be on" android:textAlignment="center" android:textSize="14sp" /> @@ -84,7 +84,7 @@ android:id="@+id/text_home3" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="at" + android:text="hosted by" android:textAlignment="center" android:textSize="14sp" /> diff --git a/app/src/main/res/layout/fragment_rate.xml b/app/src/main/res/layout/fragment_rating.xml old mode 100755 new mode 100644 similarity index 97% rename from app/src/main/res/layout/fragment_rate.xml rename to app/src/main/res/layout/fragment_rating.xml index 046ed23..29aa0fc --- a/app/src/main/res/layout/fragment_rate.xml +++ b/app/src/main/res/layout/fragment_rating.xml @@ -1,134 +1,134 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -