Før tilgangskontroll til kalendre
This commit is contained in:
parent
fb9749ba3f
commit
3b2d19bd0b
10 changed files with 419 additions and 187 deletions
|
|
@ -1,23 +1,29 @@
|
|||
package com.kbs.kbsintranett;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
import android.provider.CalendarContract;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import com.google.gson.JsonElement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
|
||||
|
||||
private CalendarEvent event;
|
||||
|
||||
public CalendarDetailsBottomSheet(CalendarEvent event) {
|
||||
|
|
@ -33,20 +39,19 @@ public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||
TextView time = view.findViewById(R.id.sheet_time);
|
||||
TextView desc = view.findViewById(R.id.sheet_desc);
|
||||
TextView loc = view.findViewById(R.id.sheet_location);
|
||||
Button btnAdd = view.findViewById(R.id.btn_add_to_calendar);
|
||||
|
||||
// Skjul knapp siden appen nå varsler automatisk (iht krav)
|
||||
btnAdd.setVisibility(View.GONE);
|
||||
LinearLayout adminLayout = view.findViewById(R.id.layout_admin_buttons);
|
||||
Button btnDelete = view.findViewById(R.id.btn_delete);
|
||||
Button btnEdit = view.findViewById(R.id.btn_edit);
|
||||
|
||||
title.setText(event.getTitle());
|
||||
time.setText(event.getTime() + " (" + event.getDay() + ". " + event.getMonth() + ")");
|
||||
|
||||
if (!event.getDescription().isEmpty()) {
|
||||
// HER ER FIKSEN FOR HTML:
|
||||
desc.setText(android.text.Html.fromHtml(event.getDescription(), android.text.Html.FROM_HTML_MODE_COMPACT));
|
||||
// Skjul #varsel-taggen for visning
|
||||
String cleanDesc = event.getDescription().replaceAll("#varsel:[\\d,]+", "").trim();
|
||||
desc.setText(Html.fromHtml(cleanDesc, Html.FROM_HTML_MODE_COMPACT));
|
||||
desc.setVisibility(View.VISIBLE);
|
||||
// Gjør linker klikkbare
|
||||
desc.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
|
||||
desc.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
} else {
|
||||
desc.setVisibility(View.GONE);
|
||||
}
|
||||
|
|
@ -58,36 +63,63 @@ public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||
loc.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Sjekk admin-rettigheter
|
||||
if (UserManager.getInstance().isEditorOrAbove()) {
|
||||
adminLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
btnDelete.setOnClickListener(v -> confirmDelete());
|
||||
|
||||
btnEdit.setOnClickListener(v -> {
|
||||
// Send eventet videre til redigering
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putSerializable("edit_event", event);
|
||||
// Vi må navigere via parent fragmentets navController
|
||||
NavHostFragment.findNavController(this).navigate(R.id.navigation_create_event, bundle);
|
||||
dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void addToSystemCalendar() {
|
||||
try {
|
||||
SimpleDateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||
apiFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
||||
|
||||
Date startDate = apiFormat.parse(event.getRawDate());
|
||||
long startMillis = startDate.getTime();
|
||||
long endMillis = startMillis + (60 * 60 * 1000); // Default 1 time hvis slutt mangler
|
||||
|
||||
if (event.getRawEndDate() != null && !event.getRawEndDate().isEmpty()) {
|
||||
Date endDate = apiFormat.parse(event.getRawEndDate());
|
||||
endMillis = endDate.getTime();
|
||||
private void confirmDelete() {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle("Slett hendelse")
|
||||
.setMessage("Er du sikker på at du vil slette '" + event.getTitle() + "'?")
|
||||
.setPositiveButton("Slett", (dialog, which) -> deleteEvent())
|
||||
.setNegativeButton("Avbryt", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_INSERT)
|
||||
.setData(CalendarContract.Events.CONTENT_URI)
|
||||
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startMillis)
|
||||
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endMillis)
|
||||
.putExtra(CalendarContract.Events.TITLE, event.getTitle())
|
||||
.putExtra(CalendarContract.Events.DESCRIPTION, event.getDescription())
|
||||
.putExtra(CalendarContract.Events.EVENT_LOCATION, event.getLocation())
|
||||
.putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY);
|
||||
private void deleteEvent() {
|
||||
// Vi må sende en CreateEventRequest med ID for å slette
|
||||
// Vi trenger ikke fylle ut alt, bare ID og kalendertype
|
||||
// Siden vi ikke vet nøyaktig hvilken kalender den kom fra (APIet gir ikke det),
|
||||
// prøver vi "Felles" som default, eller prøver å slette fra ID.
|
||||
// PHP-koden vi lagde støtter sletting basert på ID hvis vi sender riktig kalendertype.
|
||||
// For nå antar vi "Felles" eller looper i backend. I V11.3 PHP scriptet sletter den basert på ID i valgt kalender.
|
||||
|
||||
startActivity(intent);
|
||||
CreateEventRequest req = new CreateEventRequest(
|
||||
"", "", "", "", "", "Felles", new ArrayList<>(), false, ""
|
||||
);
|
||||
req.id = event.getId();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
RetrofitClient.getApiService().deleteCalendarEvent(req).enqueue(new Callback<JsonElement>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
|
||||
if (response.isSuccessful()) {
|
||||
Toast.makeText(getContext(), "Slettet!", Toast.LENGTH_SHORT).show();
|
||||
dismiss();
|
||||
// Her burde vi ideelt sett oppdatert listen bak, men brukeren kan dra for å oppdatere
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Kunne ikke slette (Er det en felles-hendelse?)", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<JsonElement> call, Throwable t) {
|
||||
Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +1,38 @@
|
|||
package com.kbs.kbsintranett;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CalendarEvent {
|
||||
public class CalendarEvent implements Serializable {
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
@SerializedName("title")
|
||||
private String title;
|
||||
@SerializedName("start_date") // Juster denne nøkkelen til hva APIet faktisk returnerer (f.eks "start")
|
||||
|
||||
@SerializedName("start_date")
|
||||
private String rawDate;
|
||||
@SerializedName("end_date") // Juster nøkkel (f.eks "end")
|
||||
|
||||
@SerializedName("end_date")
|
||||
private String rawEndDate;
|
||||
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
|
||||
@SerializedName("location")
|
||||
private String location;
|
||||
|
||||
// --- NYTT FELT: Varsling (minutter før start) ---
|
||||
// Nå henter denne verdien direkte fra "reminder_minutes" i JSON-responsen fra PHP
|
||||
@SerializedName("reminder_minutes")
|
||||
private int reminderMinutes = 15; // Default 15 min
|
||||
// V11.0: Liste av minutter (f.eks [15, 60])
|
||||
@SerializedName("reminders")
|
||||
private List<Integer> reminders = new ArrayList<>();
|
||||
|
||||
// --- UI-hjelpefelter (settes manuelt i appen etter parsing) ---
|
||||
private String day; // F.eks "12"
|
||||
private String month; // F.eks "DES"
|
||||
private String time; // F.eks "10:00 - 11:30"
|
||||
// UI-hjelpefelter
|
||||
private String day;
|
||||
private String month;
|
||||
private String time;
|
||||
|
||||
// Konstruktør for Retrofit (Gson)
|
||||
// Konstruktør
|
||||
public CalendarEvent(String title, String rawDate, String rawEndDate, String description, String location) {
|
||||
this.title = title;
|
||||
this.rawDate = rawDate;
|
||||
|
|
@ -34,31 +41,41 @@ public class CalendarEvent {
|
|||
this.location = location;
|
||||
}
|
||||
|
||||
// Konstruktør for manuell opprettelse (f.eks ved feil)
|
||||
public CalendarEvent(String title, String time, String day, String month) {
|
||||
this.title = title;
|
||||
this.time = time;
|
||||
this.day = day;
|
||||
this.month = month;
|
||||
}
|
||||
|
||||
public String getId() { return id; }
|
||||
public String getTitle() { return title; }
|
||||
public String getRawDate() { return rawDate; }
|
||||
public String getRawEndDate() { return rawEndDate; }
|
||||
public String getDescription() { return description != null ? description : ""; }
|
||||
public String getLocation() { return location != null ? location : ""; }
|
||||
|
||||
// Getters og Setters for UI-felter
|
||||
// Henter listen. Hvis den er null (gamle data), returner tom liste.
|
||||
public List<Integer> getReminders() {
|
||||
return reminders != null ? reminders : new ArrayList<>();
|
||||
}
|
||||
|
||||
// --- KOMPATIBILITETS-METODER (For å fikse build-feil i CalendarManager og Worker) ---
|
||||
|
||||
// Brukes av CalendarManager.java for lokale events
|
||||
public void setReminderMinutes(int minutes) {
|
||||
this.reminders = new ArrayList<>();
|
||||
if (minutes > 0) {
|
||||
this.reminders.add(minutes);
|
||||
}
|
||||
}
|
||||
|
||||
// Brukes hvis gammel kode prøver å hente ett tall. Returnerer det første i listen.
|
||||
public int getReminderMinutes() {
|
||||
if (reminders != null && !reminders.isEmpty()) {
|
||||
return reminders.get(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- UI SETTERS/GETTERS ---
|
||||
public String getDay() { return day; }
|
||||
public void setDay(String day) { this.day = day; }
|
||||
|
||||
public String getMonth() { return month; }
|
||||
public void setMonth(String month) { this.month = month; }
|
||||
|
||||
public String getTime() { return time; }
|
||||
public void setTime(String time) { this.time = time; }
|
||||
|
||||
// --- NYE METODER FOR VARSLING ---
|
||||
public int getReminderMinutes() { return reminderMinutes; }
|
||||
public void setReminderMinutes(int minutes) { this.reminderMinutes = minutes; }
|
||||
}
|
||||
|
|
@ -118,7 +118,8 @@ public class CalendarManager {
|
|||
}
|
||||
|
||||
CalendarEvent event = new CalendarEvent(title, rawStart, rawEnd, desc, loc);
|
||||
event.setReminderMinutes(0); // Systemet håndterer lokale varsler
|
||||
// Denne metoden eksisterer nå i CalendarEvent (se fil 1)
|
||||
event.setReminderMinutes(0);
|
||||
|
||||
formatEventForUI(event);
|
||||
deviceEvents.add(event);
|
||||
|
|
|
|||
|
|
@ -22,19 +22,25 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.gson.JsonElement;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class CreateEventFragment extends Fragment {
|
||||
private EditText etTitle, etDesc, etLocation;
|
||||
private Spinner spinnerCalendar, spinnerReminder, spinnerRecurrence;
|
||||
private Spinner spinnerCalendar, spinnerRecurrence;
|
||||
private ChipGroup chipGroupReminders;
|
||||
private Switch switchAllDay;
|
||||
private TextView txtPreview;
|
||||
private Button btnStartDate, btnStartTime, btnEndDate, btnEndTime, btnSave;
|
||||
|
|
@ -45,6 +51,9 @@ public class CreateEventFragment extends Fragment {
|
|||
private String selectedRRule = null;
|
||||
private boolean isCustomRecurrence = false;
|
||||
|
||||
// EDIT MODE
|
||||
private CalendarEvent eventToEdit = null;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
|
|
@ -61,8 +70,8 @@ public class CreateEventFragment extends Fragment {
|
|||
switchAllDay = view.findViewById(R.id.switch_all_day);
|
||||
|
||||
spinnerCalendar = view.findViewById(R.id.spinner_calendar);
|
||||
spinnerReminder = view.findViewById(R.id.spinner_reminder);
|
||||
spinnerRecurrence = view.findViewById(R.id.spinner_recurrence);
|
||||
chipGroupReminders = view.findViewById(R.id.chip_group_reminders);
|
||||
|
||||
txtPreview = view.findViewById(R.id.txt_time_preview);
|
||||
|
||||
|
|
@ -73,13 +82,22 @@ public class CreateEventFragment extends Fragment {
|
|||
|
||||
btnSave = view.findViewById(R.id.btn_save_event);
|
||||
|
||||
// Initialiser tid (neste hele time)
|
||||
setupCalendarSpinner();
|
||||
setupReminderChips();
|
||||
|
||||
// SJEKK OM VI ER I REDIGERINGS-MODUS
|
||||
if (getArguments() != null && getArguments().containsKey("edit_event")) {
|
||||
eventToEdit = (CalendarEvent) getArguments().getSerializable("edit_event");
|
||||
prefillForm(eventToEdit);
|
||||
btnSave.setText("Oppdater Hendelse");
|
||||
} else {
|
||||
// Ny event: Standard tid (neste time)
|
||||
startCal.add(Calendar.HOUR_OF_DAY, 1);
|
||||
startCal.set(Calendar.MINUTE, 0);
|
||||
endCal.setTime(startCal.getTime());
|
||||
endCal.add(Calendar.HOUR_OF_DAY, 1);
|
||||
}
|
||||
|
||||
setupStandardSpinners();
|
||||
updateRecurrenceSpinner();
|
||||
updateUI();
|
||||
|
||||
|
|
@ -97,8 +115,10 @@ public class CreateEventFragment extends Fragment {
|
|||
spinnerRecurrence.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
String selected = parent.getItemAtPosition(position).toString();
|
||||
// Unngå å overskrive ved innlasting
|
||||
if (eventToEdit != null && position == 0) return;
|
||||
|
||||
String selected = parent.getItemAtPosition(position).toString();
|
||||
if (selected.equals("Egendefinert...")) {
|
||||
showCustomRecurrenceDialog();
|
||||
} else if (selected.startsWith("Ikke gjenta")) {
|
||||
|
|
@ -111,8 +131,89 @@ public class CreateEventFragment extends Fragment {
|
|||
});
|
||||
}
|
||||
|
||||
private void setupStandardSpinners() {
|
||||
// Må matche PHP switch-case nøyaktig
|
||||
private void prefillForm(CalendarEvent event) {
|
||||
etTitle.setText(event.getTitle());
|
||||
|
||||
// Rens beskrivelsen for #varsel tag
|
||||
String cleanDesc = event.getDescription().replaceAll("#varsel:[\\d,]+", "").trim();
|
||||
etDesc.setText(cleanDesc);
|
||||
etLocation.setText(event.getLocation());
|
||||
|
||||
// Dato-parsing
|
||||
try {
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||
SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
|
||||
String start = event.getRawDate();
|
||||
if (start != null) {
|
||||
if (start.length() == 10) {
|
||||
// Heldags
|
||||
switchAllDay.setChecked(true);
|
||||
Date d = simpleFormat.parse(start);
|
||||
startCal.setTime(d);
|
||||
|
||||
// Sluttdato
|
||||
if (event.getRawEndDate() != null) {
|
||||
Date e = simpleFormat.parse(event.getRawEndDate());
|
||||
// Google sender sluttdato eksklusiv (dagen etter). Vi trekker fra 1 dag for visning.
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTime(e);
|
||||
c.add(Calendar.DAY_OF_MONTH, -1);
|
||||
endCal.setTime(c.getTime());
|
||||
} else {
|
||||
endCal.setTime(d);
|
||||
}
|
||||
} else if (start.contains("T")) {
|
||||
// Vanlig tid (kutt tidssone)
|
||||
if (start.length() > 19) start = start.substring(0, 19);
|
||||
startCal.setTime(isoFormat.parse(start));
|
||||
|
||||
String end = event.getRawEndDate();
|
||||
if (end != null && end.contains("T")) {
|
||||
if (end.length() > 19) end = end.substring(0, 19);
|
||||
endCal.setTime(isoFormat.parse(end));
|
||||
} else {
|
||||
endCal.setTime(startCal.getTime());
|
||||
endCal.add(Calendar.HOUR_OF_DAY, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Varsler - Finn tag i den rå beskrivelsen
|
||||
if (event.getDescription() != null) {
|
||||
Pattern p = Pattern.compile("#varsel:([\\d,]+)");
|
||||
Matcher m = p.matcher(event.getDescription());
|
||||
if (m.find()) {
|
||||
String[] parts = m.group(1).split(",");
|
||||
// Fjern alle sjekkmerker først
|
||||
for (int i = 0; i < chipGroupReminders.getChildCount(); i++) {
|
||||
((Chip) chipGroupReminders.getChildAt(i)).setChecked(false);
|
||||
}
|
||||
// Sett nye
|
||||
for (String part : parts) {
|
||||
try {
|
||||
int min = Integer.parseInt(part);
|
||||
checkChipByMinutes(min);
|
||||
} catch (NumberFormatException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("KBS_EDIT", "Feil ved prefill", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkChipByMinutes(int minutes) {
|
||||
for (int i = 0; i < chipGroupReminders.getChildCount(); i++) {
|
||||
Chip chip = (Chip) chipGroupReminders.getChildAt(i);
|
||||
if ((int)chip.getTag() == minutes) {
|
||||
chip.setChecked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupCalendarSpinner() {
|
||||
String[] calendars = {
|
||||
"Felles",
|
||||
"Administrasjonen",
|
||||
|
|
@ -121,11 +222,42 @@ public class CreateEventFragment extends Fragment {
|
|||
"Prosjektavdelingen"
|
||||
};
|
||||
spinnerCalendar.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, calendars));
|
||||
}
|
||||
|
||||
String[] reminders = {"Ingen varsling", "10 min før", "15 min før", "30 min før", "1 time før", "2 timer før", "24 timer før"};
|
||||
ArrayAdapter<String> remAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, reminders);
|
||||
spinnerReminder.setAdapter(remAdapter);
|
||||
spinnerReminder.setSelection(2); // 15 min default
|
||||
private void setupReminderChips() {
|
||||
addChip("Ved start", 0);
|
||||
addChip("5 min", 5);
|
||||
addChip("10 min", 10);
|
||||
addChip("15 min", 15);
|
||||
addChip("30 min", 30);
|
||||
addChip("1 time", 60);
|
||||
addChip("2 timer", 120);
|
||||
addChip("1 dag", 1440);
|
||||
addChip("2 dager", 2880);
|
||||
addChip("1 uke", 10080);
|
||||
|
||||
// Marker 15 min som standard KUN for ny event
|
||||
if (eventToEdit == null) checkChipByMinutes(15);
|
||||
}
|
||||
|
||||
private void addChip(String text, int minutes) {
|
||||
Chip chip = new Chip(getContext());
|
||||
chip.setText(text);
|
||||
chip.setTag(minutes);
|
||||
chip.setCheckable(true);
|
||||
chip.setClickable(true);
|
||||
chipGroupReminders.addView(chip);
|
||||
}
|
||||
|
||||
private List<Integer> getSelectedReminders() {
|
||||
List<Integer> selected = new ArrayList<>();
|
||||
for (int i = 0; i < chipGroupReminders.getChildCount(); i++) {
|
||||
Chip chip = (Chip) chipGroupReminders.getChildAt(i);
|
||||
if (chip.isChecked()) {
|
||||
selected.add((Integer) chip.getTag());
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
private void updateRecurrenceSpinner() {
|
||||
|
|
@ -353,19 +485,6 @@ public class CreateEventFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
private int getReminderMinutes() {
|
||||
int pos = spinnerReminder.getSelectedItemPosition();
|
||||
switch (pos) {
|
||||
case 1: return 10;
|
||||
case 2: return 15;
|
||||
case 3: return 30;
|
||||
case 4: return 60;
|
||||
case 5: return 120;
|
||||
case 6: return 1440;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void submitEvent() {
|
||||
String title = etTitle.getText().toString().trim();
|
||||
if (title.isEmpty()) {
|
||||
|
|
@ -384,33 +503,41 @@ public class CreateEventFragment extends Fragment {
|
|||
sdf.format(startCal.getTime()),
|
||||
sdf.format(endCal.getTime()),
|
||||
getCalendarSlug(),
|
||||
getReminderMinutes(),
|
||||
getSelectedReminders(),
|
||||
isAllDay,
|
||||
selectedRRule
|
||||
);
|
||||
|
||||
Toast.makeText(getContext(), "Oppretter...", Toast.LENGTH_SHORT).show();
|
||||
// HVIS VI REDIGERER, SETT ID
|
||||
if (eventToEdit != null) {
|
||||
req.id = eventToEdit.getId();
|
||||
}
|
||||
|
||||
// HER ER DEN NYE LOGIKKEN FOR Å LESE FEILMELDING FRA PHP
|
||||
RetrofitClient.getApiService().createCalendarEvent(req).enqueue(new Callback<JsonElement>() {
|
||||
Toast.makeText(getContext(), eventToEdit != null ? "Oppdaterer..." : "Oppretter...", Toast.LENGTH_SHORT).show();
|
||||
|
||||
// Velg riktig API-kall (Create vs Update)
|
||||
Call<JsonElement> call;
|
||||
if (eventToEdit != null) {
|
||||
call = RetrofitClient.getApiService().updateCalendarEvent(req);
|
||||
} else {
|
||||
call = RetrofitClient.getApiService().createCalendarEvent(req);
|
||||
}
|
||||
|
||||
call.enqueue(new Callback<JsonElement>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
|
||||
if (response.isSuccessful()) {
|
||||
Toast.makeText(getContext(), "Hendelse opprettet!", Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(getContext(), eventToEdit != null ? "Hendelse oppdatert!" : "Hendelse opprettet!", Toast.LENGTH_LONG).show();
|
||||
Navigation.findNavController(getView()).navigateUp();
|
||||
} else {
|
||||
// Les feilmelding fra serveren (body)
|
||||
String errorMsg = "Ukjent feil";
|
||||
try {
|
||||
if (response.errorBody() != null) {
|
||||
errorMsg = response.errorBody().string();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errorMsg = "Kunne ikke lese feil: " + e.getMessage();
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
|
||||
Log.e("KBS_ERROR", "Server svarte med feil: " + errorMsg);
|
||||
// Vis en kortversjon til brukeren, eller hele hvis du debugger
|
||||
Toast.makeText(getContext(), "Feil (" + response.code() + "): Sjekk Logcat", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
|
@ -422,5 +549,4 @@ public class CreateEventFragment extends Fragment {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
package com.kbs.kbsintranett;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.List;
|
||||
public class CreateEventRequest {
|
||||
@SerializedName("id")
|
||||
public String id;
|
||||
@SerializedName("title")
|
||||
public String title;
|
||||
|
||||
|
|
@ -9,7 +12,7 @@ public class CreateEventRequest {
|
|||
public String description;
|
||||
|
||||
@SerializedName("location")
|
||||
public String location; // NY
|
||||
public String location;
|
||||
|
||||
@SerializedName("start_time")
|
||||
public String startTime;
|
||||
|
|
@ -20,23 +23,24 @@ public class CreateEventRequest {
|
|||
@SerializedName("calendar_type")
|
||||
public String calendarType;
|
||||
|
||||
@SerializedName("reminder_minutes")
|
||||
public int reminderMinutes;
|
||||
@SerializedName("reminders")
|
||||
public List<Integer> reminders; // Liste, ikke int
|
||||
|
||||
@SerializedName("is_all_day")
|
||||
public boolean isAllDay; // NY
|
||||
public boolean isAllDay;
|
||||
|
||||
@SerializedName("recurrence")
|
||||
public String recurrence; // NY (RRULE)
|
||||
public String recurrence;
|
||||
|
||||
public CreateEventRequest(String title, String description, String location, String startTime, String endTime, String calendarType, int reminderMinutes, boolean isAllDay, String recurrence) {
|
||||
// Oppdatert konstruktør som tar imot List<Integer>
|
||||
public CreateEventRequest(String title, String description, String location, String startTime, String endTime, String calendarType, List<Integer> reminders, boolean isAllDay, String recurrence) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.location = location;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.calendarType = calendarType;
|
||||
this.reminderMinutes = reminderMinutes;
|
||||
this.reminders = reminders;
|
||||
this.isAllDay = isAllDay;
|
||||
this.recurrence = recurrence;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,8 @@ public class HomeFragment extends Fragment {
|
|||
}));
|
||||
} else {
|
||||
List<CalendarEvent> errorList = new ArrayList<>();
|
||||
errorList.add(new CalendarEvent("Kunne ikke laste kalender", "Sjekk nettverk", "!", "OBS"));
|
||||
// FIKSET HER: La til "" som 5. argument (location)
|
||||
errorList.add(new CalendarEvent("Kunne ikke laste kalender", "Sjekk nettverk", "!", "OBS", ""));
|
||||
recyclerView.setAdapter(new CalendarAdapter(errorList, null));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.app.AlarmManager;
|
|||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -18,6 +19,7 @@ import retrofit2.Response;
|
|||
|
||||
public class NotificationWorker extends Worker {
|
||||
private static final String TAG = "NotificationWorker";
|
||||
private static final String PREFS_NAME = "kbs_alarm_history";
|
||||
|
||||
public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
|
|
@ -28,20 +30,14 @@ public class NotificationWorker extends Worker {
|
|||
public Result doWork() {
|
||||
try {
|
||||
Response<List<CalendarEvent>> response = RetrofitClient.getApiService().getCalendarEvents().execute();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
scheduleAlarms(response.body());
|
||||
return Result.success();
|
||||
} else {
|
||||
// Ved klientfeil (4xx), stopp retry-loop
|
||||
if (response.code() >= 400 && response.code() < 500) {
|
||||
Log.w(TAG, "Kunne ikke hente kalender. Kode: " + response.code());
|
||||
return Result.failure();
|
||||
}
|
||||
if (response.code() >= 400 && response.code() < 500) return Result.failure();
|
||||
return Result.retry();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Nettverksfeil under kalendersjekk", e);
|
||||
return Result.retry();
|
||||
}
|
||||
}
|
||||
|
|
@ -49,18 +45,15 @@ public class NotificationWorker extends Worker {
|
|||
private void scheduleAlarms(List<CalendarEvent> events) {
|
||||
Context context = getApplicationContext();
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (!alarmManager.canScheduleExactAlarms()) {
|
||||
return; // Mangler rettigheter, kan ikke sette alarm
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) return;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||
|
||||
long catchUpWindow = now - (30 * 60 * 1000L); // 30 min bakover
|
||||
long futureWindow = now + (24 * 60 * 60 * 1000L); // 24 timer fremover
|
||||
long catchUpWindow = now - (30 * 60 * 1000L);
|
||||
long futureWindow = now + (30 * 24 * 60 * 60 * 1000L); // 30 dager frem
|
||||
|
||||
for (CalendarEvent event : events) {
|
||||
try {
|
||||
|
|
@ -72,24 +65,28 @@ public class NotificationWorker extends Worker {
|
|||
if (raw.length() > 19) raw = raw.substring(0, 19);
|
||||
eventDate = isoFormat.parse(raw);
|
||||
}
|
||||
|
||||
if (eventDate == null) continue;
|
||||
|
||||
int minutesBefore = event.getReminderMinutes();
|
||||
if (minutesBefore <= 0) continue;
|
||||
// Loop gjennom alle varsler for denne hendelsen
|
||||
for (int minutesBefore : event.getReminders()) {
|
||||
if (minutesBefore < 0) continue; // 0 betyr nå "ved start", negative ignoreres
|
||||
|
||||
long triggerTime = eventDate.getTime() - (minutesBefore * 60 * 1000L);
|
||||
|
||||
// Sjekk om alarmen er innenfor tidsvinduet
|
||||
if (triggerTime > catchUpWindow && triggerTime < futureWindow) {
|
||||
// Unik nøkkel for denne alarmen: EventID + Tidspunkt
|
||||
String alarmKey = "alarm_" + event.getId() + "_" + triggerTime;
|
||||
|
||||
// Catch-up: Hvis tiden har passert, fyr av umiddelbart
|
||||
if (triggerTime < now) {
|
||||
triggerTime = now + 1000;
|
||||
// Sjekk om vi allerede har fyrt denne alarmen
|
||||
if (prefs.getBoolean(alarmKey, false)) {
|
||||
continue; // Allerede håndtert
|
||||
}
|
||||
|
||||
int alarmId = (event.getTitle() + event.getRawDate()).hashCode();
|
||||
if (triggerTime > catchUpWindow && triggerTime < futureWindow) {
|
||||
if (triggerTime < now) {
|
||||
triggerTime = now + 1000; // Catch-up
|
||||
}
|
||||
|
||||
int alarmId = alarmKey.hashCode();
|
||||
Intent intent = new Intent(context, AlarmReceiver.class);
|
||||
intent.putExtra("TITLE", event.getTitle());
|
||||
String timeStr = new SimpleDateFormat("HH:mm", Locale.getDefault()).format(eventDate);
|
||||
|
|
@ -97,10 +94,7 @@ public class NotificationWorker extends Worker {
|
|||
intent.putExtra("ID", alarmId);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
alarmId,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
context, alarmId, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
|
|
@ -109,13 +103,16 @@ public class NotificationWorker extends Worker {
|
|||
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
||||
}
|
||||
|
||||
// Logger kun når en alarm faktisk blir satt/oppdatert
|
||||
Log.i(TAG, "Alarm satt for: " + event.getTitle() + " (" + new Date(triggerTime) + ")");
|
||||
// Marker som satt
|
||||
prefs.edit().putBoolean(alarmKey, true).apply();
|
||||
Log.i(TAG, "Satt alarm: " + event.getTitle() + " (" + minutesBefore + "m før)");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Feil", e);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Feil ved behandling av event", e);
|
||||
}
|
||||
}
|
||||
// Rensk opp gamle nøkler (valgfritt, for å spare plass over tid)
|
||||
}
|
||||
}
|
||||
|
|
@ -68,4 +68,11 @@ public interface WordPressApiService {
|
|||
@GET("wp-json/kbs/v1/lookup-id")
|
||||
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")
|
||||
Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request); // Sender kun ID og cal_type
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
android:orientation="vertical"
|
||||
android:padding="24dp"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sheet_title"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -45,15 +44,43 @@
|
|||
android:text="Beskrivelse..."
|
||||
android:textSize="14sp"
|
||||
android:textColor="#555"
|
||||
android:layout_marginBottom="32dp"/>
|
||||
android:layout_marginBottom="24dp"/>
|
||||
|
||||
<!-- ADMIN KNAPPER (Vises kun for admin) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_admin_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_delete"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Slett"
|
||||
android:backgroundTint="#D32F2F"
|
||||
android:textColor="#FFF"
|
||||
android:layout_marginEnd="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_edit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Endre"
|
||||
android:backgroundTint="@color/kbs_logo_blue"
|
||||
android:textColor="#FFF"
|
||||
android:layout_marginStart="8dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Gammel knapp (skjult) -->
|
||||
<Button
|
||||
android:id="@+id/btn_add_to_calendar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Lagre i min kalender / Varsle meg"
|
||||
android:backgroundTint="@color/kbs_logo_blue"
|
||||
android:textColor="@color/white"
|
||||
android:padding="12dp"/>
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FFFFFF">
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
android:layout_marginBottom="24dp"
|
||||
android:textColor="#333"/>
|
||||
|
||||
<!-- TITTEL -->
|
||||
<EditText
|
||||
android:id="@+id/et_title"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -30,6 +32,7 @@
|
|||
android:background="@android:drawable/edit_text"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- BESKRIVELSE -->
|
||||
<EditText
|
||||
android:id="@+id/et_desc"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -42,6 +45,7 @@
|
|||
android:background="@android:drawable/edit_text"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- STED -->
|
||||
<EditText
|
||||
android:id="@+id/et_location"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -52,6 +56,22 @@
|
|||
android:background="@android:drawable/edit_text"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- KALENDER VALG -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Velg Kalender:"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_calendar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:padding="12dp"/>
|
||||
|
||||
<!-- HELE DAGEN -->
|
||||
<Switch
|
||||
android:id="@+id/switch_all_day"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -59,7 +79,7 @@
|
|||
android:text="Hele dagen"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Start Dato/Tid -->
|
||||
<!-- START DATO/TID -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -83,7 +103,7 @@
|
|||
android:layout_marginStart="4dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Slutt Dato/Tid -->
|
||||
<!-- SLUTT DATO/TID -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -116,20 +136,7 @@
|
|||
android:textStyle="bold"
|
||||
android:layout_marginBottom="24dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Velg Kalender:"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_calendar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:padding="12dp"/>
|
||||
|
||||
<!-- GJENTAKELSE -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -144,19 +151,31 @@
|
|||
android:layout_marginBottom="16dp"
|
||||
android:padding="12dp"/>
|
||||
|
||||
<!-- NYTT: VARSLING MED CHIPS (MULTIVALG) -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Varsling:"
|
||||
android:text="Varsling (Velg en eller flere):"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666"/>
|
||||
android:textColor="#666"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_reminder"
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:padding="12dp"/>
|
||||
android:scrollbars="none"
|
||||
android:layout_marginBottom="32dp">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chip_group_reminders"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:singleLine="true"
|
||||
app:selectionRequired="false">
|
||||
<!-- Chips legges til programmatisk i Java -->
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_save_event"
|
||||
|
|
@ -167,4 +186,5 @@
|
|||
android:textColor="#FFFFFF"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
Loading…
Reference in a new issue