diff --git a/app/src/main/java/de/synyx/android/meeroo/business/event/EventRepository.java b/app/src/main/java/de/synyx/android/meeroo/business/event/EventRepository.java index 0025468..fce1515 100644 --- a/app/src/main/java/de/synyx/android/meeroo/business/event/EventRepository.java +++ b/app/src/main/java/de/synyx/android/meeroo/business/event/EventRepository.java @@ -7,13 +7,25 @@ /** - * @author Max Dobler - dobler@synyx.de + * @author Max Dobler - dobler@synyx.de */ public interface EventRepository { + /** + * Load today's events for room. + */ Observable loadAllEventsForRoom(long roomId); + /** + * Load events for room. + * + * @param roomId room id + * @param days amount of days to query events. Pass 1 for today, to for today and tomorrow, etc. + */ + Observable loadAllEventsForRoom(long roomId, int days); + + Maybe insertEvent(long calendarId, String title, DateTime start, DateTime end); diff --git a/app/src/main/java/de/synyx/android/meeroo/data/EventAdapter.java b/app/src/main/java/de/synyx/android/meeroo/data/EventAdapter.java index 12e712d..877ce4f 100644 --- a/app/src/main/java/de/synyx/android/meeroo/data/EventAdapter.java +++ b/app/src/main/java/de/synyx/android/meeroo/data/EventAdapter.java @@ -13,7 +13,7 @@ */ public interface EventAdapter { - Observable getEventsForRoom(long roomId); + Observable getEventsForRoom(long roomId, int days); Maybe insertEvent(long calendarId, DateTime start, DateTime end, String title); diff --git a/app/src/main/java/de/synyx/android/meeroo/data/EventAdapterImpl.java b/app/src/main/java/de/synyx/android/meeroo/data/EventAdapterImpl.java index 2f8fb08..7249769 100644 --- a/app/src/main/java/de/synyx/android/meeroo/data/EventAdapterImpl.java +++ b/app/src/main/java/de/synyx/android/meeroo/data/EventAdapterImpl.java @@ -44,7 +44,7 @@ public EventAdapterImpl(ContentResolver contentResolver) { } @Override - public Observable getEventsForRoom(long roomId) { + public Observable getEventsForRoom(long roomId, int days) { String[] projection = { Instances.EVENT_ID, // @@ -58,7 +58,7 @@ public Observable getEventsForRoom(long roomId) { String selection = Instances.CALENDAR_ID + " = " + roomId; String sortChronological = Instances.BEGIN + " ASC"; - Cursor result = contentResolver.query(constructContentUri(), projection, selection, null, sortChronological); + Cursor result = contentResolver.query(constructContentUri(days), projection, selection, null, sortChronological); return Observable.fromIterable(fromCursor(result)) // .doAfterNext(closeCursorIfLast()) // @@ -138,11 +138,15 @@ private String getNewDuration(DateTime start, DateTime end) { } - private Uri constructContentUri() { + private Uri constructContentUri(int days) { Uri.Builder builder = Instances.CONTENT_URI.buildUpon(); long now = new Date().getTime(); - long endOfDay = LocalDateTime.now().withTime(23, 59, 59, 999).toDate().getTime(); + long endOfDay = LocalDateTime + .now() + .plusDays(days - 1) + .withTime(23, 59, 59, 999) + .toDate().getTime(); ContentUris.appendId(builder, now); ContentUris.appendId(builder, endOfDay); diff --git a/app/src/main/java/de/synyx/android/meeroo/data/EventRepositoryImpl.java b/app/src/main/java/de/synyx/android/meeroo/data/EventRepositoryImpl.java index adcffd7..84d0eca 100644 --- a/app/src/main/java/de/synyx/android/meeroo/data/EventRepositoryImpl.java +++ b/app/src/main/java/de/synyx/android/meeroo/data/EventRepositoryImpl.java @@ -2,21 +2,19 @@ import androidx.annotation.NonNull; +import org.joda.time.DateTime; + +import java.util.concurrent.ConcurrentHashMap; + import de.synyx.android.meeroo.business.event.EventModel; import de.synyx.android.meeroo.business.event.EventRepository; - import io.reactivex.Maybe; import io.reactivex.Observable; - import io.reactivex.functions.Function; -import org.joda.time.DateTime; - -import java.util.concurrent.ConcurrentHashMap; - /** - * @author Max Dobler - dobler@synyx.de + * @author Max Dobler - dobler@synyx.de */ public class EventRepositoryImpl implements EventRepository { @@ -35,12 +33,18 @@ public EventRepositoryImpl(EventAdapter eventAdapter, AttendeeAdapter attendeeAd @Override public Observable loadAllEventsForRoom(long roomId) { - return eventAdapter.getEventsForRoom(roomId) // - .map(this::setEndIfCached) - .flatMap(loadAttendees()); + return loadAllEventsForRoom(roomId, 1); } + @Override + public Observable loadAllEventsForRoom(long roomId, int days) { + + return eventAdapter.getEventsForRoom(roomId, days) // + .map(this::setEndIfCached) + .flatMap(loadAttendees()); + } + private EventModel setEndIfCached(EventModel event) { DateTime end = eventEndCache.get(event.getId()); @@ -73,8 +77,8 @@ public boolean updateEvent(long eventId, DateTime start, DateTime end, boolean r private Function> loadAttendees() { return - event -> - attendeeAdapter.getAttendeesForEvent(event.getId()) - .collectInto(event, EventModel::addAttendee).toObservable(); + event -> + attendeeAdapter.getAttendeesForEvent(event.getId()) + .collectInto(event, EventModel::addAttendee).toObservable(); } } diff --git a/app/src/main/java/de/synyx/android/meeroo/domain/Agenda.kt b/app/src/main/java/de/synyx/android/meeroo/domain/Agenda.kt index 5cc9cf0..f5f48e6 100644 --- a/app/src/main/java/de/synyx/android/meeroo/domain/Agenda.kt +++ b/app/src/main/java/de/synyx/android/meeroo/domain/Agenda.kt @@ -49,11 +49,11 @@ class Agenda { fun getCurrentMeeting() = reservations.firstOrNull { it.isCurrentlyActive } - fun getUpcomingReservation() = reservations.firstOrNull { it.isUpcoming } + fun getUpcomingReservation() = getReservationsToday().firstOrNull { it.isUpcoming } fun getSecondUpcomingReservation() = - reservations + getReservationsToday() .filter { it.isUpcoming } .drop(1) .firstOrNull() @@ -68,14 +68,16 @@ class Agenda { fun getReservations(): List = unmodifiableList(reservations) - fun hasCurrentReservation() = reservations.any { it.isCurrentlyActive } + fun hasCurrentReservation() = reservations.any { it.isCurrentlyActive } - private fun sumTimeOfOngoingReservations(): (TimeSpan, TimeSpan) -> TimeSpan { + private fun sumTimeOfOngoingReservations(): (TimeSpan, TimeSpan) -> TimeSpan { - return { available, nextTimeSpan -> - if (nextTimeSpan.begin.isAfter(available.end)) available - else TimeSpan(available.begin, nextTimeSpan.end) - } + return { available, nextTimeSpan -> + if (nextTimeSpan.begin.isAfter(available.end)) available + else TimeSpan(available.begin, nextTimeSpan.end) } + } + + public fun getReservationsToday() = reservations.filter { it.isToday } } diff --git a/app/src/main/java/de/synyx/android/meeroo/domain/MeetingRoom.kt b/app/src/main/java/de/synyx/android/meeroo/domain/MeetingRoom.kt index 134d30f..94fc502 100644 --- a/app/src/main/java/de/synyx/android/meeroo/domain/MeetingRoom.kt +++ b/app/src/main/java/de/synyx/android/meeroo/domain/MeetingRoom.kt @@ -42,7 +42,7 @@ class MeetingRoom(val calendarId: Long, val name: String) { fun getUpcomingReservation(): Reservation? = agenda.getUpcomingReservation() - fun getReservations(): List = agenda.getReservations(); + fun getReservations(): List = agenda.getReservations() fun getAvailabilityTime(): Duration? { diff --git a/app/src/main/java/de/synyx/android/meeroo/domain/Reservation.java b/app/src/main/java/de/synyx/android/meeroo/domain/Reservation.java index cdcf8eb..17b2090 100644 --- a/app/src/main/java/de/synyx/android/meeroo/domain/Reservation.java +++ b/app/src/main/java/de/synyx/android/meeroo/domain/Reservation.java @@ -110,6 +110,10 @@ public boolean isUpcoming() { return begin.isAfter(timeProvider.now()); } + public boolean isToday() { + return begin.withTimeAtStartOfDay().equals(timeProvider.now().withTimeAtStartOfDay()); + } + public String getTitle() { diff --git a/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationViewHolder.java b/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationViewHolder.java deleted file mode 100644 index 7a3f789..0000000 --- a/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationViewHolder.java +++ /dev/null @@ -1,57 +0,0 @@ -package de.synyx.android.meeroo.screen.main.agenda; - -import android.view.View; - -import android.widget.TextView; - -import androidx.annotation.NonNull; - -import androidx.recyclerview.widget.RecyclerView; - -import de.synyx.android.meeroo.R; -import de.synyx.android.meeroo.domain.Reservation; - -import org.joda.time.DateTime; -import org.joda.time.format.DateTimeFormat; - -import static java.lang.String.format; - - -/** - * @author Max Dobler - dobler@synyx.de - */ -class ReservationViewHolder extends RecyclerView.ViewHolder { - - ReservationViewHolder(@NonNull View itemView) { - - super(itemView); - } - - void bind(Reservation reservation) { - - setTitle(reservation); - setTimespan(reservation); - } - - - private void setTitle(Reservation reservation) { - - TextView reservationTitle = itemView.findViewById(R.id.reservation_title); - reservationTitle.setText(reservation.getTitle()); - } - - - private void setTimespan(Reservation reservation) { - - TextView reservationTimespan = itemView.findViewById(R.id.reservation_timespan); - String beginTime = formatTime(reservation.getBegin()); - String endTime = formatTime(reservation.getEnd()); - reservationTimespan.setText(format("%s - %s", beginTime, endTime)); - } - - - private String formatTime(DateTime dateTime) { - - return DateTimeFormat.forPattern("HH:mm").print(dateTime); - } -} diff --git a/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationViewHolder.kt b/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationViewHolder.kt new file mode 100644 index 0000000..4f581ea --- /dev/null +++ b/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationViewHolder.kt @@ -0,0 +1,49 @@ +package de.synyx.android.meeroo.screen.main.agenda + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import de.synyx.android.meeroo.domain.Reservation +import android.widget.TextView +import de.synyx.android.meeroo.R +import org.joda.time.DateTime +import org.joda.time.LocalDate +import org.joda.time.format.DateTimeFormat + +/** + * @author Max Dobler - dobler@synyx.de + */ +class ReservationViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + fun bind(item: AgendaListItem.ReservationItem) { + setTitle(item.reservation) + setTimespan(item.reservation) + } + + private fun setTitle(reservation: Reservation) { + val reservationTitle = itemView.findViewById(R.id.reservation_title) + reservationTitle.text = reservation.title + } + + private fun setTimespan(reservation: Reservation) { + val reservationTimespan = itemView.findViewById(R.id.reservation_timespan) + val beginTime = formatTime(reservation.getBegin()) + val endTime = formatTime(reservation.getEnd()) + reservationTimespan.text = String.format("%s - %s", beginTime, endTime) + } + + private fun formatTime(dateTime: DateTime): String { + return DateTimeFormat.forPattern("HH:mm").print(dateTime) + } +} + +class DateHeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + fun bind(dateHeader: AgendaListItem.DateHeader) { + val label = when (dateHeader.dateTime) { + LocalDate() -> itemView.context.getString(R.string.today) + LocalDate().plusDays(1) -> itemView.context.getString(R.string.tomorrow) + else -> DateTimeFormat.fullDate().print(dateHeader.dateTime) + } + + itemView.findViewById(R.id.date_header).text = label + } +} \ No newline at end of file diff --git a/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationsRecyclerAdapter.java b/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationsRecyclerAdapter.java deleted file mode 100644 index 07e4e5c..0000000 --- a/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationsRecyclerAdapter.java +++ /dev/null @@ -1,57 +0,0 @@ -package de.synyx.android.meeroo.screen.main.agenda; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import androidx.recyclerview.widget.RecyclerView; - -import de.synyx.android.meeroo.R; -import de.synyx.android.meeroo.domain.Reservation; - -import java.util.ArrayList; -import java.util.List; - - -/** - * @author Max Dobler - dobler@synyx.de - */ -public class ReservationsRecyclerAdapter extends RecyclerView.Adapter { - - private List reservations = new ArrayList<>(); - - @NonNull - @Override - public ReservationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) { - - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_agenda_reservation, parent, false); - - return new ReservationViewHolder(view); - } - - - @Override - public void onBindViewHolder(@NonNull ReservationViewHolder reservationViewHolder, int index) { - - Reservation reservation = reservations.get(index); - reservationViewHolder.bind(reservation); - } - - - @Override - public int getItemCount() { - - return reservations.size(); - } - - - public void updateReservations(List newReservations) { - - reservations.clear(); - reservations.addAll(newReservations); - - notifyDataSetChanged(); - } -} diff --git a/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationsRecyclerAdapter.kt b/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationsRecyclerAdapter.kt new file mode 100644 index 0000000..1e6bcc9 --- /dev/null +++ b/app/src/main/java/de/synyx/android/meeroo/screen/main/agenda/ReservationsRecyclerAdapter.kt @@ -0,0 +1,66 @@ +package de.synyx.android.meeroo.screen.main.agenda + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import de.synyx.android.meeroo.R +import de.synyx.android.meeroo.domain.Reservation +import org.joda.time.LocalDate + +/** + * @author Max Dobler - dobler@synyx.de + */ +class ReservationsRecyclerAdapter : RecyclerView.Adapter() { + + private val listItems: MutableList = mutableListOf() + + override fun getItemViewType(position: Int): Int { + return when (listItems[position]) { + is AgendaListItem.DateHeader -> 0 + is AgendaListItem.ReservationItem -> 1 + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = + when (viewType) { + 0 -> DateHeaderViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.item_agenda_date_header, parent, false) + ) + 1 -> ReservationViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.item_agenda_reservation, parent, false) + ) + else -> throw IllegalArgumentException("Can't create view holder for illegal viewType $viewType") + } + + override fun onBindViewHolder(viewHolder: ViewHolder, index: Int) { + when (viewHolder) { + is DateHeaderViewHolder -> viewHolder.bind(listItems[index] as AgendaListItem.DateHeader) + is ReservationViewHolder -> viewHolder.bind(listItems[index] as AgendaListItem.ReservationItem) + } + } + + override fun getItemCount(): Int { + return listItems.size + } + + @SuppressLint("NotifyDataSetChanged") + fun updateReservations(newReservations: List) { + listItems.clear() + + newReservations.groupBy { it.begin.toLocalDate() } + .map { entry -> + listOf(AgendaListItem.DateHeader(entry.key)) + + entry.value.map { AgendaListItem.ReservationItem(it) } + }.flatten() + .also { listItems.addAll(it) } + + notifyDataSetChanged() + } +} + +sealed class AgendaListItem { + data class DateHeader(val dateTime: LocalDate) : AgendaListItem() + data class ReservationItem(val reservation: Reservation) : AgendaListItem() +} \ No newline at end of file diff --git a/app/src/main/java/de/synyx/android/meeroo/screen/main/status/LoadRoomUseCase.java b/app/src/main/java/de/synyx/android/meeroo/screen/main/status/LoadRoomUseCase.java index 2b4cf95..a4c7789 100644 --- a/app/src/main/java/de/synyx/android/meeroo/screen/main/status/LoadRoomUseCase.java +++ b/app/src/main/java/de/synyx/android/meeroo/screen/main/status/LoadRoomUseCase.java @@ -28,24 +28,24 @@ public LoadRoomUseCase() { public Maybe execute(long calendarId) { return roomCalendarRepository.loadRoom(calendarId) - .map(roomCalendar -> new MeetingRoom(roomCalendar.getCalendarId(), roomCalendar.getName())) - .flatMap(this::addReservations); + .map(roomCalendar -> new MeetingRoom(roomCalendar.getCalendarId(), roomCalendar.getName())) + .flatMap(this::loadReservations); } - private Maybe addReservations(MeetingRoom meetingRoom) { + private Maybe loadReservations(MeetingRoom meetingRoom) { return loadEventsFor(meetingRoom) // - .map(event -> + .map(event -> new Reservation(event.getId(), event.getName(), event.getBegin(), event.getEnd(), - event.isRecurring())) // - .collectInto(meetingRoom, MeetingRoom::addReservation) // - .toMaybe(); + event.isRecurring())) // + .collectInto(meetingRoom, MeetingRoom::addReservation) // + .toMaybe(); } private Observable loadEventsFor(MeetingRoom meetingRoom) { - return eventRepository.loadAllEventsForRoom(meetingRoom.getCalendarId()); + return eventRepository.loadAllEventsForRoom(meetingRoom.getCalendarId(), 2); } } diff --git a/app/src/main/res/layout-w600dp/activity_main.xml b/app/src/main/res/layout-w600dp/activity_main.xml index 032aa69..a7d8f17 100644 --- a/app/src/main/res/layout-w600dp/activity_main.xml +++ b/app/src/main/res/layout-w600dp/activity_main.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="0dp" - android:background="@color/background_dark" + android:background="@color/background_black" android:padding="0dp" tools:context="de.synyx.android.meeroo.screen.main.MainActivity"> @@ -21,7 +21,7 @@ android:id="@+id/header_title" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="20dp" + android:layout_marginStart="32dp" android:layout_marginTop="16dp" android:maxLines="1" android:ellipsize="end" diff --git a/app/src/main/res/layout/fragment_agenda.xml b/app/src/main/res/layout/fragment_agenda.xml index 6522efe..49a57bb 100644 --- a/app/src/main/res/layout/fragment_agenda.xml +++ b/app/src/main/res/layout/fragment_agenda.xml @@ -4,10 +4,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background_dark" - android:paddingStart="16dp" + android:background="@color/background_black" android:paddingTop="@dimen/header_height" - android:paddingEnd="16dp" android:paddingBottom="16dp" tools:context="de.synyx.android.meeroo.screen.main.MainActivity"> diff --git a/app/src/main/res/layout/item_agenda_date_header.xml b/app/src/main/res/layout/item_agenda_date_header.xml new file mode 100644 index 0000000..7559dbd --- /dev/null +++ b/app/src/main/res/layout/item_agenda_date_header.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_agenda_reservation.xml b/app/src/main/res/layout/item_agenda_reservation.xml index e8addae..d1acf7e 100644 --- a/app/src/main/res/layout/item_agenda_reservation.xml +++ b/app/src/main/res/layout/item_agenda_reservation.xml @@ -3,8 +3,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="16dp" - android:paddingBottom="16dp"> + android:background="@color/background_dark" + android:paddingVertical="16dp" + android:paddingHorizontal="32dp" + android:layout_marginVertical="1dp"> Bestätigen Empfohlen für Google-Kalender Empfohlen für CalDAV-Kalender + Heute + Morgen \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 28ab312..533dc07 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -88,5 +88,7 @@ Confirm Recommended for Google calender Recommended for CalDAV calender + Today + Tomorrow