diff --git a/app/src/main/java/com/kbs/kbsintranett/HomeAdapter.java b/app/src/main/java/com/kbs/kbsintranett/HomeAdapter.java index a458168..ee89b19 100644 --- a/app/src/main/java/com/kbs/kbsintranett/HomeAdapter.java +++ b/app/src/main/java/com/kbs/kbsintranett/HomeAdapter.java @@ -122,7 +122,6 @@ public class HomeAdapter extends RecyclerView.Adapter { 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 { 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 { } 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 { EmptyViewHolder(View v) { super(v); } } - // --- WRAPPER KLASSER --- public static class CreateButtonItem {} public static class EmptyTasksItem {} public static class SectionTitleItem { diff --git a/app/src/main/java/com/kbs/kbsintranett/HomeFragment.java b/app/src/main/java/com/kbs/kbsintranett/HomeFragment.java index 5948f8e..9781b32 100644 --- a/app/src/main/java/com/kbs/kbsintranett/HomeFragment.java +++ b/app/src/main/java/com/kbs/kbsintranett/HomeFragment.java @@ -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 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 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 cachedApiEvents = CacheManager.getCachedCalendarEvents(getContext()); + List 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 mergedEvents = CalendarManager.mergeAndSort(cachedApiEvents, deviceEvents); - activeNetworkCalls = 3; - if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE); + List cachedNews = CacheManager.getCachedNewsPosts(getContext()); + formatNewsDates(cachedNews); - timeoutHandler.removeCallbacks(timeoutRunnable); - timeoutHandler.postDelayed(timeoutRunnable, 10000); + List cachedTasks = CacheManager.getTasks(getContext()); - fetchCalendarData(useCacheCalendar); - fetchNewsData(useCacheNews); - fetchTaskData(useCacheTasks); + // 2. Oppdater UI på 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 på dato. + // Vi henter IKKE bare fordi listene er tomme (da stoler vi på 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; + timeoutHandler.removeCallbacks(timeoutRunnable); + timeoutHandler.postDelayed(timeoutRunnable, 10000); // 10 sek timeout + + 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 deviceEvents = CalendarManager.getDeviceEvents(getContext(), true); - new Handler(Looper.getMainLooper()).post(() -> { - if (!isAdded()) return; - - List 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; - } + RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful() && response.body() != null) { + List apiEvents = response.body(); + CacheManager.saveCalendarEvents(getContext(), apiEvents); - List deviceEvents = CalendarManager.getDeviceEvents(getContext(), true); - - new Handler(Looper.getMainLooper()).post(() -> { - if (!isAdded()) return; - - if (useCache) { - List cached = CacheManager.getCachedCalendarEvents(getContext()); - for (CalendarEvent e : cached) CalendarManager.formatEventForUI(e); - currentEvents = CalendarManager.mergeAndSort(cached, deviceEvents); - checkLoadingComplete(); - } else { - RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - List apiEvents = new ArrayList<>(); - if (response.isSuccessful() && response.body() != null) { - apiEvents = response.body(); - CacheManager.saveCalendarEvents(getContext(), apiEvents); - } else { - apiEvents = CacheManager.getCachedCalendarEvents(getContext()); - } - for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e); - currentEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents); - checkLoadingComplete(); - } - @Override - public void onFailure(Call> call, Throwable t) { - List cached = CacheManager.getCachedCalendarEvents(getContext()); - for (CalendarEvent e : cached) CalendarManager.formatEventForUI(e); - currentEvents = CalendarManager.mergeAndSort(cached, deviceEvents); - checkLoadingComplete(); - } + new Thread(() -> { + List deviceEvents = CalendarManager.getDeviceEvents(getContext(), true); + for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e); + List merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents); + new Handler(Looper.getMainLooper()).post(() -> { + currentEvents = merged; + checkLoadingComplete(); }); - } - }); - } catch (Exception e) { - checkLoadingCompleteAsync(); + }).start(); + } else { + checkLoadingComplete(); + } } - }).start(); - } - - private void fetchNewsData(boolean useCache) { - if (useCache) { - currentNews = CacheManager.getCachedNewsPosts(getContext()); - formatNewsDates(currentNews); - checkLoadingComplete(); - } else { - RetrofitClient.getApiService().getPosts().enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> 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> 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; + @Override + public void onFailure(Call> call, Throwable t) { + checkLoadingComplete(); } - checkLoadingComplete(); - } else { - RetrofitClient.getApiService().getTasks().enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> 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> call, Throwable t) { - currentTasks = CacheManager.getTasks(getContext()); - checkLoadingComplete(); - } - }); - } + }); } - private void checkLoadingCompleteAsync() { - new Handler(Looper.getMainLooper()).post(this::checkLoadingComplete); + private void fetchNewsData() { + RetrofitClient.getApiService().getPosts().enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful() && response.body() != null) { + currentNews = response.body(); + CacheManager.saveNewsPosts(getContext(), currentNews); + formatNewsDates(currentNews); + } + checkLoadingComplete(); + } + @Override + public void onFailure(Call> call, Throwable t) { + checkLoadingComplete(); + } + }); + } + + private void fetchTaskData() { + RetrofitClient.getApiService().getTasks().enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful() && response.body() != null) { + currentTasks = response.body(); + CacheManager.saveTasks(getContext(), currentTasks); + } + checkLoadingComplete(); + } + @Override + public void onFailure(Call> call, Throwable t) { + checkLoadingComplete(); + } + }); } private void formatNewsDates(List 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 { - Date date = rawFormat.parse(post.date); - post.date = targetFormat.format(date); + 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 så 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,21 +266,26 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis List 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; - for (CalendarEvent e : currentEvents) { - if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) { - items.add(e); - calCount++; + if (currentEvents != null) { + for (CalendarEvent e : currentEvents) { + if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) { + items.add(e); + calCount++; + } + if (calCount >= 3) break; } - if (calCount >= 3) break; } + // 2. OPPGAVER items.add(new HomeAdapter.SectionTitleItem("Mine oppgaver", HomeAdapter.SectionTitleItem.TYPE_TASKS)); List 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, så 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); }