Før feilsøking

This commit is contained in:
ErolHaagenrud 2026-01-08 20:59:07 +01:00
parent e89f7a3522
commit b7c34401ee
2 changed files with 160 additions and 177 deletions

View file

@ -122,7 +122,6 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
TaskViewHolder vh = (TaskViewHolder) holder;
vh.title.setText(task.getTitle());
// FIKS: Vis "Ingen frist" hvis dato er 0
if (task.getDueDate() > 0) {
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
vh.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
@ -145,6 +144,15 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
vh.date.setTextColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_muted_blue_gray));
}
// --- REINTRODUSERT FREMDRIFTSBEREGNING ---
int total = task.getAssigneeStatus().size();
int done = 0;
for (Boolean isFinished : task.getAssigneeStatus().values()) {
if (isFinished) done++;
}
vh.progress.setText("Fremdrift: " + done + "/" + total);
// ------------------------------------------
vh.checkBox.setOnClickListener(v -> listener.onTaskStatusChanged(task, vh.checkBox.isChecked()));
vh.itemView.setOnClickListener(v -> listener.onTaskItemClick(task));
}
@ -198,13 +206,14 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
}
static class TaskViewHolder extends RecyclerView.ViewHolder {
TextView title, date;
TextView title, date, progress;
CheckBox checkBox;
CardView cardView;
TaskViewHolder(View v) {
super(v);
title = v.findViewById(R.id.task_title);
date = v.findViewById(R.id.task_date);
progress = v.findViewById(R.id.task_progress); // Husk denne
checkBox = v.findViewById(R.id.task_checkbox);
cardView = (CardView) v;
}
@ -227,7 +236,6 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
EmptyViewHolder(View v) { super(v); }
}
// --- WRAPPER KLASSER ---
public static class CreateButtonItem {}
public static class EmptyTasksItem {}
public static class SectionTitleItem {

View file

@ -5,7 +5,6 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -20,7 +19,6 @@ import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.gson.JsonElement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
@ -30,10 +28,10 @@ import java.util.Locale;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import com.google.gson.JsonElement;
public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickListener {
private static final String TAG = "HomeFragment";
private RecyclerView recyclerView;
private HomeAdapter adapter;
private ProgressBar mainProgressBar;
@ -44,12 +42,12 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
private List<TaskItem> currentTasks = new ArrayList<>();
private int activeNetworkCalls = 0;
// Cache lever i 60 minutter. PUSH vil ugyldiggjøre den før tiden ved behov.
private static final int CACHE_TTL_MINUTES = 60;
private Handler timeoutHandler = new Handler(Looper.getMainLooper());
private Runnable timeoutRunnable = this::forceStopLoading;
private static final int CACHE_TTL_MINUTES = 60;
private ActivityResultLauncher<String> requestPermissionLauncher;
@Override
@ -77,8 +75,10 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
// Ved manuell swipe: Tving nettverksoppdatering
swipeRefreshLayout.setOnRefreshListener(() -> refreshData(true));
// Ved oppstart: Bruk cache (false = ikke tving nettverk)
refreshData(false);
}
@ -89,207 +89,174 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
}
private void refreshData(boolean forceNetwork) {
if (activeNetworkCalls > 0) return;
// 1. Last cache i bakgrunnen
new Thread(() -> {
if (getContext() == null) return;
boolean useCacheCalendar = !forceNetwork && CacheManager.isCacheValid(getContext(), "calendar", CACHE_TTL_MINUTES);
boolean useCacheNews = !forceNetwork && CacheManager.isCacheValid(getContext(), "news", CACHE_TTL_MINUTES);
boolean useCacheTasks = !forceNetwork && CacheManager.isCacheValid(getContext(), "tasks", CACHE_TTL_MINUTES);
// Hent data fra disk
List<CalendarEvent> cachedApiEvents = CacheManager.getCachedCalendarEvents(getContext());
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
// Hvis alt er i cache, last direkte
if (useCacheCalendar && useCacheNews && useCacheTasks) {
loadAllFromCache();
return;
for (CalendarEvent e : cachedApiEvents) CalendarManager.formatEventForUI(e);
List<CalendarEvent> mergedEvents = CalendarManager.mergeAndSort(cachedApiEvents, deviceEvents);
List<WpPost> cachedNews = CacheManager.getCachedNewsPosts(getContext());
formatNewsDates(cachedNews);
List<TaskItem> cachedTasks = CacheManager.getTasks(getContext());
// 2. Oppdater UI hovedtråden
new Handler(Looper.getMainLooper()).post(() -> {
if (!isAdded()) return;
// Oppdater variablene
currentEvents = mergedEvents;
currentNews = cachedNews;
currentTasks = cachedTasks;
// Vis dataene vi fant umiddelbart (fjerner spinner hvis den var der)
buildAndDisplayList();
// 3. Vurder om vi skal hente nytt fra nettet
boolean cacheValidCal = CacheManager.isCacheValid(getContext(), "calendar", CACHE_TTL_MINUTES);
boolean cacheValidNews = CacheManager.isCacheValid(getContext(), "news", CACHE_TTL_MINUTES);
boolean cacheValidTasks = CacheManager.isCacheValid(getContext(), "tasks", CACHE_TTL_MINUTES);
// Vi henter KUN hvis brukeren swiper (force) ELLER cachen er utgått dato.
// Vi henter IKKE bare fordi listene er tomme (da stoler vi at cachen er korrekt tom).
boolean needNetwork = forceNetwork || !cacheValidCal || !cacheValidNews || !cacheValidTasks;
if (needNetwork) {
// Vis spinner KUN hvis det er helt tomt fra før, eller ved manuell swipe
boolean isEverythingEmpty = currentNews.isEmpty() && currentTasks.isEmpty() && currentEvents.isEmpty();
if (isEverythingEmpty && mainProgressBar != null) {
mainProgressBar.setVisibility(View.VISIBLE);
} else if (forceNetwork && swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
// Ellers: Silent update (ingen spinner, men vi henter i bakgrunnen)
activeNetworkCalls = 3;
if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE);
timeoutHandler.removeCallbacks(timeoutRunnable);
timeoutHandler.postDelayed(timeoutRunnable, 10000);
timeoutHandler.postDelayed(timeoutRunnable, 10000); // 10 sek timeout
fetchCalendarData(useCacheCalendar);
fetchNewsData(useCacheNews);
fetchTaskData(useCacheTasks);
fetchCalendarData();
fetchNewsData();
fetchTaskData();
} else {
// Cachen er god nok! Skjul evt spinnere.
stopLoaders();
}
});
}).start();
}
private void stopLoaders() {
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
}
private void forceStopLoading() {
if (activeNetworkCalls > 0) {
activeNetworkCalls = 0;
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
stopLoaders();
buildAndDisplayList();
}
}
private void loadAllFromCache() {
if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE);
new Thread(() -> {
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
new Handler(Looper.getMainLooper()).post(() -> {
if (!isAdded()) return;
List<CalendarEvent> cachedApi = CacheManager.getCachedCalendarEvents(getContext());
for (CalendarEvent e : cachedApi) CalendarManager.formatEventForUI(e);
currentEvents = CalendarManager.mergeAndSort(cachedApi, deviceEvents);
currentNews = CacheManager.getCachedNewsPosts(getContext());
formatNewsDates(currentNews);
currentTasks = CacheManager.getTasks(getContext());
// FIKS: Hvis cache er tom, prøv nettverk likevel for å unngå tom skjerm
if (currentTasks.isEmpty() && !CacheManager.isCacheValid(getContext(), "tasks", CACHE_TTL_MINUTES)) {
fetchTaskData(false);
}
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
buildAndDisplayList();
});
}).start();
}
private void fetchCalendarData(boolean useCache) {
private void fetchCalendarData() {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(Manifest.permission.READ_CALENDAR);
currentEvents.clear();
checkLoadingComplete();
return;
}
new Thread(() -> {
try {
if (getContext() == null) {
checkLoadingCompleteAsync();
return;
}
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
new Handler(Looper.getMainLooper()).post(() -> {
if (!isAdded()) return;
if (useCache) {
List<CalendarEvent> cached = CacheManager.getCachedCalendarEvents(getContext());
for (CalendarEvent e : cached) CalendarManager.formatEventForUI(e);
currentEvents = CalendarManager.mergeAndSort(cached, deviceEvents);
checkLoadingComplete();
} else {
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
@Override
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
List<CalendarEvent> apiEvents = new ArrayList<>();
if (response.isSuccessful() && response.body() != null) {
apiEvents = response.body();
List<CalendarEvent> apiEvents = response.body();
CacheManager.saveCalendarEvents(getContext(), apiEvents);
} else {
apiEvents = CacheManager.getCachedCalendarEvents(getContext());
}
new Thread(() -> {
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
currentEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
new Handler(Looper.getMainLooper()).post(() -> {
currentEvents = merged;
checkLoadingComplete();
});
}).start();
} else {
checkLoadingComplete();
}
}
@Override
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
List<CalendarEvent> cached = CacheManager.getCachedCalendarEvents(getContext());
for (CalendarEvent e : cached) CalendarManager.formatEventForUI(e);
currentEvents = CalendarManager.mergeAndSort(cached, deviceEvents);
checkLoadingComplete();
}
});
}
});
} catch (Exception e) {
checkLoadingCompleteAsync();
}
}).start();
}
private void fetchNewsData(boolean useCache) {
if (useCache) {
currentNews = CacheManager.getCachedNewsPosts(getContext());
formatNewsDates(currentNews);
checkLoadingComplete();
} else {
private void fetchNewsData() {
RetrofitClient.getApiService().getPosts().enqueue(new Callback<List<WpPost>>() {
@Override
public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
if (response.isSuccessful() && response.body() != null) {
currentNews = response.body();
CacheManager.saveNewsPosts(getContext(), currentNews);
} else {
currentNews = CacheManager.getCachedNewsPosts(getContext());
}
formatNewsDates(currentNews);
}
checkLoadingComplete();
}
@Override
public void onFailure(Call<List<WpPost>> call, Throwable t) {
currentNews = CacheManager.getCachedNewsPosts(getContext());
formatNewsDates(currentNews);
checkLoadingComplete();
}
});
}
}
private void fetchTaskData(boolean useCache) {
// FIKS: Hvis cache er valgt, men listen er tom, hent fra nett!
if (useCache) {
currentTasks = CacheManager.getTasks(getContext());
if (currentTasks.isEmpty()) {
fetchTaskData(false); // Rekursivt kall men tvinger nettverk
return;
}
checkLoadingComplete();
} else {
private void fetchTaskData() {
RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
@Override
public void onResponse(Call<List<TaskItem>> call, Response<List<TaskItem>> response) {
if (response.isSuccessful() && response.body() != null) {
currentTasks = response.body();
CacheManager.saveTasks(getContext(), currentTasks);
} else {
currentTasks = CacheManager.getTasks(getContext());
}
checkLoadingComplete();
}
@Override
public void onFailure(Call<List<TaskItem>> call, Throwable t) {
currentTasks = CacheManager.getTasks(getContext());
checkLoadingComplete();
}
});
}
}
private void checkLoadingCompleteAsync() {
new Handler(Looper.getMainLooper()).post(this::checkLoadingComplete);
}
private void formatNewsDates(List<WpPost> posts) {
if (posts == null) return;
SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
for (WpPost post : posts) {
try {
if (post.date != null && post.date.contains("T")) {
Date date = rawFormat.parse(post.date);
post.date = targetFormat.format(date);
}
} catch (Exception ignored) {}
}
}
private synchronized void checkLoadingComplete() {
activeNetworkCalls--;
// Oppdater visningen fortløpende snart data kommer inn
buildAndDisplayList();
if (activeNetworkCalls <= 0) {
activeNetworkCalls = 0;
timeoutHandler.removeCallbacks(timeoutRunnable);
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
buildAndDisplayList();
stopLoaders();
}
}
@ -299,9 +266,11 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
List<Object> items = new ArrayList<>();
String myEmail = UserManager.getInstance().getUserEmail();
// 1. KALENDER
items.add(new HomeAdapter.SectionTitleItem("Kommende hendelser", HomeAdapter.SectionTitleItem.TYPE_CALENDAR));
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
int calCount = 0;
if (currentEvents != null) {
for (CalendarEvent e : currentEvents) {
if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) {
items.add(e);
@ -309,11 +278,14 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
}
if (calCount >= 3) break;
}
}
// 2. OPPGAVER
items.add(new HomeAdapter.SectionTitleItem("Mine oppgaver", HomeAdapter.SectionTitleItem.TYPE_TASKS));
List<TaskItem> myActiveTasks = new ArrayList<>();
if (currentTasks != null) {
for (TaskItem t : currentTasks) {
// Vis oppgaver jeg er deltaker i, som ikke er fullført av meg, og ikke helt lukket
if (t.isUserParticipant(myEmail) && !t.getParticipantStatus(myEmail) && !t.isFullyCompleted()) {
myActiveTasks.add(t);
}
@ -323,16 +295,16 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
if (myActiveTasks.isEmpty()) {
items.add(new HomeAdapter.EmptyTasksItem());
} else {
// NY SORTERING: Med frist først, uten frist (alfabetisk)
// Sortering: Dato (stigende), deretter de uten dato (alfabetisk)
Collections.sort(myActiveTasks, (t1, t2) -> {
boolean t1HasDate = t1.getDueDate() > 0;
boolean t2HasDate = t2.getDueDate() > 0;
if (t1HasDate && !t2HasDate) return -1; // t1 først
if (!t1HasDate && t2HasDate) return 1; // t2 først
if (!t1HasDate && !t2HasDate) return t1.getTitle().compareToIgnoreCase(t2.getTitle()); // Alfabetisk
if (t1HasDate && !t2HasDate) return -1;
if (!t1HasDate && t2HasDate) return 1;
if (!t1HasDate && !t2HasDate) return t1.getTitle().compareToIgnoreCase(t2.getTitle());
return Long.compare(t1.getDueDate(), t2.getDueDate()); // Dato
return Long.compare(t1.getDueDate(), t2.getDueDate());
});
for (int i = 0; i < Math.min(myActiveTasks.size(), 3); i++) {
@ -340,6 +312,7 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
}
}
// 3. NYHETER
items.add(new HomeAdapter.SectionTitleItem("Siste nytt", HomeAdapter.SectionTitleItem.TYPE_NEWS));
if (currentNews != null) {
items.addAll(currentNews);
@ -349,6 +322,8 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
recyclerView.setAdapter(adapter);
}
// --- NAVIGASJON & KLIKK ---
@Override public void onCreateEventClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_create_event); }
@Override public void onViewAllCalendarClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_calendar_full); }
@Override public void onViewAllNewsClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_news_full); }