From c41f916577a877e9268e8103d6f75cf915a0e9aa Mon Sep 17 00:00:00 2001 From: Markus Friedli Date: Sun, 17 Dec 2023 16:17:03 +0100 Subject: [PATCH] Use expandable groups in all list views (#487) * Migrate from ListView to ExpandableListView * Group alert log by year * Group test alarms in overview * Group test alarm contexts by activate state * Group alert log by year * Group test alarm contexts by activate state * Group shifts by year month --- .../state/ApplicationPreferences.java | 17 +++ ...aredPreferencesApplicationPreferences.java | 61 ++++++++ .../android/pikettassist/ui/MainActivity.java | 2 +- .../ui/alerts/AlertArrayAdapter.java | 43 ------ .../ui/alerts/AlertExpandableListAdapter.java | 107 ++++++++++++++ .../ui/alerts/AlertListFragment.java | 77 ++++++++-- .../pikettassist/ui/alerts/YearGroup.java | 10 ++ .../ui/common/AbstractListFragment.java | 26 ++-- .../pikettassist/ui/overview/State.java | 17 +++ ...r.java => StateExpandableListAdapter.java} | 86 +++++++++-- .../ui/overview/StateFragment.java | 82 +++++++++-- .../ui/overview/TestAlarmState.java | 23 ++- .../ui/shifts/ShiftArrayAdapter.java | 72 ---------- .../ui/shifts/ShiftExpandableListAdapter.java | 134 ++++++++++++++++++ .../ui/shifts/ShiftListFragment.java | 64 +++++++-- .../ui/shifts/YearMonthGroup.java | 11 ++ .../pikettassist/ui/testalarm/StateGroup.java | 9 ++ .../ui/testalarm/TestAlarmArrayAdapter.java | 46 ------ .../TestAlarmExpandableListAdapter.java | 108 ++++++++++++++ .../ui/testalarm/TestAlarmFragment.java | 69 +++++++-- app/src/main/res/layout/fragment_list.xml | 2 +- .../res/layout/general_list_group_item.xml | 16 +++ app/src/main/res/layout/test_alarm_item.xml | 3 +- app/src/main/res/values-night/colors.xml | 1 + app/src/main/res/values/colors.xml | 2 + .../ui/shifts/ShiftArrayAdapterTest.java | 30 ---- .../ShiftExpandableListAdapterTest.java | 30 ++++ 27 files changed, 877 insertions(+), 271 deletions(-) delete mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertArrayAdapter.java create mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertExpandableListAdapter.java create mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/YearGroup.java rename app/src/main/java/com/github/frimtec/android/pikettassist/ui/overview/{StateArrayAdapter.java => StateExpandableListAdapter.java} (58%) delete mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/shifts/ShiftArrayAdapter.java create mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/shifts/ShiftExpandableListAdapter.java create mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/shifts/YearMonthGroup.java create mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/testalarm/StateGroup.java delete mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/testalarm/TestAlarmArrayAdapter.java create mode 100644 app/src/main/java/com/github/frimtec/android/pikettassist/ui/testalarm/TestAlarmExpandableListAdapter.java create mode 100644 app/src/main/res/layout/general_list_group_item.xml delete mode 100644 app/src/test/java/com/github/frimtec/android/pikettassist/ui/shifts/ShiftArrayAdapterTest.java create mode 100644 app/src/test/java/com/github/frimtec/android/pikettassist/ui/shifts/ShiftExpandableListAdapterTest.java diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/state/ApplicationPreferences.java b/app/src/main/java/com/github/frimtec/android/pikettassist/state/ApplicationPreferences.java index aafab589..2908e0df 100644 --- a/app/src/main/java/com/github/frimtec/android/pikettassist/state/ApplicationPreferences.java +++ b/app/src/main/java/com/github/frimtec/android/pikettassist/state/ApplicationPreferences.java @@ -7,6 +7,7 @@ import java.time.Duration; import java.time.LocalTime; +import java.time.YearMonth; import java.util.Set; public interface ApplicationPreferences { @@ -112,4 +113,20 @@ static ApplicationPreferences instance() { boolean isDayProfile(Context context, LocalTime currentTime); + Set getExpandedAlertLogGroups(Context context); + + void setExpandedAlertLogGroups(Context context, Set values); + + Set getExpandedShiftGroups(Context context); + + void setExpandedShiftGroups(Context context, Set values); + + Set getExpandedTestAlertGroups(Context context); + + void setExpandedTestAlertGroups(Context context, Set values); + + boolean isTestAlarmStatesExpanded(Context context); + + void setTestAlarmStatesExpanded(Context context, boolean values); + } diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/state/SharedPreferencesApplicationPreferences.java b/app/src/main/java/com/github/frimtec/android/pikettassist/state/SharedPreferencesApplicationPreferences.java index 97bc6d36..4321cfc3 100644 --- a/app/src/main/java/com/github/frimtec/android/pikettassist/state/SharedPreferencesApplicationPreferences.java +++ b/app/src/main/java/com/github/frimtec/android/pikettassist/state/SharedPreferencesApplicationPreferences.java @@ -11,6 +11,7 @@ import java.time.Duration; import java.time.LocalTime; +import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.Set; @@ -47,6 +48,10 @@ final class SharedPreferencesApplicationPreferences implements ApplicationPrefer private static final String PREF_KEY_DAY_START_TIME = "day_start_time"; private static final String PREF_KEY_NIGHT_START_TIME = "night_start_time"; private static final String PREF_APP_THEME = "app_theme"; + private static final String PREF_KEY_ALERT_LOG_EXPANDED_GROUPS = "alert_log_expanded_groups"; + private static final String PREF_KEY_SHIFT_EXPANDED_GROUPS = "shift_expanded_groups"; + private static final String PREF_KEY_TEST_ALERT_EXPANDED_GROUPS = "test_alert_expanded_groups"; + private static final String PREF_KEY_TEST_ALARM_EXPANDED_GROUP = "test_alarm_expanded_group"; public static final SharedPreferencesApplicationPreferences INSTANCE = new SharedPreferencesApplicationPreferences(); @@ -250,6 +255,62 @@ public boolean isDayProfile(Context context, LocalTime currentTime) { return currentTime.isAfter(getDayProfileStartTime(context)) && currentTime.isBefore(getNightProfileStartTime(context)); } + @Override + public Set getExpandedAlertLogGroups(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getStringSet(PREF_KEY_ALERT_LOG_EXPANDED_GROUPS, Collections.emptySet()).stream() + .map(Integer::parseInt) + .collect(Collectors.toSet()); + } + + @Override + public void setExpandedAlertLogGroups(Context context, Set values) { + setSharedPreferences(context, setter -> setter.putStringSet(PREF_KEY_ALERT_LOG_EXPANDED_GROUPS, values.stream() + .map(String::valueOf) + .collect(Collectors.toSet()))); + } + + @Override + public Set getExpandedShiftGroups(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getStringSet(PREF_KEY_SHIFT_EXPANDED_GROUPS, Collections.emptySet()).stream() + .map(YearMonth::parse) + .collect(Collectors.toSet()); + } + + @Override + public void setExpandedShiftGroups(Context context, Set values) { + setSharedPreferences(context, setter -> setter.putStringSet(PREF_KEY_SHIFT_EXPANDED_GROUPS, values.stream() + .map(String::valueOf) + .collect(Collectors.toSet()))); + } + + @Override + public Set getExpandedTestAlertGroups(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getStringSet(PREF_KEY_TEST_ALERT_EXPANDED_GROUPS, Set.of(String.valueOf(true), String.valueOf(false))).stream() + .map(Boolean::parseBoolean) + .collect(Collectors.toSet()); + } + + @Override + public void setExpandedTestAlertGroups(Context context, Set values) { + setSharedPreferences(context, setter -> setter.putStringSet(PREF_KEY_TEST_ALERT_EXPANDED_GROUPS, values.stream() + .map(String::valueOf) + .collect(Collectors.toSet()))); + } + + @Override + public boolean isTestAlarmStatesExpanded(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getBoolean(PREF_KEY_TEST_ALARM_EXPANDED_GROUP, false); + } + + @Override + public void setTestAlarmStatesExpanded(Context context, boolean values) { + setSharedPreferences(context, setter -> setter.putBoolean(PREF_KEY_TEST_ALARM_EXPANDED_GROUP, values)); + } + private static int getOnCallNightVolume(Context context) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); return preferences.getInt(PREF_KEY_ON_CALL_NIGHT_VOLUME, R.integer.default_volume_night); diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/MainActivity.java b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/MainActivity.java index c75d2ae5..adf22013 100644 --- a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/MainActivity.java +++ b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/MainActivity.java @@ -204,7 +204,7 @@ private void registerOnSmsAdapter() { private void refresh() { refreshTabLabels(); FragmentManager fm = getSupportFragmentManager(); - var activeFragment = (AbstractListFragment) fm.findFragmentByTag("f" + viewPager.getCurrentItem()); + var activeFragment = (AbstractListFragment) fm.findFragmentByTag("f" + viewPager.getCurrentItem()); if (activeFragment != null) { activeFragment.refresh(); } diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertArrayAdapter.java b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertArrayAdapter.java deleted file mode 100644 index 39963a55..00000000 --- a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertArrayAdapter.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.github.frimtec.android.pikettassist.ui.alerts; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.github.frimtec.android.pikettassist.R; -import com.github.frimtec.android.pikettassist.domain.Alert; - -import java.util.List; -import java.util.Objects; - -class AlertArrayAdapter extends ArrayAdapter { - - AlertArrayAdapter(Context context, List alerts) { - super(context, 0, alerts); - } - - @NonNull - @Override - public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - Alert alert = getItem(position); - Objects.requireNonNull(alert); - if (convertView == null) { - convertView = LayoutInflater.from(getContext()).inflate(R.layout.alert_log_item, parent, false); - } - ImageView playIcon = convertView.findViewById(R.id.alert_log_item_image_play); - playIcon.setVisibility(alert.isClosed() ? View.INVISIBLE : View.VISIBLE); - TextView timeWindow = convertView.findViewById(R.id.alert_log_item_time_window); - TextView durations = convertView.findViewById(R.id.alert_log_item_durations); - timeWindow.setText(AlertViewHelper.getTimeWindow(alert)); - durations.setText(AlertViewHelper.getDurations(getContext(), alert)); - return convertView; - } - -} diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertExpandableListAdapter.java b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertExpandableListAdapter.java new file mode 100644 index 00000000..095f88e0 --- /dev/null +++ b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertExpandableListAdapter.java @@ -0,0 +1,107 @@ +package com.github.frimtec.android.pikettassist.ui.alerts; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseExpandableListAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.github.frimtec.android.pikettassist.R; +import com.github.frimtec.android.pikettassist.domain.Alert; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +class AlertExpandableListAdapter extends BaseExpandableListAdapter { + + private final Context context; + private final List years; + + AlertExpandableListAdapter(Context context, List alerts) { + this.context = context; + Map> groupedAlerts = alerts.stream() + .collect(Collectors.groupingBy( + alert -> LocalDateTime.ofInstant(alert.startTime(), ZoneId.systemDefault()).getYear()) + ); + this.years = groupedAlerts.keySet() + .stream() + .sorted(Comparator.reverseOrder()) + .map(year -> new YearGroup(year, groupedAlerts.get(year))) + .collect(Collectors.toList()); + } + + @Override + public int getGroupCount() { + return this.years.size(); + } + + @Override + public int getChildrenCount(int groupPosition) { + return this.years.get(groupPosition).alerts().size(); + } + + @Override + public Object getGroup(int groupPosition) { + return this.years.get(groupPosition); + } + + @Override + public Object getChild(int groupPosition, int childPosition) { + return this.years.get(groupPosition).alerts().get(childPosition); + } + + @Override + public long getGroupId(int groupPosition) { + return groupPosition; + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return groupPosition * 1_000_000L + childPosition; + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + YearGroup yearGroup = this.years.get(groupPosition); + if (convertView == null) { + convertView = LayoutInflater.from(this.context).inflate(R.layout.general_list_group_item, parent, false); + } + TextView title = convertView.findViewById(R.id.general_list_group_item_title); + title.setText(String.format(Locale.getDefault(), "%d (%d)", yearGroup.year(), yearGroup.alerts().size())); + return convertView; + } + + @Override + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { + Alert alert = this.years.get(groupPosition).alerts().get(childPosition); + Objects.requireNonNull(alert); + if (convertView == null) { + convertView = LayoutInflater.from(this.context).inflate(R.layout.alert_log_item, parent, false); + } + ImageView playIcon = convertView.findViewById(R.id.alert_log_item_image_play); + playIcon.setVisibility(alert.isClosed() ? View.INVISIBLE : View.VISIBLE); + TextView timeWindow = convertView.findViewById(R.id.alert_log_item_time_window); + TextView durations = convertView.findViewById(R.id.alert_log_item_durations); + timeWindow.setText(AlertViewHelper.getTimeWindow(alert)); + durations.setText(AlertViewHelper.getDurations(this.context, alert)); + return convertView; + } + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } +} diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertListFragment.java b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertListFragment.java index 28f2dbc2..44368067 100644 --- a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertListFragment.java +++ b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/AlertListFragment.java @@ -1,5 +1,7 @@ package com.github.frimtec.android.pikettassist.ui.alerts; +import static android.widget.ExpandableListView.getPackedPositionChild; +import static android.widget.ExpandableListView.getPackedPositionGroup; import static java.time.temporal.ChronoUnit.DAYS; import android.annotation.SuppressLint; @@ -14,11 +16,11 @@ import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.EditText; +import android.widget.ExpandableListAdapter; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.ImageButton; -import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; @@ -34,6 +36,7 @@ import com.github.frimtec.android.pikettassist.service.ShiftService; import com.github.frimtec.android.pikettassist.service.dao.AlertDao; import com.github.frimtec.android.pikettassist.service.system.Feature; +import com.github.frimtec.android.pikettassist.state.ApplicationPreferences; import com.github.frimtec.android.pikettassist.ui.FragmentPosition; import com.github.frimtec.android.pikettassist.ui.common.AbstractListFragment; import com.github.frimtec.android.pikettassist.ui.common.DialogHelper; @@ -46,10 +49,16 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.time.Instant; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; -public class AlertListFragment extends AbstractListFragment { +public class AlertListFragment extends AbstractListFragment { private static final String TAG = "AlertListFragment"; @@ -125,11 +134,13 @@ public void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedI } @Override - protected void configureListView(ListView listView) { + protected void configureListView(ExpandableListView listView) { listView.setClickable(true); - listView.setOnItemClickListener((parent, view1, position, id) -> { - Alert selectedAlert = (Alert) listView.getItemAtPosition(position); + listView.setOnChildClickListener((parent, v, groupPosition, childPosition, id) -> { + YearGroup selectedYearGroup = (YearGroup) listView.getExpandableListAdapter().getGroup(groupPosition); + Alert selectedAlert = selectedYearGroup.alerts().get(childPosition); showAlertDetails(selectedAlert); + return true; }); registerForContextMenu(listView); View headerView = getLayoutInflater().inflate(R.layout.alert_header, listView, false); @@ -152,6 +163,45 @@ protected void configureListView(ListView listView) { fileSelectionActivityResultLauncher.launch(intent); }); listView.addHeaderView(headerView); + + listView.setOnGroupExpandListener(groupPosition -> changeExpandedGroupsPreferences(listView, expandedYears -> { + expandedYears.add(((YearGroup) listView.getExpandableListAdapter().getGroup(groupPosition)).year()); + return expandedYears; + })); + listView.setOnGroupCollapseListener(groupPosition -> changeExpandedGroupsPreferences(listView, expandedYears -> { + expandedYears.remove(((YearGroup) listView.getExpandableListAdapter().getGroup(groupPosition)).year()); + return expandedYears; + })); + } + + private void changeExpandedGroupsPreferences(ExpandableListView listView, Function, Set> transformer) { + ExpandableListAdapter adapter = listView.getExpandableListAdapter(); + Set years = new HashSet<>(); + IntStream.range(0, adapter.getGroupCount()).forEach(i -> { + YearGroup item = (YearGroup) adapter.getGroup(i); + years.add(item.year()); + }); + ApplicationPreferences applicationPreferences = ApplicationPreferences.instance(); + Set expandedAlertLogGroups = applicationPreferences.getExpandedAlertLogGroups(getContext()); + expandedAlertLogGroups.retainAll(years); + applicationPreferences.setExpandedAlertLogGroups(getContext(), transformer.apply(expandedAlertLogGroups)); + } + + @Override + protected Set getExpandedGroups(ExpandableListView listView) { + ExpandableListAdapter adapter = listView.getExpandableListAdapter(); + Map yearToPosition = IntStream.range(0, adapter.getGroupCount()) + .boxed() + .collect(Collectors.toMap( + i -> ((YearGroup) adapter.getGroup(i)).year(), + i -> i + )); + HashSet expandedGroups = ApplicationPreferences.instance().getExpandedAlertLogGroups(getContext()).stream() + .filter(yearToPosition::containsKey) + .map(yearToPosition::get) + .collect(Collectors.toCollection(HashSet::new)); + expandedGroups.add(0); + return expandedGroups; } private long countAlertsWithinLastDays(List alertList, int days) { @@ -160,25 +210,28 @@ private long countAlertsWithinLastDays(List alertList, int days) { } @Override - protected ArrayAdapter createAdapter() { - return new AlertArrayAdapter(getContext(), loadAlertList()); + protected ExpandableListAdapter createAdapter() { + return new AlertExpandableListAdapter(getContext(), loadAlertList()); } @Override public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View view, ContextMenu.ContextMenuInfo menuInfo) { + ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo; + if (getPackedPositionChild(info.packedPosition) == -1) { + return; + } addContextMenu(menu, MENU_CONTEXT_VIEW_ID, R.string.list_item_menu_view); addContextMenu(menu, MENU_CONTEXT_DELETE_ID, R.string.list_item_menu_delete); } @Override public boolean onFragmentContextItemSelected(MenuItem item) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) item.getMenuInfo(); if (info == null) { Log.w(TAG, "No menu item was selected"); return false; } - ListView listView = getListView(); - Alert selectedAlert = (Alert) listView.getItemAtPosition(info.position); + Alert selectedAlert = (Alert) getListView().getExpandableListAdapter().getChild(getPackedPositionGroup(info.packedPosition), getPackedPositionChild(info.packedPosition)); switch (item.getItemId()) { case MENU_CONTEXT_VIEW_ID -> { showAlertDetails(selectedAlert); diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/YearGroup.java b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/YearGroup.java new file mode 100644 index 00000000..a5d45d66 --- /dev/null +++ b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/alerts/YearGroup.java @@ -0,0 +1,10 @@ +package com.github.frimtec.android.pikettassist.ui.alerts; + +import com.github.frimtec.android.pikettassist.domain.Alert; + +import java.util.List; + +public record YearGroup(int year, List alerts) { + + +} diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/common/AbstractListFragment.java b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/common/AbstractListFragment.java index 72967e99..046d89c6 100644 --- a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/common/AbstractListFragment.java +++ b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/common/AbstractListFragment.java @@ -7,8 +7,8 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ListView; +import android.widget.ExpandableListAdapter; +import android.widget.ExpandableListView; import androidx.annotation.NonNull; import androidx.annotation.StringRes; @@ -19,11 +19,13 @@ import com.github.frimtec.android.pikettassist.ui.FragmentPosition; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import java.util.Collections; import java.util.Optional; +import java.util.Set; -public abstract class AbstractListFragment extends Fragment { +public abstract class AbstractListFragment extends Fragment { - private ListView listView; + private ExpandableListView listView; private FloatingActionButton addButton; private final FragmentPosition fragmentPosition; @@ -65,21 +67,25 @@ protected Optional addAction() { } public final void refresh() { - ArrayAdapter adapter = createAdapter(); - listView.setAdapter(adapter); + listView.setAdapter(createAdapter()); + getExpandedGroups(listView).forEach(groupPosition -> listView.expandGroup(groupPosition)); addButton.setVisibility(isAddButtonVisible() ? View.VISIBLE : View.INVISIBLE); } + protected Set getExpandedGroups(ExpandableListView listView) { + return Collections.emptySet(); + } + protected boolean isAddButtonVisible() { return false; } - protected abstract void configureListView(ListView listView); + protected abstract void configureListView(ExpandableListView listView); - protected abstract ArrayAdapter createAdapter(); + protected abstract ExpandableListAdapter createAdapter(); - protected ListView getListView() { - return listView; + protected ExpandableListView getListView() { + return this.listView; } @Override diff --git a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/overview/State.java b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/overview/State.java index 7a43bb10..17d08a64 100644 --- a/app/src/main/java/com/github/frimtec/android/pikettassist/ui/overview/State.java +++ b/app/src/main/java/com/github/frimtec/android/pikettassist/ui/overview/State.java @@ -5,6 +5,8 @@ import android.view.MenuItem; import android.widget.Button; +import java.util.Collections; +import java.util.List; import java.util.function.Supplier; public class State { @@ -15,6 +17,8 @@ public class State { private final Supplier