Før tilbakeknapp overalt

This commit is contained in:
ErolHaagenrud 2025-12-18 13:19:53 +01:00
parent d3b9504f9a
commit 91f8c2c0c7
5 changed files with 231 additions and 141 deletions

View file

@ -12,8 +12,8 @@ android {
applicationId = "com.kbs.kbsintranett" applicationId = "com.kbs.kbsintranett"
minSdk = 28 minSdk = 28
targetSdk = 34 targetSdk = 34
versionCode = 2 versionCode = 3
versionName = "1.2" versionName = "1.4"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }

View file

@ -3,6 +3,8 @@ package com.kbs.kbsintranett;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.DatePickerDialog; import android.app.DatePickerDialog;
import android.app.TimePickerDialog; import android.app.TimePickerDialog;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -134,13 +136,11 @@ public class CreateEventFragment extends Fragment {
private void prefillForm(CalendarEvent event) { private void prefillForm(CalendarEvent event) {
etTitle.setText(event.getTitle()); etTitle.setText(event.getTitle());
// Rens beskrivelsen for #varsel tag
String cleanDesc = event.getDescription().replaceAll("#varsel:[\\d,]+", "").trim(); String cleanDesc = event.getDescription().replaceAll("#varsel:[\\d,]+", "").trim();
etDesc.setText(cleanDesc); etDesc.setText(cleanDesc);
etLocation.setText(event.getLocation()); etLocation.setText(event.getLocation());
// --- FIKS 404 FEIL VED OPPDATERING --- // --- FIKS 404 FEIL VED OPPDATERING ---
// 1. Sett spinneren til riktig kalender
ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinnerCalendar.getAdapter(); ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinnerCalendar.getAdapter();
if (adapter != null) { if (adapter != null) {
int position = adapter.getPosition(event.getCalendarName()); int position = adapter.getPosition(event.getCalendarName());
@ -148,12 +148,9 @@ public class CreateEventFragment extends Fragment {
spinnerCalendar.setSelection(position); spinnerCalendar.setSelection(position);
} }
} }
// 2. Deaktiver spinneren. Man kan ikke bytte kalender ved oppdatering (API-begrensning).
// Man slette og opprette nytt for å flytte.
spinnerCalendar.setEnabled(false); spinnerCalendar.setEnabled(false);
// ------------------------------------- // -------------------------------------
// Dato-parsing
try { try {
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()); SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
@ -161,7 +158,6 @@ public class CreateEventFragment extends Fragment {
String start = event.getRawDate(); String start = event.getRawDate();
if (start != null) { if (start != null) {
if (start.length() == 10) { if (start.length() == 10) {
// Heldags
switchAllDay.setChecked(true); switchAllDay.setChecked(true);
Date d = simpleFormat.parse(start); Date d = simpleFormat.parse(start);
startCal.setTime(d); startCal.setTime(d);
@ -176,7 +172,6 @@ public class CreateEventFragment extends Fragment {
endCal.setTime(d); endCal.setTime(d);
} }
} else if (start.contains("T")) { } else if (start.contains("T")) {
// Vanlig tid (kutt tidssone)
if (start.length() > 19) start = start.substring(0, 19); if (start.length() > 19) start = start.substring(0, 19);
startCal.setTime(isoFormat.parse(start)); startCal.setTime(isoFormat.parse(start));
@ -191,10 +186,8 @@ public class CreateEventFragment extends Fragment {
} }
} }
// Varsler
List<Integer> existingReminders = event.getReminders(); List<Integer> existingReminders = event.getReminders();
if (!existingReminders.isEmpty()) { if (!existingReminders.isEmpty()) {
// Fjern alle sjekkmerker først (15 min er default checked)
for (int i = 0; i < chipGroupReminders.getChildCount(); i++) { for (int i = 0; i < chipGroupReminders.getChildCount(); i++) {
((Chip) chipGroupReminders.getChildAt(i)).setChecked(false); ((Chip) chipGroupReminders.getChildAt(i)).setChecked(false);
} }
@ -217,15 +210,68 @@ public class CreateEventFragment extends Fragment {
} }
} }
private void setupCalendarSpinner() { // --- NY LOGIKK FOR FARGER I SPINNER ---
// HENT DYNAMISK LISTE FRA USERMANAGER
List<String> calendars = UserManager.getInstance().getWriteableCalendars();
private String getCalendarColor(String name) {
// Matcher fargene i PHP-config (V12.6)
switch (name) {
case "Felles": return "#0069B3"; // KBS Blå
case "Administrasjonen": return "#607D8B"; // Blue Grey
case "Serviceavdelingen": return "#E65100"; // Orange
case "Automasjonsavdelingen": return "#2E7D32"; // Green
case "Prosjektavdelingen": return "#7B1FA2"; // Purple
default: return "#888888"; // Grå fallback
}
}
private void setupCalendarSpinner() {
List<String> calendars = UserManager.getInstance().getWriteableCalendars();
if (calendars.isEmpty()) calendars.add("Felles"); if (calendars.isEmpty()) calendars.add("Felles");
spinnerCalendar.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, calendars)); // Vi bruker en Custom Adapter for å styre farger
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_spinner_item, calendars) {
// getView: Dette er det som vises i selve boksen når noe er valgt
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
String calName = getItem(position);
String colorHex = getCalendarColor(calName);
// Sett bakgrunnsfarge lik kalenderfarge
view.setBackgroundColor(Color.parseColor(colorHex));
// Hvit tekst for kontrast
view.setTextColor(Color.WHITE);
view.setTypeface(null, Typeface.BOLD);
return view;
} }
// getDropDownView: Dette er listen som popper opp
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getDropDownView(position, convertView, parent);
String calName = getItem(position);
String colorHex = getCalendarColor(calName);
// Her holder vi bakgrunnen hvit, men farger teksten
view.setBackgroundColor(Color.WHITE);
view.setTextColor(Color.parseColor(colorHex));
view.setTypeface(null, Typeface.BOLD);
return view;
}
};
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerCalendar.setAdapter(adapter);
}
// --------------------------------------
private void setupReminderChips() { private void setupReminderChips() {
addChip("Ved start", 0); addChip("Ved start", 0);
addChip("5 min", 5); addChip("5 min", 5);
@ -475,7 +521,6 @@ public class CreateEventFragment extends Fragment {
} }
} }
// NY: Henter navnet kalenderen direkte fra Spinneren
private String getCalendarSlug() { private String getCalendarSlug() {
if (spinnerCalendar.getSelectedItem() != null) { if (spinnerCalendar.getSelectedItem() != null) {
return spinnerCalendar.getSelectedItem().toString(); return spinnerCalendar.getSelectedItem().toString();

View file

@ -20,6 +20,7 @@ import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.work.PeriodicWorkRequest; import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager; import androidx.work.WorkManager;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -37,7 +38,10 @@ public class HomeFragment extends Fragment {
private ActivityResultLauncher<String> requestPermissionLauncher; private ActivityResultLauncher<String> requestPermissionLauncher;
private RecyclerView calendarRecycler; private RecyclerView calendarRecycler;
private RecyclerView newsRecycler; private RecyclerView newsRecycler;
private ProgressBar mainProgressBar; // Variabelen som være her private ProgressBar mainProgressBar;
private SwipeRefreshLayout swipeRefreshLayout;
private int activeNetworkCalls = 0;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
@ -46,7 +50,7 @@ public class HomeFragment extends Fragment {
new ActivityResultContracts.RequestPermission(), new ActivityResultContracts.RequestPermission(),
isGranted -> { isGranted -> {
if (calendarRecycler != null) { if (calendarRecycler != null) {
fetchCalendarEvents(calendarRecycler); refreshData();
} }
} }
); );
@ -63,11 +67,12 @@ public class HomeFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
// Her leter vi etter IDen som finnes i XML-filen over
mainProgressBar = view.findViewById(R.id.main_loading_spinner); mainProgressBar = view.findViewById(R.id.main_loading_spinner);
if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE); if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE);
swipeRefreshLayout = view.findViewById(R.id.swipe_refresh_home);
swipeRefreshLayout.setOnRefreshListener(this::refreshData);
View profileBtn = view.findViewById(R.id.btn_profile); View profileBtn = view.findViewById(R.id.btn_profile);
if (profileBtn != null) { if (profileBtn != null) {
profileBtn.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.navigation_profile)); profileBtn.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.navigation_profile));
@ -75,7 +80,6 @@ public class HomeFragment extends Fragment {
Button btnCreateEvent = view.findViewById(R.id.btn_create_event); Button btnCreateEvent = view.findViewById(R.id.btn_create_event);
List<String> writeable = UserManager.getInstance().getWriteableCalendars(); List<String> writeable = UserManager.getInstance().getWriteableCalendars();
if (writeable != null && !writeable.isEmpty()) { if (writeable != null && !writeable.isEmpty()) {
btnCreateEvent.setVisibility(View.VISIBLE); btnCreateEvent.setVisibility(View.VISIBLE);
btnCreateEvent.setOnClickListener(v -> { btnCreateEvent.setOnClickListener(v -> {
@ -85,6 +89,7 @@ public class HomeFragment extends Fragment {
btnCreateEvent.setVisibility(View.GONE); btnCreateEvent.setVisibility(View.GONE);
} }
// KALENDER: Standard scrolling (slik at man kan scrolle i vinduet 230dp)
calendarRecycler = view.findViewById(R.id.recycler_calendar); calendarRecycler = view.findViewById(R.id.recycler_calendar);
calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext())); calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {})); calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {}));
@ -100,6 +105,7 @@ public class HomeFragment extends Fragment {
} }
} }
// NYHETER: Nested scrolling disabled (skal vises i full høyde under kalenderen)
newsRecycler = view.findViewById(R.id.recycler_news); newsRecycler = view.findViewById(R.id.recycler_news);
newsRecycler.setLayoutManager(new LinearLayoutManager(getContext())); newsRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
newsRecycler.setNestedScrollingEnabled(false); newsRecycler.setNestedScrollingEnabled(false);
@ -112,22 +118,41 @@ public class HomeFragment extends Fragment {
}); });
} }
fetchNewsFromWordpress(newsRecycler); refreshData();
} }
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
if (activeNetworkCalls == 0) {
refreshData();
}
}
private void refreshData() {
activeNetworkCalls = 2;
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED) {
fetchCalendarEvents(calendarRecycler); fetchCalendarEvents(calendarRecycler);
} else { } else {
checkLoadingComplete();
requestPermissionLauncher.launch(Manifest.permission.READ_CALENDAR); requestPermissionLauncher.launch(Manifest.permission.READ_CALENDAR);
} }
fetchNewsFromWordpress(newsRecycler);
}
private void checkLoadingComplete() {
activeNetworkCalls--;
if (activeNetworkCalls <= 0) {
activeNetworkCalls = 0;
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
}
} }
private void fetchCalendarEvents(RecyclerView recyclerView) { private void fetchCalendarEvents(RecyclerView recyclerView) {
new Thread(() -> { new Thread(() -> {
// isPreview = true for raskere lasting
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true); List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
new Handler(Looper.getMainLooper()).post(() -> fetchApiEvents(recyclerView, deviceEvents)); new Handler(Looper.getMainLooper()).post(() -> fetchApiEvents(recyclerView, deviceEvents));
}).start(); }).start();
@ -139,8 +164,6 @@ public class HomeFragment extends Fragment {
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) { public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
if (!isAdded()) return; if (!isAdded()) return;
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
List<CalendarEvent> apiEvents = new ArrayList<>(); List<CalendarEvent> apiEvents = new ArrayList<>();
if (response.isSuccessful() && response.body() != null) { if (response.isSuccessful() && response.body() != null) {
apiEvents = response.body(); apiEvents = response.body();
@ -159,34 +182,38 @@ public class HomeFragment extends Fragment {
} }
} }
List<CalendarEvent> top5 = new ArrayList<>(); List<CalendarEvent> topEvents = new ArrayList<>();
// ENDRET: Grensen er satt tilbake til 5
for(int i=0; i<Math.min(upcomingEvents.size(), 5); i++) { for(int i=0; i<Math.min(upcomingEvents.size(), 5); i++) {
top5.add(upcomingEvents.get(i)); topEvents.add(upcomingEvents.get(i));
} }
recyclerView.setAdapter(new CalendarAdapter(top5, event -> { recyclerView.setAdapter(new CalendarAdapter(topEvents, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event); CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(() -> fetchCalendarEvents(recyclerView)); sheet.setOnEventChangeListener(HomeFragment.this::refreshData);
sheet.show(getParentFragmentManager(), "CalendarDetails"); sheet.show(getParentFragmentManager(), "CalendarDetails");
})); }));
checkLoadingComplete();
} }
@Override @Override
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) { public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
if (!isAdded()) return; if (!isAdded()) return;
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
if (!deviceEvents.isEmpty()) { if (!deviceEvents.isEmpty()) {
List<CalendarEvent> top5 = new ArrayList<>(); List<CalendarEvent> topEvents = new ArrayList<>();
for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) top5.add(deviceEvents.get(i)); // ENDRET: Grensen er satt tilbake til 5 (Fallback)
recyclerView.setAdapter(new CalendarAdapter(top5, event -> { for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) topEvents.add(deviceEvents.get(i));
recyclerView.setAdapter(new CalendarAdapter(topEvents, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event); CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.show(getParentFragmentManager(), "CalendarDetails"); sheet.show(getParentFragmentManager(), "CalendarDetails");
})); }));
} else { } else {
recyclerView.setAdapter(new CalendarAdapter(new ArrayList<>(), null)); recyclerView.setAdapter(new CalendarAdapter(new ArrayList<>(), null));
} }
checkLoadingComplete();
} }
}); });
} }
@ -219,12 +246,14 @@ public class HomeFragment extends Fragment {
}); });
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
} }
checkLoadingComplete();
} }
@Override @Override
public void onFailure(Call<List<WpPost>> call, Throwable t) { public void onFailure(Call<List<WpPost>> call, Throwable t) {
if (getContext() == null) return; if (getContext() == null) return;
recyclerView.setAdapter(new NewsAdapter(new ArrayList<>(), null)); recyclerView.setAdapter(new NewsAdapter(new ArrayList<>(), null));
checkLoadingComplete();
} }
}); });
} }

View file

@ -5,9 +5,19 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/kbs_very_light_blue"> android:background="@color/kbs_very_light_blue">
<!-- SwipeRefreshLayout må pakke inn det skrollbare innholdet -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_home"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="8dp"> android:padding="8dp">
@ -79,11 +89,11 @@
android:background="?attr/selectableItemBackground"/> android:background="?attr/selectableItemBackground"/>
</LinearLayout> </LinearLayout>
<!-- ENDRET: Fast høyde for å skape "vindu" med scroll -->
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_calendar" android:id="@+id/recycler_calendar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="190dp"
android:layout_weight="1"
android:scrollbars="vertical" android:scrollbars="vertical"
android:layout_marginBottom="16dp"/> android:layout_marginBottom="16dp"/>
@ -120,13 +130,15 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_news" android:id="@+id/recycler_news"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:layout_weight="2" android:nestedScrollingEnabled="false"
android:scrollbars="vertical"/> android:scrollbars="vertical"/>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<!-- DENNE MANGLER SANNZYNLIGVIS HOS DEG NÅ: --> <!-- Initial Loader -->
<ProgressBar <ProgressBar
android:id="@+id/main_loading_spinner" android:id="@+id/main_loading_spinner"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/black"> android:background="@android:color/black">
@ -14,7 +16,8 @@
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_margin="16dp" android:layout_margin="16dp"
android:tint="@android:color/white" app:tint="@android:color/white"
android:contentDescription="Lukk bildevisning"
android:elevation="10dp"/> android:elevation="10dp"/>
<!-- Selve bildet --> <!-- Selve bildet -->
@ -24,7 +27,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:adjustViewBounds="true"/> android:adjustViewBounds="true"
android:contentDescription="Fullskjermbilde" />
<!-- Loading spinner --> <!-- Loading spinner -->
<ProgressBar <ProgressBar