Før sentralisert brukerliste

This commit is contained in:
ErolHaagenrud 2026-01-08 11:32:48 +01:00
parent 974c41ca4d
commit ee198150c2
18 changed files with 684 additions and 337 deletions

View file

@ -2,22 +2,27 @@ package com.kbs.kbsintranett;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.DatePickerDialog; import android.app.DatePickerDialog;
import android.app.Dialog;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
@ -26,15 +31,18 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
private EditText etTitle, etDesc; private EditText etTitle, etDesc;
private Button btnDate, btnUsers, btnSave; private Button btnDate, btnUsers, btnSave;
private TextView txtDatePreview, txtUsersPreview; private TextView txtSheetTitle, txtDatePreview, txtUsersPreview;
private Calendar dueDate = Calendar.getInstance(); private Calendar dueDate = Calendar.getInstance();
private List<User> allUsersFromApi = new ArrayList<>(); private List<User> allUsersFromApi = new ArrayList<>();
private List<User> filteredUsers = new ArrayList<>(); private List<User> filteredUsers = new ArrayList<>();
private List<User> selectedUsers = new ArrayList<>(); private List<User> selectedUsers = new ArrayList<>();
private TaskItem taskToEdit = null; // NYTT: Hold oppgaven som skal endres
public interface OnTaskAddedListener { public interface OnTaskAddedListener {
void onTaskAdded(TaskItem task); void onTaskAdded(TaskItem task);
void onTaskUpdated(TaskItem task); // NYTT
} }
private OnTaskAddedListener listener; private OnTaskAddedListener listener;
@ -43,11 +51,32 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
this.listener = listener; this.listener = listener;
} }
// NYTT: Metode for å sette oppgaven som skal redigeres
public void setTaskToEdit(TaskItem task) {
this.taskToEdit = task;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
dialog.setOnShowListener(dialogInterface -> {
BottomSheetDialog d = (BottomSheetDialog) dialogInterface;
View bottomSheet = d.findViewById(com.google.android.material.R.id.design_bottom_sheet);
if (bottomSheet != null) {
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
return dialog;
}
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bottom_sheet_add_task, container, false); View v = inflater.inflate(R.layout.bottom_sheet_add_task, container, false);
txtSheetTitle = v.findViewById(R.id.txt_sheet_title); // Pass at denne IDen finnes i XML
etTitle = v.findViewById(R.id.et_task_title); etTitle = v.findViewById(R.id.et_task_title);
etDesc = v.findViewById(R.id.et_task_desc); etDesc = v.findViewById(R.id.et_task_desc);
btnDate = v.findViewById(R.id.btn_task_date); btnDate = v.findViewById(R.id.btn_task_date);
@ -56,7 +85,16 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
txtDatePreview = v.findViewById(R.id.txt_date_preview); txtDatePreview = v.findViewById(R.id.txt_date_preview);
txtUsersPreview = v.findViewById(R.id.txt_users_preview); txtUsersPreview = v.findViewById(R.id.txt_users_preview);
dueDate.add(Calendar.DAY_OF_MONTH, 1); if (taskToEdit != null) {
txtSheetTitle.setText("Rediger Oppgave");
etTitle.setText(taskToEdit.getTitle());
etDesc.setText(taskToEdit.getDescription());
dueDate.setTimeInMillis(taskToEdit.getDueDate());
btnSave.setText("Oppdater Oppgave");
} else {
dueDate.add(Calendar.DAY_OF_MONTH, 1);
}
updateDatePreview(); updateDatePreview();
btnDate.setOnClickListener(view -> { btnDate.setOnClickListener(view -> {
@ -81,6 +119,18 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
if (response.isSuccessful() && response.body() != null) { if (response.isSuccessful() && response.body() != null) {
allUsersFromApi = response.body(); allUsersFromApi = response.body();
filterUsersByHierarchy(); filterUsersByHierarchy();
// Hvis vi redigerer, vi mappe eksisterende deltakere til selectedUsers listen
if (taskToEdit != null) {
selectedUsers.clear();
Map<String, Boolean> currentAssignees = taskToEdit.getAssigneeStatus();
for (User u : allUsersFromApi) {
if (currentAssignees.containsKey(u.getEmail())) {
selectedUsers.add(u);
}
}
updateUsersPreview();
}
} }
} }
@Override @Override
@ -91,34 +141,30 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
private void filterUsersByHierarchy() { private void filterUsersByHierarchy() {
filteredUsers.clear(); filteredUsers.clear();
UserManager me = UserManager.getInstance(); UserManager me = UserManager.getInstance();
// 1. Hvis Admin/Editor, legg til alle
if (me.isEditorOrAbove()) {
filteredUsers.addAll(allUsersFromApi);
return;
}
// 2. Finn mine roller/avdelinger
List<String> myRoles = new ArrayList<>(); List<String> myRoles = new ArrayList<>();
User self = null;
for (User u : allUsersFromApi) { for (User u : allUsersFromApi) {
if (u.getEmail().equalsIgnoreCase(me.getUserEmail())) { if (u.getEmail().equalsIgnoreCase(me.getUserEmail())) {
self = u;
for (String r : u.getRoles()) myRoles.add(r.toLowerCase()); for (String r : u.getRoles()) myRoles.add(r.toLowerCase());
break; break;
} }
} }
// 3. Filtrer logikk (Identisk med CreateEventFragment) if (me.isEditorOrAbove()) {
filteredUsers.addAll(allUsersFromApi);
return;
}
for (User u : allUsersFromApi) { for (User u : allUsersFromApi) {
// Alltid legg til seg selv
if (u.getEmail().equalsIgnoreCase(me.getUserEmail())) { if (u.getEmail().equalsIgnoreCase(me.getUserEmail())) {
if (!filteredUsers.contains(u)) filteredUsers.add(u); if (!filteredUsers.contains(u)) filteredUsers.add(u);
continue; continue;
} }
boolean hasAccess = false; boolean hasAccess = false;
for (String role : u.getRoles()) { for (String role : u.getRoles()) {
String r = role.toLowerCase(); String r = role.toLowerCase();
// Sjekk om vi deler en avdelingsrolle
if ((r.equals("serviceavdelingen") && myRoles.contains("serviceavdelingen")) || if ((r.equals("serviceavdelingen") && myRoles.contains("serviceavdelingen")) ||
(r.equals("automasjonsavdelingen") && myRoles.contains("automasjonsavdelingen")) || (r.equals("automasjonsavdelingen") && myRoles.contains("automasjonsavdelingen")) ||
(r.equals("prosjektavdelingen") && myRoles.contains("prosjektavdelingen")) || (r.equals("prosjektavdelingen") && myRoles.contains("prosjektavdelingen")) ||
@ -128,10 +174,7 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
break; break;
} }
} }
if (hasAccess && !filteredUsers.contains(u)) filteredUsers.add(u);
if (hasAccess && !filteredUsers.contains(u)) {
filteredUsers.add(u);
}
} }
} }
@ -140,28 +183,44 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
Toast.makeText(getContext(), "Henter tilgjengelige personer...", Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Henter tilgjengelige personer...", Toast.LENGTH_SHORT).show();
return; return;
} }
String[] names = new String[filteredUsers.size()]; String[] names = new String[filteredUsers.size()];
boolean[] checked = new boolean[filteredUsers.size()]; boolean[] checked = new boolean[filteredUsers.size()];
for (int i = 0; i < filteredUsers.size(); i++) { for (int i = 0; i < filteredUsers.size(); i++) {
names[i] = filteredUsers.get(i).getName(); names[i] = filteredUsers.get(i).getName();
checked[i] = selectedUsers.contains(filteredUsers.get(i)); // Sjekk om e-posten finnes i de valgte brukernes liste
boolean isSelected = false;
for(User su : selectedUsers) {
if(su.getEmail().equalsIgnoreCase(filteredUsers.get(i).getEmail())) {
isSelected = true;
break;
}
}
checked[i] = isSelected;
} }
new AlertDialog.Builder(getContext()) new AlertDialog.Builder(getContext())
.setTitle("Tildel til...") .setTitle("Tildel til...")
.setMultiChoiceItems(names, checked, (dialog, which, isChecked) -> { .setMultiChoiceItems(names, checked, (dialog, which, isChecked) -> {
if (isChecked) selectedUsers.add(filteredUsers.get(which)); User user = filteredUsers.get(which);
else selectedUsers.remove(filteredUsers.get(which)); if (isChecked) {
}) boolean alreadyIn = false;
.setPositiveButton("OK", (dialog, which) -> { for(User su : selectedUsers) if(su.getEmail().equalsIgnoreCase(user.getEmail())) alreadyIn = true;
if (selectedUsers.isEmpty()) txtUsersPreview.setText("Kun meg"); if(!alreadyIn) selectedUsers.add(user);
else if (selectedUsers.size() == 1) txtUsersPreview.setText(selectedUsers.get(0).getName()); } else {
else txtUsersPreview.setText(selectedUsers.size() + " personer valgt"); User toRemove = null;
for(User su : selectedUsers) if(su.getEmail().equalsIgnoreCase(user.getEmail())) toRemove = su;
if(toRemove != null) selectedUsers.remove(toRemove);
}
}) })
.setPositiveButton("OK", (dialog, which) -> updateUsersPreview())
.show(); .show();
} }
private void updateUsersPreview() {
if (selectedUsers.isEmpty()) txtUsersPreview.setText("Kun meg");
else if (selectedUsers.size() == 1) txtUsersPreview.setText(selectedUsers.get(0).getName());
else txtUsersPreview.setText(selectedUsers.size() + " personer valgt");
}
private void updateDatePreview() { private void updateDatePreview() {
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()); SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
txtDatePreview.setText("Frist: " + sdf.format(dueDate.getTime())); txtDatePreview.setText("Frist: " + sdf.format(dueDate.getTime()));
@ -174,14 +233,64 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
return; return;
} }
TaskItem task = new TaskItem(title, etDesc.getText().toString(), dueDate.getTimeInMillis()); if (taskToEdit != null) {
if (selectedUsers.isEmpty()) { // REDIGER MODUS: Oppdater eksisterende objekt
task.addAssignee(UserManager.getInstance().getUserEmail()); // Vi beholde ID, Creator osv, men oppdatere innhold og deltakere
} else { // OBS: Hvis vi fjerner deltakere som allerede hadde gjort oppgaven, forsvinner deres status.
for (User u : selectedUsers) task.addAssignee(u.getEmail()); // Dette er akseptabel oppførsel for enkelthets skyld.
}
if (listener != null) listener.onTaskAdded(task); // Lag en kopi av gamle statuser for å bevare "Fullført" for de som fortsatt er med
Map<String, Boolean> oldStatus = taskToEdit.getAssigneeStatus();
taskToEdit.getAssigneeStatus().clear();
if (selectedUsers.isEmpty()) {
String myEmail = UserManager.getInstance().getUserEmail();
taskToEdit.addAssignee(myEmail);
if (oldStatus.containsKey(myEmail)) taskToEdit.setParticipantStatus(myEmail, oldStatus.get(myEmail));
} else {
for (User u : selectedUsers) {
taskToEdit.addAssignee(u.getEmail());
if (oldStatus.containsKey(u.getEmail())) {
taskToEdit.setParticipantStatus(u.getEmail(), oldStatus.get(u.getEmail()));
}
}
}
// Sjekk om tittelen eller beskrivelsen faktisk har endret seg for å sette titlene nytt
// (Vi bruker reflection her eller bare setter verdiene direkte siden de er private i TaskItem)
// Siden jeg ikke endret TaskItem til å ha public settlere, antar jeg at du legger til disse
// eller at vi bare lager en ny TaskItem med samme ID.
// Vi simulerer oppdatering av feltene (Sørg for at TaskItem har disse setterene!)
// Hvis du ikke har setttere, legg dem til i TaskItem.java:
// public void setTitle(String t) { this.title = t; }
// public void setDescription(String d) { this.description = d; }
// public void setDueDate(long d) { this.dueDate = d; }
// Jeg skriver logikken slik at den forventer setttere:
updateTaskFields(taskToEdit, title, etDesc.getText().toString(), dueDate.getTimeInMillis());
if (listener != null) listener.onTaskUpdated(taskToEdit);
} else {
// NY OPPGAVE MODUS
TaskItem newTask = new TaskItem(title, etDesc.getText().toString(), dueDate.getTimeInMillis());
if (selectedUsers.isEmpty()) {
newTask.addAssignee(UserManager.getInstance().getUserEmail());
} else {
for (User u : selectedUsers) newTask.addAssignee(u.getEmail());
}
if (listener != null) listener.onTaskAdded(newTask);
}
dismiss(); dismiss();
} }
// Hjelpemetode (forutsetter at du legger til disse tre setterne i TaskItem.java)
private void updateTaskFields(TaskItem t, String title, String desc, long date) {
// Her kaller vi setterne vi legger til i TaskItem
// Se punkt 5 nedenfor for oppdatert TaskItem.java
t.setTitle(title);
t.setDescription(desc);
t.setDueDate(date);
}
} }

View file

@ -6,7 +6,6 @@ import android.os.Looper;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -15,6 +14,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 com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import retrofit2.Call; import retrofit2.Call;
@ -26,7 +26,7 @@ public class CalendarFullFragment extends Fragment {
private RecyclerView recyclerView; private RecyclerView recyclerView;
private ProgressBar progressBar; private ProgressBar progressBar;
private TextView emptyView; private TextView emptyView;
private Button btnAddEvent; private FloatingActionButton fabAddEvent;
private LinearLayoutManager layoutManager; private LinearLayoutManager layoutManager;
@Nullable @Nullable
@ -41,16 +41,14 @@ public class CalendarFullFragment extends Fragment {
recyclerView = view.findViewById(R.id.recycler_full_calendar); recyclerView = view.findViewById(R.id.recycler_full_calendar);
progressBar = view.findViewById(R.id.loading_full_calendar); progressBar = view.findViewById(R.id.loading_full_calendar);
emptyView = view.findViewById(R.id.empty_view_calendar); emptyView = view.findViewById(R.id.empty_view_calendar);
btnAddEvent = view.findViewById(R.id.btn_add_calendar_event); fabAddEvent = view.findViewById(R.id.fab_add_calendar_event);
layoutManager = new LinearLayoutManager(getContext()); layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
// VIKTIG: Ingen referanse til btn_back_calendar her lenger.
if (!UserManager.getInstance().getWriteableCalendars().isEmpty() || UserManager.getInstance().isEditorOrAbove()) { if (!UserManager.getInstance().getWriteableCalendars().isEmpty() || UserManager.getInstance().isEditorOrAbove()) {
btnAddEvent.setVisibility(View.VISIBLE); fabAddEvent.setVisibility(View.VISIBLE);
btnAddEvent.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.navigation_create_event)); fabAddEvent.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.navigation_create_event));
} }
} }

View file

@ -2,18 +2,25 @@ package com.kbs.kbsintranett;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@ -21,17 +28,21 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final int TYPE_SECTION_TITLE = 2; public static final int TYPE_SECTION_TITLE = 2;
public static final int TYPE_CALENDAR_ITEM = 3; public static final int TYPE_CALENDAR_ITEM = 3;
public static final int TYPE_NEWS_ITEM = 4; public static final int TYPE_NEWS_ITEM = 4;
public static final int TYPE_TASK_ITEM = 5;
public static final int TYPE_EMPTY_TASKS = 6;
private final List<Object> items; private final List<Object> items;
private final OnHomeClickListener listener; private final OnHomeClickListener listener;
public interface OnHomeClickListener { public interface OnHomeClickListener {
void onProfileClick();
void onCreateEventClick(); void onCreateEventClick();
void onViewAllCalendarClick(); void onViewAllCalendarClick();
void onViewAllNewsClick(); void onViewAllNewsClick();
void onViewAllTasksClick();
void onCalendarItemClick(CalendarEvent event); void onCalendarItemClick(CalendarEvent event);
void onNewsItemClick(WpPost post); void onNewsItemClick(WpPost post);
void onTaskItemClick(TaskItem task);
void onTaskStatusChanged(TaskItem task, boolean isDone);
} }
public HomeAdapter(List<Object> items, OnHomeClickListener listener) { public HomeAdapter(List<Object> items, OnHomeClickListener listener) {
@ -46,6 +57,8 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
if (item instanceof SectionTitleItem) return TYPE_SECTION_TITLE; if (item instanceof SectionTitleItem) return TYPE_SECTION_TITLE;
if (item instanceof CalendarEvent) return TYPE_CALENDAR_ITEM; if (item instanceof CalendarEvent) return TYPE_CALENDAR_ITEM;
if (item instanceof WpPost) return TYPE_NEWS_ITEM; if (item instanceof WpPost) return TYPE_NEWS_ITEM;
if (item instanceof TaskItem) return TYPE_TASK_ITEM;
if (item instanceof EmptyTasksItem) return TYPE_EMPTY_TASKS;
return -1; return -1;
} }
@ -60,6 +73,10 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
return new SectionTitleViewHolder(inflater.inflate(R.layout.item_home_section_title, parent, false)); return new SectionTitleViewHolder(inflater.inflate(R.layout.item_home_section_title, parent, false));
case TYPE_CALENDAR_ITEM: case TYPE_CALENDAR_ITEM:
return new CalendarViewHolder(inflater.inflate(R.layout.item_calendar, parent, false)); return new CalendarViewHolder(inflater.inflate(R.layout.item_calendar, parent, false));
case TYPE_TASK_ITEM:
return new TaskViewHolder(inflater.inflate(R.layout.item_task, parent, false));
case TYPE_EMPTY_TASKS:
return new EmptyViewHolder(inflater.inflate(R.layout.item_home_empty_tasks, parent, false));
case TYPE_NEWS_ITEM: case TYPE_NEWS_ITEM:
return new NewsViewHolder(inflater.inflate(R.layout.item_news, parent, false)); return new NewsViewHolder(inflater.inflate(R.layout.item_news, parent, false));
default: default:
@ -79,7 +96,8 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
SectionTitleViewHolder vh = (SectionTitleViewHolder) holder; SectionTitleViewHolder vh = (SectionTitleViewHolder) holder;
vh.title.setText(section.title); vh.title.setText(section.title);
vh.btnViewAll.setOnClickListener(v -> { vh.btnViewAll.setOnClickListener(v -> {
if (section.isCalendar) listener.onViewAllCalendarClick(); if (section.type == SectionTitleItem.TYPE_CALENDAR) listener.onViewAllCalendarClick();
else if (section.type == SectionTitleItem.TYPE_TASKS) listener.onViewAllTasksClick();
else listener.onViewAllNewsClick(); else listener.onViewAllNewsClick();
}); });
} }
@ -90,28 +108,49 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
vh.month.setText(event.getMonth()); vh.month.setText(event.getMonth());
vh.time.setText(event.getTime()); vh.time.setText(event.getTime());
vh.title.setText(event.getTitle()); vh.title.setText(event.getTitle());
boolean isPrivate = event.getDescription() != null && event.getDescription().contains("#deltakere:"); boolean isPrivate = event.getDescription() != null && event.getDescription().contains("#deltakere:");
int color;
try { try {
color = Color.parseColor(isPrivate ? "#673AB7" : event.getCalendarColor()); int color = Color.parseColor(isPrivate ? "#673AB7" : event.getCalendarColor());
vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(color));
} catch (Exception e) { } catch (Exception e) {
color = Color.parseColor("#0069B3"); vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#0069B3")));
} }
vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(color));
vh.itemView.setOnClickListener(v -> listener.onCalendarItemClick(event)); vh.itemView.setOnClickListener(v -> listener.onCalendarItemClick(event));
} }
else if (holder instanceof TaskViewHolder) {
TaskItem task = (TaskItem) item;
TaskViewHolder vh = (TaskViewHolder) holder;
vh.title.setText(task.getTitle());
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
vh.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
String myEmail = UserManager.getInstance().getUserEmail();
boolean myStatus = task.getParticipantStatus(myEmail);
vh.checkBox.setChecked(myStatus);
long now = System.currentTimeMillis();
boolean isOverdue = task.getDueDate() < now && !task.isFullyCompleted() && !myStatus;
if (isOverdue) {
vh.cardView.setCardBackgroundColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_soft_light_pink_beige));
vh.date.setTextColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_logo_accent_red));
} else {
vh.cardView.setCardBackgroundColor(Color.WHITE);
vh.date.setTextColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_muted_blue_gray));
}
vh.checkBox.setOnClickListener(v -> listener.onTaskStatusChanged(task, vh.checkBox.isChecked()));
vh.itemView.setOnClickListener(v -> listener.onTaskItemClick(task));
}
else if (holder instanceof NewsViewHolder) { else if (holder instanceof NewsViewHolder) {
WpPost post = (WpPost) item; WpPost post = (WpPost) item;
NewsViewHolder vh = (NewsViewHolder) holder; NewsViewHolder vh = (NewsViewHolder) holder;
vh.title.setText(post.getTitleStr()); vh.title.setText(post.getTitleStr());
vh.excerpt.setText(post.getExcerptStr()); vh.excerpt.setText(post.getExcerptStr());
vh.date.setText(post.date); vh.date.setText(post.date);
String cat = post.getCategoryName(); String cat = post.getCategoryName();
vh.category.setText(cat); vh.category.setText(cat);
vh.category.setVisibility(cat.isEmpty() ? View.GONE : View.VISIBLE); vh.category.setVisibility(cat.isEmpty() ? View.GONE : View.VISIBLE);
String imgUrl = post.getFeaturedImageUrl(); String imgUrl = post.getFeaturedImageUrl();
if (imgUrl != null) { if (imgUrl != null) {
vh.image.setVisibility(View.VISIBLE); vh.image.setVisibility(View.VISIBLE);
@ -129,7 +168,6 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
} }
// --- VIEW HOLDERS --- // --- VIEW HOLDERS ---
static class CreateButtonViewHolder extends RecyclerView.ViewHolder { static class CreateButtonViewHolder extends RecyclerView.ViewHolder {
Button btnCreate; Button btnCreate;
CreateButtonViewHolder(View v) { super(v); btnCreate = v.findViewById(R.id.btn_create_event); } CreateButtonViewHolder(View v) { super(v); btnCreate = v.findViewById(R.id.btn_create_event); }
@ -143,13 +181,26 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
static class CalendarViewHolder extends RecyclerView.ViewHolder { static class CalendarViewHolder extends RecyclerView.ViewHolder {
TextView day, month, title, time; TextView day, month, title, time;
LinearLayout dateBox; LinearLayout dateBox;
CalendarViewHolder(View v) { CalendarViewHolder(View view) {
super(view);
day = view.findViewById(R.id.cal_day);
month = view.findViewById(R.id.cal_month);
title = view.findViewById(R.id.cal_title);
time = view.findViewById(R.id.cal_time);
dateBox = view.findViewById(R.id.date_box_background);
}
}
static class TaskViewHolder extends RecyclerView.ViewHolder {
TextView title, date;
CheckBox checkBox;
CardView cardView;
TaskViewHolder(View v) {
super(v); super(v);
day = v.findViewById(R.id.cal_day); title = v.findViewById(R.id.task_title);
month = v.findViewById(R.id.cal_month); date = v.findViewById(R.id.task_date);
title = v.findViewById(R.id.cal_title); checkBox = v.findViewById(R.id.task_checkbox);
time = v.findViewById(R.id.cal_time); cardView = (CardView) v;
dateBox = v.findViewById(R.id.date_box_background);
} }
} }
@ -166,10 +217,18 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
} }
} }
static class EmptyViewHolder extends RecyclerView.ViewHolder {
EmptyViewHolder(View v) { super(v); }
}
// --- WRAPPER KLASSER --- // --- WRAPPER KLASSER ---
public static class CreateButtonItem {} public static class CreateButtonItem {}
public static class EmptyTasksItem {}
public static class SectionTitleItem { public static class SectionTitleItem {
String title; boolean isCalendar; public static final int TYPE_CALENDAR = 0;
public SectionTitleItem(String t, boolean c) { this.title = t; this.isCalendar = c; } public static final int TYPE_NEWS = 1;
public static final int TYPE_TASKS = 2;
String title; int type;
public SectionTitleItem(String t, int type) { this.title = t; this.type = type; }
} }
} }

View file

@ -9,6 +9,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts; import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -19,8 +20,10 @@ 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.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.gson.JsonElement;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -37,6 +40,7 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
private List<CalendarEvent> currentEvents = new ArrayList<>(); private List<CalendarEvent> currentEvents = new ArrayList<>();
private List<WpPost> currentNews = new ArrayList<>(); private List<WpPost> currentNews = new ArrayList<>();
private List<TaskItem> currentTasks = new ArrayList<>();
private int activeNetworkCalls = 0; private int activeNetworkCalls = 0;
private ActivityResultLauncher<String> requestPermissionLauncher; private ActivityResultLauncher<String> requestPermissionLauncher;
@ -67,22 +71,17 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
swipeRefreshLayout.setOnRefreshListener(this::refreshData); swipeRefreshLayout.setOnRefreshListener(this::refreshData);
if (android.os.Build.VERSION.SDK_INT >= 33) {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
}
}
refreshData(); refreshData();
} }
private void refreshData() { private void refreshData() {
if (activeNetworkCalls > 0) return; if (activeNetworkCalls > 0) return;
activeNetworkCalls = 2; activeNetworkCalls = 3;
if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE); if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE);
fetchCalendarData(); fetchCalendarData();
fetchNewsData(); fetchNewsData();
fetchTaskData();
} }
private void fetchCalendarData() { private void fetchCalendarData() {
@ -106,12 +105,10 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
} else { } else {
apiEvents = CacheManager.getCachedCalendarEvents(getContext()); apiEvents = CacheManager.getCachedCalendarEvents(getContext());
} }
for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e); for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
currentEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents); currentEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
checkLoadingComplete(); checkLoadingComplete();
} }
@Override @Override
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) { public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
List<CalendarEvent> cached = CacheManager.getCachedCalendarEvents(getContext()); List<CalendarEvent> cached = CacheManager.getCachedCalendarEvents(getContext());
@ -137,7 +134,6 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
formatNewsDates(currentNews); formatNewsDates(currentNews);
checkLoadingComplete(); checkLoadingComplete();
} }
@Override @Override
public void onFailure(Call<List<WpPost>> call, Throwable t) { public void onFailure(Call<List<WpPost>> call, Throwable t) {
currentNews = CacheManager.getCachedNewsPosts(getContext()); currentNews = CacheManager.getCachedNewsPosts(getContext());
@ -147,6 +143,26 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
}); });
} }
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 formatNewsDates(List<WpPost> posts) { private void formatNewsDates(List<WpPost> posts) {
SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()); SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault()); SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
@ -170,45 +186,97 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
private void buildAndDisplayList() { private void buildAndDisplayList() {
List<Object> items = new ArrayList<>(); List<Object> items = new ArrayList<>();
String myEmail = UserManager.getInstance().getUserEmail();
// 1. Kalender Seksjon (Knappen "Ny hendelse" er fjernet herfra) items.add(new HomeAdapter.SectionTitleItem("Kommende hendelser", HomeAdapter.SectionTitleItem.TYPE_CALENDAR));
items.add(new HomeAdapter.SectionTitleItem("Kommende hendelser", true));
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date()); String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
int count = 0; int calCount = 0;
for (CalendarEvent e : currentEvents) { for (CalendarEvent e : currentEvents) {
if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) { if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) {
items.add(e); items.add(e);
count++; calCount++;
} }
if (count >= 3) break; if (calCount >= 3) break;
} }
// 2. Nyheter Seksjon items.add(new HomeAdapter.SectionTitleItem("Mine oppgaver", HomeAdapter.SectionTitleItem.TYPE_TASKS));
items.add(new HomeAdapter.SectionTitleItem("Siste nytt", false)); List<TaskItem> myActiveTasks = new ArrayList<>();
for (TaskItem t : currentTasks) {
if (t.isUserParticipant(myEmail) && !t.getParticipantStatus(myEmail) && !t.isFullyCompleted()) {
myActiveTasks.add(t);
}
}
if (myActiveTasks.isEmpty()) {
items.add(new HomeAdapter.EmptyTasksItem());
} else {
Collections.sort(myActiveTasks, (t1, t2) -> Long.compare(t1.getDueDate(), t2.getDueDate()));
for (int i = 0; i < Math.min(myActiveTasks.size(), 3); i++) {
items.add(myActiveTasks.get(i));
}
}
items.add(new HomeAdapter.SectionTitleItem("Siste nytt", HomeAdapter.SectionTitleItem.TYPE_NEWS));
items.addAll(currentNews); items.addAll(currentNews);
adapter = new HomeAdapter(items, this); adapter = new HomeAdapter(items, this);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
} }
@Override public void onProfileClick() {} @Override public void onCreateEventClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_create_event); }
@Override public void onCreateEventClick() { @Override public void onViewAllCalendarClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_calendar_full); }
Navigation.findNavController(getView()).navigate(R.id.navigation_create_event); @Override public void onViewAllNewsClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_news_full); }
} @Override public void onViewAllTasksClick() { Navigation.findNavController(getView()).navigate(R.id.navigation_tasks); }
@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);
}
@Override public void onCalendarItemClick(CalendarEvent event) { @Override public void onCalendarItemClick(CalendarEvent event) {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event); CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(this::refreshData); sheet.setOnEventChangeListener(this::refreshData);
sheet.show(getParentFragmentManager(), "CalendarDetails"); sheet.show(getParentFragmentManager(), "CalendarDetails");
} }
@Override public void onNewsItemClick(WpPost post) { @Override public void onNewsItemClick(WpPost post) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable("post_data", post); bundle.putSerializable("post_data", post);
Navigation.findNavController(getView()).navigate(R.id.navigation_news_detail, bundle); Navigation.findNavController(getView()).navigate(R.id.navigation_news_detail, bundle);
} }
@Override public void onTaskItemClick(TaskItem task) {
TaskDetailsBottomSheet sheet = new TaskDetailsBottomSheet(task, new TaskDetailsBottomSheet.OnTaskChangeListener() {
@Override public void onTaskChanged() { saveAndSyncTasks(); }
@Override public void onTaskDeleted(TaskItem taskToDelete) {
currentTasks.remove(taskToDelete);
saveAndSyncTasks();
}
@Override public void onEditRequested(TaskItem taskToEdit) {
AddTaskBottomSheet editDialog = new AddTaskBottomSheet();
editDialog.setTaskToEdit(taskToEdit);
editDialog.setOnTaskAddedListener(new AddTaskBottomSheet.OnTaskAddedListener() {
@Override public void onTaskAdded(TaskItem task) {}
@Override public void onTaskUpdated(TaskItem task) {
saveAndSyncTasks();
}
});
editDialog.show(getChildFragmentManager(), "EditTask");
}
});
sheet.show(getChildFragmentManager(), "TaskDetails");
}
@Override public void onTaskStatusChanged(TaskItem task, boolean isDone) {
String myEmail = UserManager.getInstance().getUserEmail();
task.setParticipantStatus(myEmail, isDone);
if (task.getCreatedByEmail().equalsIgnoreCase(myEmail) && isDone) {
task.setFullyCompleted(true);
}
saveAndSyncTasks();
}
private void saveAndSyncTasks() {
CacheManager.saveTasks(getContext(), currentTasks);
buildAndDisplayList();
RetrofitClient.getApiService().syncTasks(currentTasks).enqueue(new Callback<JsonElement>() {
@Override public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {}
@Override public void onFailure(Call<JsonElement> call, Throwable t) {}
});
}
} }

View file

@ -40,10 +40,7 @@ import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
// Din oppdaterte linje:
public static final String GOOGLE_WEB_CLIENT_ID = BuildConfig.WEB_CLIENT_ID; public static final String GOOGLE_WEB_CLIENT_ID = BuildConfig.WEB_CLIENT_ID;
private static final String TAG = "MainActivity";
private NavController navController; private NavController navController;
private DrawerLayout drawerLayout; private DrawerLayout drawerLayout;
private AppBarConfiguration appBarConfiguration; private AppBarConfiguration appBarConfiguration;
@ -73,7 +70,18 @@ public class MainActivity extends AppCompatActivity {
.build(); .build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
// Vi bruker en tilpasset listener for å sikre at Hjem og andre punkter alltid fungerer
navigationView.setNavigationItemSelectedListener(item -> {
boolean handled = NavigationUI.onNavDestinationSelected(item, navController);
if (handled || item.getItemId() == R.id.navigation_home) {
if (item.getItemId() == R.id.navigation_home) {
navController.navigate(R.id.navigation_home);
}
drawerLayout.closeDrawer(GravityCompat.START);
}
return handled;
});
navController.addOnDestinationChangedListener((controller, destination, arguments) -> { navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
if (destination.getId() == R.id.navigation_login) { if (destination.getId() == R.id.navigation_login) {
@ -82,7 +90,6 @@ public class MainActivity extends AppCompatActivity {
} else { } else {
toolbar.setVisibility(View.VISIBLE); toolbar.setVisibility(View.VISIBLE);
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
// Oppdater navn hver gang vi bytter skjerm for å være sikker
updateNavHeader(navigationView); updateNavHeader(navigationView);
} }
}); });
@ -90,9 +97,7 @@ public class MainActivity extends AppCompatActivity {
setupTaskReminders(); setupTaskReminders();
createNotificationChannel(); createNotificationChannel();
requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {}); requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {});
checkNotificationPermission(); checkNotificationPermission();
checkExactAlarmPermission(); checkExactAlarmPermission();
checkLoginState(); checkLoginState();
@ -102,7 +107,6 @@ public class MainActivity extends AppCompatActivity {
View headerView = navigationView.getHeaderView(0); View headerView = navigationView.getHeaderView(0);
TextView name = headerView.findViewById(R.id.nav_header_name); TextView name = headerView.findViewById(R.id.nav_header_name);
TextView email = headerView.findViewById(R.id.nav_header_email); TextView email = headerView.findViewById(R.id.nav_header_email);
UserManager user = UserManager.getInstance(); UserManager user = UserManager.getInstance();
if (user.isLoggedIn()) { if (user.isLoggedIn()) {
name.setText(user.getUserDisplayName()); name.setText(user.getUserDisplayName());

View file

@ -4,7 +4,6 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -30,7 +29,7 @@ public class NewsFullFragment extends Fragment {
private RecyclerView recyclerViewCategories; private RecyclerView recyclerViewCategories;
private ProgressBar progressBar; private ProgressBar progressBar;
private NewsAdapter newsAdapter; private NewsAdapter newsAdapter;
private List<WpPost> allPosts = new ArrayList<>(); // Holder ALLE postene private List<WpPost> allPosts = new ArrayList<>();
@Nullable @Nullable
@Override @Override
@ -45,7 +44,6 @@ public class NewsFullFragment extends Fragment {
recyclerViewNews = view.findViewById(R.id.recycler_news_full); recyclerViewNews = view.findViewById(R.id.recycler_news_full);
recyclerViewCategories = view.findViewById(R.id.recycler_categories); recyclerViewCategories = view.findViewById(R.id.recycler_categories);
progressBar = view.findViewById(R.id.loading_news_full); progressBar = view.findViewById(R.id.loading_news_full);
ImageView backBtn = view.findViewById(R.id.btn_back_news);
// Setup Nyhetsliste // Setup Nyhetsliste
recyclerViewNews.setLayoutManager(new LinearLayoutManager(getContext())); recyclerViewNews.setLayoutManager(new LinearLayoutManager(getContext()));
@ -54,15 +52,12 @@ public class NewsFullFragment extends Fragment {
recyclerViewCategories.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false)); recyclerViewCategories.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
setupCategories(); setupCategories();
backBtn.setOnClickListener(v -> Navigation.findNavController(view).navigateUp());
fetchAllNews(); fetchAllNews();
} }
private void setupCategories() { private void setupCategories() {
// Listen over kategorier du ønsket
List<String> categories = Arrays.asList( List<String> categories = Arrays.asList(
"Alle", // Standard vis alt "Alle",
"Avtaler og invitasjoner", "BHT", "Bilhold", "Cordel", "Avtaler og invitasjoner", "BHT", "Bilhold", "Cordel",
"Ferieavvikling", "Fest og moro", "Generell drift", "Ferieavvikling", "Fest og moro", "Generell drift",
"HMS", "IT og sikkerhet", "Miljøfyrtårn", "Møtereferat", "SMX" "HMS", "IT og sikkerhet", "Miljøfyrtårn", "Møtereferat", "SMX"
@ -76,7 +71,6 @@ public class NewsFullFragment extends Fragment {
private void fetchAllNews() { private void fetchAllNews() {
progressBar.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE);
// Hent 50 siste (bør holde for en "Siste nytt" liste, ellers vi paginere)
RetrofitClient.getApiService().getAllPosts().enqueue(new Callback<List<WpPost>>() { RetrofitClient.getApiService().getAllPosts().enqueue(new Callback<List<WpPost>>() {
@Override @Override
public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) { public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
@ -87,7 +81,6 @@ public class NewsFullFragment extends Fragment {
allPosts = response.body(); allPosts = response.body();
formatDates(allPosts); formatDates(allPosts);
// Vis alle i starten
newsAdapter = new NewsAdapter(new ArrayList<>(allPosts), post -> { newsAdapter = new NewsAdapter(new ArrayList<>(allPosts), post -> {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable("post_data", post); bundle.putSerializable("post_data", post);
@ -110,29 +103,23 @@ public class NewsFullFragment extends Fragment {
private void filterNews(String category) { private void filterNews(String category) {
if (newsAdapter == null) return; if (newsAdapter == null) return;
List<WpPost> filteredList = new ArrayList<>(); List<WpPost> filteredList = new ArrayList<>();
if (category.equals("Alle")) { if (category.equals("Alle")) {
filteredList.addAll(allPosts); filteredList.addAll(allPosts);
} else { } else {
for (WpPost post : allPosts) { for (WpPost post : allPosts) {
// Vi sjekker om kategorinavnet matcher
if (post.getCategoryName().equals(category)) { if (post.getCategoryName().equals(category)) {
filteredList.add(post); filteredList.add(post);
} }
} }
} }
// Oppdater adapteren med den filtrerte listen
newsAdapter.updateList(filteredList); newsAdapter.updateList(filteredList);
} }
private void formatDates(List<WpPost> posts) { private void formatDates(List<WpPost> posts) {
SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()); SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
rawFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault()); SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
targetFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
for (WpPost post : posts) { for (WpPost post : posts) {
try { try {

View file

@ -8,6 +8,8 @@ import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
@ -46,18 +48,38 @@ public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault()); SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
holder.date.setText("Frist: " + sdf.format(new Date(task.getDueDate()))); holder.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
// Vis hvem som tildelte oppgaven hvis det ikke er meg selv (Nytt krav)
if (task.getCreatedByEmail() != null && !task.getCreatedByEmail().equalsIgnoreCase(currentUserEmail)) {
holder.creator.setText("Tildelt av: " + task.getCreatedByName());
holder.creator.setVisibility(View.VISIBLE);
} else {
holder.creator.setVisibility(View.GONE);
}
boolean myStatus = task.getParticipantStatus(currentUserEmail); boolean myStatus = task.getParticipantStatus(currentUserEmail);
holder.checkBox.setChecked(myStatus); holder.checkBox.setChecked(myStatus);
long now = System.currentTimeMillis();
boolean isOverdue = task.getDueDate() < now && !task.isFullyCompleted() && !myStatus;
if (isOverdue) {
holder.cardView.setCardBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_soft_light_pink_beige));
holder.date.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_logo_accent_red));
holder.date.setText("FORFALT: " + sdf.format(new Date(task.getDueDate())));
} else {
holder.cardView.setCardBackgroundColor(Color.WHITE);
holder.date.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_muted_blue_gray));
}
if (myStatus || task.isFullyCompleted()) { if (myStatus || task.isFullyCompleted()) {
holder.title.setPaintFlags(holder.title.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); holder.title.setPaintFlags(holder.title.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.title.setTextColor(Color.GRAY); holder.title.setTextColor(Color.GRAY);
} else { holder.cardView.setCardBackgroundColor(Color.parseColor("#F5F5F5"));
} else if (!isOverdue) {
holder.title.setPaintFlags(holder.title.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); holder.title.setPaintFlags(holder.title.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
holder.title.setTextColor(Color.BLACK); holder.title.setTextColor(Color.BLACK);
} }
// Vis fremdrift (f.eks 1/3 fullført)
int total = task.getAssigneeStatus().size(); int total = task.getAssigneeStatus().size();
int done = 0; int done = 0;
for (Boolean b : task.getAssigneeStatus().values()) if (b) done++; for (Boolean b : task.getAssigneeStatus().values()) if (b) done++;
@ -71,14 +93,17 @@ public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {
public int getItemCount() { return tasks.size(); } public int getItemCount() { return tasks.size(); }
static class ViewHolder extends RecyclerView.ViewHolder { static class ViewHolder extends RecyclerView.ViewHolder {
TextView title, date, progress; TextView title, date, creator, progress;
CheckBox checkBox; CheckBox checkBox;
CardView cardView;
ViewHolder(View v) { ViewHolder(View v) {
super(v); super(v);
title = v.findViewById(R.id.task_title); title = v.findViewById(R.id.task_title);
date = v.findViewById(R.id.task_date); date = v.findViewById(R.id.task_date);
creator = v.findViewById(R.id.task_creator);
progress = v.findViewById(R.id.task_progress); progress = v.findViewById(R.id.task_progress);
checkBox = v.findViewById(R.id.task_checkbox); checkBox = v.findViewById(R.id.task_checkbox);
cardView = (CardView) v;
} }
} }
} }

View file

@ -1,6 +1,5 @@
package com.kbs.kbsintranett; package com.kbs.kbsintranett;
import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -25,6 +24,7 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
public interface OnTaskChangeListener { public interface OnTaskChangeListener {
void onTaskChanged(); void onTaskChanged();
void onTaskDeleted(TaskItem task); void onTaskDeleted(TaskItem task);
void onEditRequested(TaskItem task); // NYTT
} }
public TaskDetailsBottomSheet(TaskItem task, OnTaskChangeListener listener) { public TaskDetailsBottomSheet(TaskItem task, OnTaskChangeListener listener) {
@ -42,7 +42,9 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
TextView desc = v.findViewById(R.id.detail_task_desc); TextView desc = v.findViewById(R.id.detail_task_desc);
LinearLayout participantsContainer = v.findViewById(R.id.container_participants_status); LinearLayout participantsContainer = v.findViewById(R.id.container_participants_status);
SwitchMaterial switchNotify = v.findViewById(R.id.switch_notifications); SwitchMaterial switchNotify = v.findViewById(R.id.switch_notifications);
LinearLayout ownerActions = v.findViewById(R.id.layout_owner_actions);
Button btnDelete = v.findViewById(R.id.btn_delete_task); Button btnDelete = v.findViewById(R.id.btn_delete_task);
Button btnEdit = v.findViewById(R.id.btn_edit_task);
title.setText(task.getTitle()); title.setText(task.getTitle());
SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd. MMMM yyyy", Locale.getDefault()); SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd. MMMM yyyy", Locale.getDefault());
@ -53,7 +55,7 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
desc.setVisibility(View.VISIBLE); desc.setVisibility(View.VISIBLE);
} }
// List opp alle deltakere og deres status participantsContainer.removeAllViews();
for (Map.Entry<String, Boolean> entry : task.getAssigneeStatus().entrySet()) { for (Map.Entry<String, Boolean> entry : task.getAssigneeStatus().entrySet()) {
TextView t = new TextView(getContext()); TextView t = new TextView(getContext());
String status = entry.getValue() ? "✅ Fullført" : "⏳ Pågår"; String status = entry.getValue() ? "✅ Fullført" : "⏳ Pågår";
@ -68,13 +70,16 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
if (listener != null) listener.onTaskChanged(); if (listener != null) listener.onTaskChanged();
}); });
// Kun eier kan slette oppgaven if (task.getCreatedByEmail().equalsIgnoreCase(UserManager.getInstance().getUserEmail())) {
if (task.getCreatedByEmail().equals(UserManager.getInstance().getUserEmail())) { ownerActions.setVisibility(View.VISIBLE);
btnDelete.setVisibility(View.VISIBLE);
btnDelete.setOnClickListener(view -> { btnDelete.setOnClickListener(view -> {
if (listener != null) listener.onTaskDeleted(task); if (listener != null) listener.onTaskDeleted(task);
dismiss(); dismiss();
}); });
btnEdit.setOnClickListener(view -> {
if (listener != null) listener.onEditRequested(task);
dismiss();
});
} }
return v; return v;

View file

@ -13,11 +13,10 @@ public class TaskItem implements Serializable {
private String createdByEmail; private String createdByEmail;
private String createdByName; private String createdByName;
// Key: Bruker-epost, Value: Har fullført (true/false)
private Map<String, Boolean> assigneeStatus = new HashMap<>(); private Map<String, Boolean> assigneeStatus = new HashMap<>();
private boolean notificationsEnabled = true; private boolean notificationsEnabled = true;
private boolean isFullyCompleted = false; // Satt av eier eller når alle er ferdige private boolean isFullyCompleted = false;
public TaskItem(String title, String description, long dueDate) { public TaskItem(String title, String description, long dueDate) {
this.id = UUID.randomUUID().toString(); this.id = UUID.randomUUID().toString();
@ -28,15 +27,7 @@ public class TaskItem implements Serializable {
this.createdByName = UserManager.getInstance().getUserDisplayName(); this.createdByName = UserManager.getInstance().getUserDisplayName();
} }
// Hjelpemetoder // Getters
public void addAssignee(String email) { assigneeStatus.put(email, false); }
public void setParticipantStatus(String email, boolean done) { assigneeStatus.put(email, done); }
public boolean getParticipantStatus(String email) { return assigneeStatus.getOrDefault(email, false); }
public boolean isUserParticipant(String email) { return assigneeStatus.containsKey(email); }
public Map<String, Boolean> getAssigneeStatus() { return assigneeStatus; }
// Getters / Setters
public String getId() { return id; } public String getId() { return id; }
public String getTitle() { return title; } public String getTitle() { return title; }
public String getDescription() { return description; } public String getDescription() { return description; }
@ -44,7 +35,19 @@ public class TaskItem implements Serializable {
public String getCreatedByEmail() { return createdByEmail; } public String getCreatedByEmail() { return createdByEmail; }
public String getCreatedByName() { return createdByName; } public String getCreatedByName() { return createdByName; }
public boolean isNotificationsEnabled() { return notificationsEnabled; } public boolean isNotificationsEnabled() { return notificationsEnabled; }
public void setNotificationsEnabled(boolean enabled) { this.notificationsEnabled = enabled; }
public boolean isFullyCompleted() { return isFullyCompleted; } public boolean isFullyCompleted() { return isFullyCompleted; }
public void setFullyCompleted(boolean fullyCompleted) { isFullyCompleted = fullyCompleted; }
// Setters (NYTT FOR REDIGERING)
public void setTitle(String title) { this.title = title; }
public void setDescription(String description) { this.description = description; }
public void setDueDate(long dueDate) { this.dueDate = dueDate; }
public void setNotificationsEnabled(boolean enabled) { this.notificationsEnabled = enabled; }
public void setFullyCompleted(boolean fullyCompleted) { this.isFullyCompleted = fullyCompleted; }
// Participant logikk
public void addAssignee(String email) { assigneeStatus.put(email, false); }
public void setParticipantStatus(String email, boolean done) { assigneeStatus.put(email, done); }
public boolean getParticipantStatus(String email) { return assigneeStatus.getOrDefault(email, false); }
public boolean isUserParticipant(String email) { return assigneeStatus.containsKey(email); }
public Map<String, Boolean> getAssigneeStatus() { return assigneeStatus; }
} }

View file

@ -4,21 +4,29 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
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 com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import com.google.gson.JsonElement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickListener { public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickListener {
private RecyclerView recyclerView; private RecyclerView recyclerView;
private TaskAdapter adapter; private TaskAdapter adapter;
private TabLayout tabLayout; private TabLayout tabLayout;
private SwipeRefreshLayout swipeRefresh;
private List<TaskItem> allTasks = new ArrayList<>(); private List<TaskItem> allTasks = new ArrayList<>();
private String myEmail; private String myEmail;
@ -35,14 +43,25 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
myEmail = UserManager.getInstance().getUserEmail(); myEmail = UserManager.getInstance().getUserEmail();
tabLayout = view.findViewById(R.id.task_tabs); tabLayout = view.findViewById(R.id.task_tabs);
recyclerView = view.findViewById(R.id.recycler_tasks); recyclerView = view.findViewById(R.id.recycler_tasks);
swipeRefresh = view.findViewById(R.id.swipe_refresh_tasks);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
setupTabs();
FloatingActionButton fab = view.findViewById(R.id.fab_add_task); FloatingActionButton fab = view.findViewById(R.id.fab_add_task);
fab.setOnClickListener(v -> { fab.setOnClickListener(v -> {
AddTaskBottomSheet dialog = new AddTaskBottomSheet(); AddTaskBottomSheet dialog = new AddTaskBottomSheet();
dialog.setOnTaskAddedListener(task -> { dialog.setOnTaskAddedListener(new AddTaskBottomSheet.OnTaskAddedListener() {
allTasks.add(0, task); @Override
saveAndRefresh(); public void onTaskAdded(TaskItem task) {
allTasks.add(0, task);
saveAndSync();
}
@Override
public void onTaskUpdated(TaskItem task) {
saveAndSync();
}
}); });
dialog.show(getChildFragmentManager(), "AddTask"); dialog.show(getChildFragmentManager(), "AddTask");
}); });
@ -53,12 +72,41 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
@Override public void onTabReselected(TabLayout.Tab tab) {} @Override public void onTabReselected(TabLayout.Tab tab) {}
}); });
loadTasks(); swipeRefresh.setOnRefreshListener(this::fetchTasksFromServer);
}
private void loadTasks() {
allTasks = CacheManager.getTasks(getContext()); allTasks = CacheManager.getTasks(getContext());
filterAndDisplay(); filterAndDisplay();
fetchTasksFromServer();
}
private void setupTabs() {
tabLayout.removeAllTabs();
tabLayout.addTab(tabLayout.newTab().setText("Mine"));
tabLayout.addTab(tabLayout.newTab().setText("Fullførte"));
tabLayout.addTab(tabLayout.newTab().setText("Tildelt andre"));
if (UserManager.getInstance().isEditorOrAbove()) {
tabLayout.addTab(tabLayout.newTab().setText("Alle"));
}
}
private void fetchTasksFromServer() {
swipeRefresh.setRefreshing(true);
RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
@Override
public void onResponse(Call<List<TaskItem>> call, Response<List<TaskItem>> response) {
swipeRefresh.setRefreshing(false);
if (response.isSuccessful() && response.body() != null) {
allTasks = response.body();
CacheManager.saveTasks(getContext(), allTasks);
filterAndDisplay();
}
}
@Override
public void onFailure(Call<List<TaskItem>> call, Throwable t) {
swipeRefresh.setRefreshing(false);
Toast.makeText(getContext(), "Kunne ikke synkronisere oppgaver", Toast.LENGTH_SHORT).show();
}
});
} }
private void filterAndDisplay() { private void filterAndDisplay() {
@ -66,21 +114,31 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
int selectedTab = tabLayout.getSelectedTabPosition(); int selectedTab = tabLayout.getSelectedTabPosition();
for (TaskItem t : allTasks) { for (TaskItem t : allTasks) {
if (selectedTab == 0) { // MINE (Oppgaver tildelt meg som ikke er fullført av meg ennå) boolean isParticipant = t.isUserParticipant(myEmail);
if (t.isUserParticipant(myEmail) && !t.getParticipantStatus(myEmail) && !t.isFullyCompleted()) { boolean isCreator = t.getCreatedByEmail().equalsIgnoreCase(myEmail);
filtered.add(t); boolean iHaveDoneIt = t.getParticipantStatus(myEmail);
} boolean hasOtherParticipants = false;
} else if (selectedTab == 1) { // TILDELT ANDRE (Oppgaver jeg har laget, uansett om jeg er med selv) for (String email : t.getAssigneeStatus().keySet()) {
if (t.getCreatedByEmail().equals(myEmail) && !t.isFullyCompleted()) { if (!email.equalsIgnoreCase(myEmail)) { hasOtherParticipants = true; break; }
filtered.add(t); }
}
} else { // FULLFØRTE (Alt som er arkivert som ferdig) switch (selectedTab) {
if (t.isFullyCompleted() || (t.isUserParticipant(myEmail) && t.getParticipantStatus(myEmail))) { case 0: // MINE
filtered.add(t); if (isParticipant && !iHaveDoneIt && !t.isFullyCompleted()) filtered.add(t);
} break;
case 1: // FULLFØRTE
if (t.isFullyCompleted() || (isParticipant && iHaveDoneIt)) filtered.add(t);
break;
case 2: // TILDELT ANDRE
if (isCreator && !t.isFullyCompleted() && hasOtherParticipants) filtered.add(t);
break;
case 3: // ALLE
if (!t.isFullyCompleted()) filtered.add(t);
break;
} }
} }
if (selectedTab == 1) Collections.sort(filtered, (t1, t2) -> Long.compare(t2.getDueDate(), t1.getDueDate()));
else Collections.sort(filtered, (t1, t2) -> Long.compare(t1.getDueDate(), t2.getDueDate()));
adapter = new TaskAdapter(filtered, this); adapter = new TaskAdapter(filtered, this);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
} }
@ -88,15 +146,21 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
@Override @Override
public void onTaskClick(TaskItem task) { public void onTaskClick(TaskItem task) {
TaskDetailsBottomSheet sheet = new TaskDetailsBottomSheet(task, new TaskDetailsBottomSheet.OnTaskChangeListener() { TaskDetailsBottomSheet sheet = new TaskDetailsBottomSheet(task, new TaskDetailsBottomSheet.OnTaskChangeListener() {
@Override @Override public void onTaskChanged() { saveAndSync(); }
public void onTaskChanged() { @Override public void onTaskDeleted(TaskItem taskToDelete) {
saveAndRefresh();
}
@Override
public void onTaskDeleted(TaskItem taskToDelete) {
allTasks.remove(taskToDelete); allTasks.remove(taskToDelete);
saveAndRefresh(); saveAndSync();
}
@Override public void onEditRequested(TaskItem taskToEdit) {
AddTaskBottomSheet editDialog = new AddTaskBottomSheet();
editDialog.setTaskToEdit(taskToEdit);
editDialog.setOnTaskAddedListener(new AddTaskBottomSheet.OnTaskAddedListener() {
@Override public void onTaskAdded(TaskItem task) {} // Ikke i bruk her
@Override public void onTaskUpdated(TaskItem task) {
saveAndSync();
}
});
editDialog.show(getChildFragmentManager(), "EditTask");
} }
}); });
sheet.show(getChildFragmentManager(), "TaskDetails"); sheet.show(getChildFragmentManager(), "TaskDetails");
@ -105,18 +169,22 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
@Override @Override
public void onStatusChanged(TaskItem task, boolean isDone) { public void onStatusChanged(TaskItem task, boolean isDone) {
task.setParticipantStatus(myEmail, isDone); task.setParticipantStatus(myEmail, isDone);
boolean hasOthers = false;
// Logikk: Hvis den som opprettet oppgaven merker den som ferdig, for (String email : task.getAssigneeStatus().keySet()) {
// regnes hele oppgaven som ferdig for alle (arkiveres). if (!email.equalsIgnoreCase(myEmail)) { hasOthers = true; break; }
if (task.getCreatedByEmail().equals(myEmail) && isDone) { }
if (task.getCreatedByEmail().equalsIgnoreCase(myEmail) && isDone && !hasOthers) {
task.setFullyCompleted(true); task.setFullyCompleted(true);
} }
saveAndSync();
saveAndRefresh();
} }
private void saveAndRefresh() { private void saveAndSync() {
CacheManager.saveTasks(getContext(), allTasks); CacheManager.saveTasks(getContext(), allTasks);
filterAndDisplay(); filterAndDisplay();
RetrofitClient.getApiService().syncTasks(allTasks).enqueue(new Callback<JsonElement>() {
@Override public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {}
@Override public void onFailure(Call<JsonElement> call, Throwable t) {}
});
} }
} }

View file

@ -17,20 +17,47 @@ import retrofit2.http.PartMap;
import retrofit2.http.Query; import retrofit2.http.Query;
public interface WordPressApiService { public interface WordPressApiService {
// --- NYHETER ---
@GET("wp-json/wp/v2/posts?per_page=10&_embed") @GET("wp-json/wp/v2/posts?per_page=10&_embed")
Call<List<WpPost>> getPosts(); Call<List<WpPost>> getPosts();
@GET("wp-json/kbs/v1/forms/{id}")
Call<GravityForm> getForm(@Path("id") int formId);
@POST("wp-json/gf/v2/forms/{id}/submissions") @GET("wp-json/wp/v2/posts?per_page=50&_embed")
Call<JsonElement> submitForm(@Path("id") int formId, @Body FormSubmission submission); Call<List<WpPost>> getAllPosts();
// --- AUTENTISERING & ENHET ---
@POST("wp-json/kbs/v1/login") @POST("wp-json/kbs/v1/login")
Call<LoginResponse> googleLogin(@Body LoginRequest request); Call<LoginResponse> googleLogin(@Body LoginRequest request);
@POST("wp-json/kbs/v1/device/register")
Call<JsonElement> registerDevice(@Body RegisterDeviceRequest request);
@GET("wp-json/kbs/v1/users")
Call<List<User>> getUsersList();
// --- KALENDER ---
@GET("wp-json/kbs/v1/calendar/events")
Call<List<CalendarEvent>> getCalendarEvents();
@POST("wp-json/kbs/v1/calendar/create")
Call<JsonElement> createCalendarEvent(@Body CreateEventRequest request);
@POST("wp-json/kbs/v1/calendar/update")
Call<JsonElement> updateCalendarEvent(@Body CreateEventRequest request);
@POST("wp-json/kbs/v1/calendar/delete")
Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request);
// --- SKJEMAER (GRAVITY FORMS) ---
@GET("wp-json/kbs/v1/forms") @GET("wp-json/kbs/v1/forms")
Call<List<GravityForm>> getFormsList(); Call<List<GravityForm>> getFormsList();
@GET("wp-json/kbs/v1/forms/{id}")
Call<GravityForm> getForm(@Path("id") int formId);
@Multipart @Multipart
@POST("wp-json/gf/v2/forms/{id}/submissions") @POST("wp-json/gf/v2/forms/{id}/submissions")
Call<JsonElement> submitMultipartForm( Call<JsonElement> submitMultipartForm(
@ -39,16 +66,6 @@ public interface WordPressApiService {
@Part List<MultipartBody.Part> files @Part List<MultipartBody.Part> files
); );
@GET("wp-json/kbs/v1/calendar/events")
Call<List<CalendarEvent>> getCalendarEvents();
@POST("wp-json/kbs/v1/calendar/create")
Call<JsonElement> createCalendarEvent(@Body CreateEventRequest request);
// NYTT ENDEPUNKT
@GET("wp-json/kbs/v1/users")
Call<List<User>> getUsersList();
@GET("wp-json/gf/v2/entries") @GET("wp-json/gf/v2/entries")
Call<GravityEntryResponse> getEntries( Call<GravityEntryResponse> getEntries(
@Query("form_ids") int formId, @Query("form_ids") int formId,
@ -59,9 +76,8 @@ public interface WordPressApiService {
@GET("wp-json/gf/v2/entries/{entry_id}") @GET("wp-json/gf/v2/entries/{entry_id}")
Call<JsonElement> getSingleEntry(@Path("entry_id") String entryId); Call<JsonElement> getSingleEntry(@Path("entry_id") String entryId);
@GET("wp-json/wp/v2/posts?per_page=50&_embed")
Call<List<WpPost>> getAllPosts();
// --- HÅNDBOK ---
@GET("wp-json/kbs/v1/handbook") @GET("wp-json/kbs/v1/handbook")
Call<List<HandbookItem>> getHandbookItems(); Call<List<HandbookItem>> getHandbookItems();
@ -71,12 +87,11 @@ public interface WordPressApiService {
@GET("wp-json/kbs/v1/lookup-id") @GET("wp-json/kbs/v1/lookup-id")
Call<JsonObject> lookupPageId(@Query("url") String url); Call<JsonObject> lookupPageId(@Query("url") String url);
@POST("wp-json/kbs/v1/calendar/update")
Call<JsonElement> updateCalendarEvent(@Body CreateEventRequest request);
@POST("wp-json/kbs/v1/calendar/delete") // --- OPPGAVER (SYNKRONISERING) ---
Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request); @GET("wp-json/kbs/v1/tasks")
Call<List<TaskItem>> getTasks();
@POST("wp-json/kbs/v1/device/register") @POST("wp-json/kbs/v1/tasks/sync")
Call<JsonElement> registerDevice(@Body RegisterDeviceRequest request); Call<JsonElement> syncTasks(@Body List<TaskItem> tasks);
} }

View file

@ -1,84 +1,92 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:fillViewport="true">
android:padding="24dp"
android:background="@color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ny Oppgave"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_marginBottom="16dp"/>
<EditText
android:id="@+id/et_task_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Hva skal gjøres?"
android:inputType="textCapSentences"
android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/et_task_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Beskrivelse (valgfritt)"
android:inputType="textMultiLine"
android:minLines="2"
android:gravity="top"
android:layout_marginBottom="16dp"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="vertical"
android:gravity="center_vertical" android:padding="24dp"
android:layout_marginBottom="12dp"> android:background="@color/white">
<Button
android:id="@+id/btn_task_date"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sett frist" />
<TextView <TextView
android:id="@+id/txt_date_preview" android:id="@+id/txt_sheet_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Frist: -" android:text="Ny Oppgave"
android:layout_marginStart="8dp"/> android:textSize="20sp"
</LinearLayout> android:textStyle="bold"
android:textColor="@color/black"
android:layout_marginBottom="16dp"/>
<EditText
android:id="@+id/et_task_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Hva skal gjøres?"
android:inputType="textCapSentences"
android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/et_task_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Beskrivelse (valgfritt)"
android:inputType="textMultiLine"
android:minLines="2"
android:gravity="top"
android:layout_marginBottom="16dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="12dp">
<Button
android:id="@+id/btn_task_date"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sett frist" />
<TextView
android:id="@+id/txt_date_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Frist: -"
android:layout_marginStart="8dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="24dp">
<Button
android:id="@+id/btn_task_users"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Velg deltakere" />
<TextView
android:id="@+id/txt_users_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Kun meg"
android:layout_marginStart="8dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="24dp">
<Button <Button
android:id="@+id/btn_task_users" android:id="@+id/btn_save_task"
style="@style/Widget.MaterialComponents.Button.TextButton" android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Velg deltakere" /> android:text="Lagre Oppgave"
<TextView android:backgroundTint="@color/kbs_logo_blue"
android:id="@+id/txt_users_preview" android:textColor="@color/white"
android:layout_width="wrap_content" android:layout_marginBottom="16dp"/>
android:layout_height="wrap_content"
android:text="Kun meg"
android:layout_marginStart="8dp"/>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView>
<Button
android:id="@+id/btn_save_task"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Lagre Oppgave"
android:backgroundTint="@color/kbs_logo_blue"
android:textColor="@color/white"/>
</LinearLayout>

View file

@ -65,13 +65,32 @@
android:checked="true" android:checked="true"
android:layout_marginBottom="24dp"/> android:layout_marginBottom="24dp"/>
<Button <LinearLayout
android:id="@+id/btn_delete_task" android:id="@+id/layout_owner_actions"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Slett oppgave" android:orientation="horizontal"
android:backgroundTint="@color/kbs_logo_accent_red" android:visibility="gone">
android:textColor="@color/white"
android:visibility="gone"/> <Button
android:id="@+id/btn_delete_task"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Slett"
android:backgroundTint="@color/kbs_logo_accent_red"
android:textColor="@color/white"
android:layout_marginEnd="8dp"/>
<Button
android:id="@+id/btn_edit_task"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Endre"
android:backgroundTint="@color/kbs_logo_blue"
android:textColor="@color/white"
android:layout_marginStart="8dp"/>
</LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,44 +1,42 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"> android:background="@color/white">
<!-- Knappen ligger nå helt øverst i fragmentet (under den globale toolbaren) --> <androidx.recyclerview.widget.RecyclerView
<Button android:id="@+id/recycler_full_calendar"
android:id="@+id/btn_add_calendar_event"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="80dp" />
<ProgressBar
android:id="@+id/loading_full_calendar"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="+ Ny Kalenderhendelse" android:layout_gravity="center"/>
android:backgroundTint="@color/kbs_logo_blue"
android:textColor="@color/white" <TextView
android:layout_margin="16dp" android:id="@+id/empty_view_calendar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ingen hendelser funnet"
android:layout_gravity="center"
android:visibility="gone"/> android:visibility="gone"/>
<FrameLayout <!-- NY FAB: Flytende knapp nederst til høyre -->
android:layout_width="match_parent" <com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_height="match_parent"> android:id="@+id/fab_add_calendar_event"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="24dp"
android:src="@android:drawable/ic_input_add"
app:backgroundTint="@color/kbs_logo_blue"
app:tint="@color/white"
android:visibility="gone"
android:contentDescription="Ny hendelse" />
<androidx.recyclerview.widget.RecyclerView </FrameLayout>
android:id="@+id/recycler_full_calendar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false" />
<ProgressBar
android:id="@+id/loading_full_calendar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<TextView
android:id="@+id/empty_view_calendar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ingen hendelser funnet"
android:layout_gravity="center"
android:visibility="gone"/>
</FrameLayout>
</LinearLayout>

View file

@ -5,31 +5,7 @@
android:orientation="vertical" android:orientation="vertical"
android:background="@color/kbs_very_light_blue"> android:background="@color/kbs_very_light_blue">
<RelativeLayout <!-- Redundant header er fjernet. Kun kategorilisten og nyhetslisten er igjen. -->
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@android:color/white"
android:elevation="4dp">
<ImageView
android:id="@+id/btn_back_news"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@android:drawable/ic_menu_revert"
android:layout_centerVertical="true"
android:contentDescription="Tilbake"
android:background="?attr/selectableItemBackgroundBorderless"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Siste nytt"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:layout_centerInParent="true"/>
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_categories" android:id="@+id/recycler_categories"

View file

@ -13,21 +13,7 @@
android:background="@color/white" android:background="@color/white"
app:tabSelectedTextColor="@color/kbs_logo_blue" app:tabSelectedTextColor="@color/kbs_logo_blue"
app:tabIndicatorColor="@color/kbs_logo_blue"> app:tabIndicatorColor="@color/kbs_logo_blue">
<!-- Faner legges til programmatisk i TasksFragment.java -->
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Mine" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tildelt andre" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fullførte" />
</com.google.android.material.tabs.TabLayout> </com.google.android.material.tabs.TabLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ingen oppgaver tildelt"
android:textColor="@color/kbs_muted_blue_gray"
android:textSize="14sp"
android:textStyle="italic"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp" />

View file

@ -44,6 +44,15 @@
android:textSize="12sp" android:textSize="12sp"
android:textColor="@color/kbs_muted_blue_gray" /> android:textColor="@color/kbs_muted_blue_gray" />
<TextView
android:id="@+id/task_creator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Tildelt av: -"
android:textSize="11sp"
android:textColor="@color/kbs_logo_blue"
android:visibility="gone" />
<TextView <TextView
android:id="@+id/task_progress" android:id="@+id/task_progress"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -51,7 +60,7 @@
android:text="Fremdrift: 0/0" android:text="Fremdrift: 0/0"
android:textSize="11sp" android:textSize="11sp"
android:textStyle="italic" android:textStyle="italic"
android:textColor="@color/kbs_logo_blue" android:textColor="@color/kbs_muted_blue_gray"
android:layout_marginTop="2dp"/> android:layout_marginTop="2dp"/>
</LinearLayout> </LinearLayout>