Før individuelle avtaler
This commit is contained in:
parent
864820212f
commit
93092f33d9
11 changed files with 399 additions and 168 deletions
|
|
@ -7,56 +7,38 @@ import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.work.Worker;
|
|
||||||
import androidx.work.WorkerParameters;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import retrofit2.Response;
|
|
||||||
|
|
||||||
public class NotificationWorker extends Worker {
|
public class AlarmScheduler {
|
||||||
private static final String TAG = "NotificationWorker";
|
|
||||||
|
private static final String TAG = "AlarmScheduler";
|
||||||
private static final String PREFS_NAME = "kbs_alarm_history";
|
private static final String PREFS_NAME = "kbs_alarm_history";
|
||||||
|
|
||||||
public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
/**
|
||||||
super(context, workerParams);
|
* Denne metoden går gjennom en liste hendelser og setter alarmer for dem.
|
||||||
}
|
*/
|
||||||
|
public static void scheduleAlarmsForEvents(Context context, List<CalendarEvent> events) {
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
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 {
|
|
||||||
if (response.code() >= 400 && response.code() < 500) return Result.failure();
|
|
||||||
return Result.retry();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
return Result.retry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scheduleAlarms(List<CalendarEvent> events) {
|
|
||||||
Context context = getApplicationContext();
|
|
||||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||||
|
|
||||||
|
// Sjekk rettigheter for Android 12+ (Exact Alarm)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) {
|
||||||
|
Log.w(TAG, "Mangler rettighet til å sette nøyaktige alarmer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) return;
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||||
|
|
||||||
long catchUpWindow = now - (30 * 60 * 1000L);
|
// Vi ser etter hendelser 30 dager frem i tid
|
||||||
long futureWindow = now + (30 * 24 * 60 * 60 * 1000L); // 30 dager frem
|
long futureWindow = now + (30L * 24 * 60 * 60 * 1000L);
|
||||||
|
|
||||||
for (CalendarEvent event : events) {
|
for (CalendarEvent event : events) {
|
||||||
try {
|
try {
|
||||||
|
// Hopp over hvis ingen dato eller heldags (uten tidspunkt)
|
||||||
if (event.getRawDate() == null || event.getRawDate().length() == 10) continue;
|
if (event.getRawDate() == null || event.getRawDate().length() == 10) continue;
|
||||||
|
|
||||||
Date eventDate = null;
|
Date eventDate = null;
|
||||||
|
|
@ -65,28 +47,26 @@ public class NotificationWorker extends Worker {
|
||||||
if (raw.length() > 19) raw = raw.substring(0, 19);
|
if (raw.length() > 19) raw = raw.substring(0, 19);
|
||||||
eventDate = isoFormat.parse(raw);
|
eventDate = isoFormat.parse(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventDate == null) continue;
|
if (eventDate == null) continue;
|
||||||
|
|
||||||
// Loop gjennom alle varsler for denne hendelsen
|
// Loop gjennom alle varsler (f.eks. 15 min før, 60 min før)
|
||||||
for (int minutesBefore : event.getReminders()) {
|
for (int minutesBefore : event.getReminders()) {
|
||||||
if (minutesBefore < 0) continue; // 0 betyr nå "ved start", negative ignoreres
|
if (minutesBefore < 0) continue;
|
||||||
|
|
||||||
long triggerTime = eventDate.getTime() - (minutesBefore * 60 * 1000L);
|
long triggerTime = eventDate.getTime() - (minutesBefore * 60 * 1000L);
|
||||||
|
|
||||||
// Unik nøkkel for denne alarmen: EventID + Tidspunkt
|
|
||||||
String alarmKey = "alarm_" + event.getId() + "_" + triggerTime;
|
String alarmKey = "alarm_" + event.getId() + "_" + triggerTime;
|
||||||
|
|
||||||
// Sjekk om vi allerede har fyrt denne alarmen
|
// Hvis tidspunktet er i fremtiden (og innenfor vinduet)
|
||||||
|
if (triggerTime > now && triggerTime < futureWindow) {
|
||||||
|
|
||||||
|
// Sjekk om vi allerede har satt denne alarmen for å unngå dobbeltarbeid
|
||||||
if (prefs.getBoolean(alarmKey, false)) {
|
if (prefs.getBoolean(alarmKey, false)) {
|
||||||
continue; // Allerede håndtert
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (triggerTime > catchUpWindow && triggerTime < futureWindow) {
|
int alarmId = alarmKey.hashCode(); // Unik ID basert på hendelse+tid
|
||||||
if (triggerTime < now) {
|
|
||||||
triggerTime = now + 1000; // Catch-up
|
|
||||||
}
|
|
||||||
|
|
||||||
int alarmId = alarmKey.hashCode();
|
|
||||||
Intent intent = new Intent(context, AlarmReceiver.class);
|
Intent intent = new Intent(context, AlarmReceiver.class);
|
||||||
intent.putExtra("TITLE", event.getTitle());
|
intent.putExtra("TITLE", event.getTitle());
|
||||||
String timeStr = new SimpleDateFormat("HH:mm", Locale.getDefault()).format(eventDate);
|
String timeStr = new SimpleDateFormat("HH:mm", Locale.getDefault()).format(eventDate);
|
||||||
|
|
@ -94,9 +74,13 @@ public class NotificationWorker extends Worker {
|
||||||
intent.putExtra("ID", alarmId);
|
intent.putExtra("ID", alarmId);
|
||||||
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||||
context, alarmId, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
context,
|
||||||
|
alarmId,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Sett alarmen
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -105,14 +89,12 @@ public class NotificationWorker extends Worker {
|
||||||
|
|
||||||
// Marker som satt
|
// Marker som satt
|
||||||
prefs.edit().putBoolean(alarmKey, true).apply();
|
prefs.edit().putBoolean(alarmKey, true).apply();
|
||||||
Log.i(TAG, "Satt alarm: " + event.getTitle() + " (" + minutesBefore + "m før)");
|
Log.d(TAG, "Alarm satt for " + event.getTitle() + " om " + minutesBefore + " min.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Feil", e);
|
Log.e(TAG, "Feil ved setting av alarm", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rensk opp gamle nøkler (valgfritt, for å spare plass over tid)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package com.kbs.kbsintranett;
|
package com.kbs.kbsintranett;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
@ -57,6 +58,12 @@ public class AuthRepository {
|
||||||
// Lagre listen over skrivbare kalendere
|
// Lagre listen over skrivbare kalendere
|
||||||
UserManager.getInstance().setWriteableCalendars(response.body().writeableCalendars);
|
UserManager.getInstance().setWriteableCalendars(response.body().writeableCalendars);
|
||||||
|
|
||||||
|
// NYTT: Hvis vi har en ventende FCM-token, send den nå som vi er logget inn
|
||||||
|
String pendingToken = UserManager.getInstance().getFcmToken();
|
||||||
|
if (pendingToken != null && !pendingToken.isEmpty()) {
|
||||||
|
updateDeviceToken(pendingToken);
|
||||||
|
}
|
||||||
|
|
||||||
callback.onSuccess(role);
|
callback.onSuccess(role);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -72,4 +79,33 @@ public class AuthRepository {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sender FCM-token til WordPress for å registrere enheten for push-varsler.
|
||||||
|
*/
|
||||||
|
public static void updateDeviceToken(String token) {
|
||||||
|
if (!UserManager.getInstance().isLoggedIn()) {
|
||||||
|
// Hvis ikke logget inn, bare lagre den til senere
|
||||||
|
UserManager.getInstance().setFcmToken(token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send til server
|
||||||
|
RegisterDeviceRequest request = new RegisterDeviceRequest(token);
|
||||||
|
RetrofitClient.getApiService().registerDevice(request).enqueue(new Callback<JsonElement>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Log.d(TAG, "FCM Token registrert på server OK.");
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Feil ved registrering av FCM Token. Kode: " + response.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<JsonElement> call, Throwable t) {
|
||||||
|
Log.e(TAG, "Nettverksfeil ved sending av FCM token", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import java.util.List;
|
||||||
public class CalendarEvent implements Serializable {
|
public class CalendarEvent implements Serializable {
|
||||||
@SerializedName("id")
|
@SerializedName("id")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@SerializedName("title")
|
@SerializedName("title")
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
|
@ -66,6 +67,8 @@ public class CalendarEvent implements Serializable {
|
||||||
public void setCalendarColor(String color) { this.calendarColor = color; }
|
public void setCalendarColor(String color) { this.calendarColor = color; }
|
||||||
|
|
||||||
// --- KOMPATIBILITETS-METODER ---
|
// --- KOMPATIBILITETS-METODER ---
|
||||||
|
|
||||||
|
// Denne brukes for enkle varsler
|
||||||
public void setReminderMinutes(int minutes) {
|
public void setReminderMinutes(int minutes) {
|
||||||
this.reminders = new ArrayList<>();
|
this.reminders = new ArrayList<>();
|
||||||
if (minutes > 0) {
|
if (minutes > 0) {
|
||||||
|
|
@ -73,6 +76,12 @@ public class CalendarEvent implements Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NY METODE (Den som manglet og forårsaket krasj)
|
||||||
|
// Lar oss sette hele listen med varsler på en gang
|
||||||
|
public void setRemindersList(List<Integer> reminders) {
|
||||||
|
this.reminders = reminders != null ? new ArrayList<>(reminders) : new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
public int getReminderMinutes() {
|
public int getReminderMinutes() {
|
||||||
if (reminders != null && !reminders.isEmpty()) {
|
if (reminders != null && !reminders.isEmpty()) {
|
||||||
return reminders.get(0);
|
return reminders.get(0);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.kbs.kbsintranett;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.DatePickerDialog;
|
import android.app.DatePickerDialog;
|
||||||
import android.app.TimePickerDialog;
|
import android.app.TimePickerDialog;
|
||||||
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
@ -33,8 +34,6 @@ import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
@ -117,7 +116,6 @@ public class CreateEventFragment extends Fragment {
|
||||||
spinnerRecurrence.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
spinnerRecurrence.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
// Unngå å overskrive ved innlasting hvis vi allerede har satt en verdi
|
|
||||||
if (eventToEdit != null && position == 0 && selectedRRule != null) return;
|
if (eventToEdit != null && position == 0 && selectedRRule != null) return;
|
||||||
|
|
||||||
String selected = parent.getItemAtPosition(position).toString();
|
String selected = parent.getItemAtPosition(position).toString();
|
||||||
|
|
@ -140,7 +138,6 @@ public class CreateEventFragment extends Fragment {
|
||||||
etDesc.setText(cleanDesc);
|
etDesc.setText(cleanDesc);
|
||||||
etLocation.setText(event.getLocation());
|
etLocation.setText(event.getLocation());
|
||||||
|
|
||||||
// --- FIKS 404 FEIL VED OPPDATERING ---
|
|
||||||
ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinnerCalendar.getAdapter();
|
ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinnerCalendar.getAdapter();
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
int position = adapter.getPosition(event.getCalendarName());
|
int position = adapter.getPosition(event.getCalendarName());
|
||||||
|
|
@ -149,7 +146,6 @@ public class CreateEventFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spinnerCalendar.setEnabled(false);
|
spinnerCalendar.setEnabled(false);
|
||||||
// -------------------------------------
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||||
|
|
@ -210,17 +206,14 @@ public class CreateEventFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NY LOGIKK FOR FARGER I SPINNER ---
|
|
||||||
|
|
||||||
private String getCalendarColor(String name) {
|
private String getCalendarColor(String name) {
|
||||||
// Matcher fargene i PHP-config (V12.6)
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "Felles": return "#0069B3"; // KBS Blå
|
case "Felles": return "#0069B3";
|
||||||
case "Administrasjonen": return "#607D8B"; // Blue Grey
|
case "Administrasjonen": return "#607D8B";
|
||||||
case "Serviceavdelingen": return "#E65100"; // Orange
|
case "Serviceavdelingen": return "#E65100";
|
||||||
case "Automasjonsavdelingen": return "#2E7D32"; // Green
|
case "Automasjonsavdelingen": return "#2E7D32";
|
||||||
case "Prosjektavdelingen": return "#7B1FA2"; // Purple
|
case "Prosjektavdelingen": return "#7B1FA2";
|
||||||
default: return "#888888"; // Grå fallback
|
default: return "#888888";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,41 +221,27 @@ public class CreateEventFragment extends Fragment {
|
||||||
List<String> calendars = UserManager.getInstance().getWriteableCalendars();
|
List<String> calendars = UserManager.getInstance().getWriteableCalendars();
|
||||||
if (calendars.isEmpty()) calendars.add("Felles");
|
if (calendars.isEmpty()) calendars.add("Felles");
|
||||||
|
|
||||||
// Vi bruker en Custom Adapter for å styre farger
|
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_spinner_item, calendars) {
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_spinner_item, calendars) {
|
||||||
|
|
||||||
// getView: Dette er det som vises i selve boksen når noe er valgt
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||||
TextView view = (TextView) super.getView(position, convertView, parent);
|
TextView view = (TextView) super.getView(position, convertView, parent);
|
||||||
|
|
||||||
String calName = getItem(position);
|
String calName = getItem(position);
|
||||||
String colorHex = getCalendarColor(calName);
|
String colorHex = getCalendarColor(calName);
|
||||||
|
|
||||||
// Sett bakgrunnsfarge lik kalenderfarge
|
|
||||||
view.setBackgroundColor(Color.parseColor(colorHex));
|
view.setBackgroundColor(Color.parseColor(colorHex));
|
||||||
|
|
||||||
// Hvit tekst for kontrast
|
|
||||||
view.setTextColor(Color.WHITE);
|
view.setTextColor(Color.WHITE);
|
||||||
view.setTypeface(null, Typeface.BOLD);
|
view.setTypeface(null, Typeface.BOLD);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDropDownView: Dette er listen som popper opp
|
|
||||||
@Override
|
@Override
|
||||||
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
||||||
TextView view = (TextView) super.getDropDownView(position, convertView, parent);
|
TextView view = (TextView) super.getDropDownView(position, convertView, parent);
|
||||||
|
|
||||||
String calName = getItem(position);
|
String calName = getItem(position);
|
||||||
String colorHex = getCalendarColor(calName);
|
String colorHex = getCalendarColor(calName);
|
||||||
|
|
||||||
// Her holder vi bakgrunnen hvit, men farger teksten
|
|
||||||
view.setBackgroundColor(Color.WHITE);
|
view.setBackgroundColor(Color.WHITE);
|
||||||
view.setTextColor(Color.parseColor(colorHex));
|
view.setTextColor(Color.parseColor(colorHex));
|
||||||
view.setTypeface(null, Typeface.BOLD);
|
view.setTypeface(null, Typeface.BOLD);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -270,7 +249,6 @@ public class CreateEventFragment extends Fragment {
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
spinnerCalendar.setAdapter(adapter);
|
spinnerCalendar.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
// --------------------------------------
|
|
||||||
|
|
||||||
private void setupReminderChips() {
|
private void setupReminderChips() {
|
||||||
addChip("Ved start", 0);
|
addChip("Ved start", 0);
|
||||||
|
|
@ -539,16 +517,15 @@ public class CreateEventFragment extends Fragment {
|
||||||
String format = isAllDay ? "yyyy-MM-dd" : "yyyy-MM-dd'T'HH:mm";
|
String format = isAllDay ? "yyyy-MM-dd" : "yyyy-MM-dd'T'HH:mm";
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
|
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
|
||||||
|
|
||||||
|
String startTimeStr = sdf.format(startCal.getTime());
|
||||||
|
String endTimeStr = sdf.format(endCal.getTime());
|
||||||
|
String location = etLocation.getText().toString();
|
||||||
|
String description = etDesc.getText().toString();
|
||||||
|
List<Integer> reminders = getSelectedReminders();
|
||||||
|
|
||||||
CreateEventRequest req = new CreateEventRequest(
|
CreateEventRequest req = new CreateEventRequest(
|
||||||
title,
|
title, description, location, startTimeStr, endTimeStr,
|
||||||
etDesc.getText().toString(),
|
getCalendarSlug(), reminders, isAllDay, selectedRRule
|
||||||
etLocation.getText().toString(),
|
|
||||||
sdf.format(startCal.getTime()),
|
|
||||||
sdf.format(endCal.getTime()),
|
|
||||||
getCalendarSlug(),
|
|
||||||
getSelectedReminders(),
|
|
||||||
isAllDay,
|
|
||||||
selectedRRule
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (eventToEdit != null) {
|
if (eventToEdit != null) {
|
||||||
|
|
@ -557,6 +534,10 @@ public class CreateEventFragment extends Fragment {
|
||||||
|
|
||||||
Toast.makeText(getContext(), eventToEdit != null ? "Oppdaterer..." : "Oppretter...", Toast.LENGTH_SHORT).show();
|
Toast.makeText(getContext(), eventToEdit != null ? "Oppdaterer..." : "Oppretter...", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
// **VIKTIG ENDRING:**
|
||||||
|
// Vi henter context her (mens Fragmentet lever) for å bruke den i bakgrunnstråden
|
||||||
|
final Context appContext = requireContext().getApplicationContext();
|
||||||
|
|
||||||
Call<JsonElement> call;
|
Call<JsonElement> call;
|
||||||
if (eventToEdit != null) {
|
if (eventToEdit != null) {
|
||||||
call = RetrofitClient.getApiService().updateCalendarEvent(req);
|
call = RetrofitClient.getApiService().updateCalendarEvent(req);
|
||||||
|
|
@ -569,25 +550,38 @@ public class CreateEventFragment extends Fragment {
|
||||||
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
|
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
Toast.makeText(getContext(), eventToEdit != null ? "Hendelse oppdatert!" : "Hendelse opprettet!", Toast.LENGTH_LONG).show();
|
Toast.makeText(getContext(), eventToEdit != null ? "Hendelse oppdatert!" : "Hendelse opprettet!", Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
// Start oppdatering av alarmer i bakgrunnen
|
||||||
|
fetchCalendarAndSchedule(appContext);
|
||||||
|
|
||||||
Navigation.findNavController(getView()).navigateUp();
|
Navigation.findNavController(getView()).navigateUp();
|
||||||
} else {
|
} else {
|
||||||
String errorMsg = "Ukjent feil";
|
Toast.makeText(getContext(), "Feil (" + response.code() + ")", Toast.LENGTH_LONG).show();
|
||||||
try {
|
|
||||||
if (response.errorBody() != null) {
|
|
||||||
errorMsg = response.errorBody().string();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {}
|
|
||||||
|
|
||||||
Log.e("KBS_ERROR", "Server svarte med feil: " + errorMsg);
|
|
||||||
Toast.makeText(getContext(), "Feil (" + response.code() + "): Sjekk Logcat", Toast.LENGTH_LONG).show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<JsonElement> call, Throwable t) {
|
public void onFailure(Call<JsonElement> call, Throwable t) {
|
||||||
Log.e("KBS_ERROR", "Nettverksfeil", t);
|
Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
|
||||||
Toast.makeText(getContext(), "Nettverksfeil: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Oppdatert metode som tar imot context som parameter
|
||||||
|
private void fetchCalendarAndSchedule(Context context) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
// Sjekk en ekstra gang for å være sikker
|
||||||
|
if (context == null) return;
|
||||||
|
|
||||||
|
Response<List<CalendarEvent>> response = RetrofitClient.getApiService().getCalendarEvents().execute();
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
// Bruk contexten vi fikk tilsendt, ikke getContext() som kan være null
|
||||||
|
AlarmScheduler.scheduleAlarmsForEvents(context, response.body());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("CreateEvent", "Kunne ikke oppdatere alarmer", e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -22,15 +22,12 @@ 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 androidx.work.PeriodicWorkRequest;
|
|
||||||
import androidx.work.WorkManager;
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
@ -55,7 +52,7 @@ public class HomeFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
startNotificationWorker();
|
// GAMMEL METODE FJERNET HERFRA (startNotificationWorker)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
@ -282,11 +279,4 @@ public class HomeFragment extends Fragment {
|
||||||
});
|
});
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startNotificationWorker() {
|
|
||||||
PeriodicWorkRequest notifRequest =
|
|
||||||
new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
|
|
||||||
.build();
|
|
||||||
WorkManager.getInstance(requireContext()).enqueue(notifRequest);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.kbs.kbsintranett;
|
package com.kbs.kbsintranett;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
|
|
@ -22,9 +21,6 @@ import androidx.core.content.ContextCompat;
|
||||||
import androidx.navigation.NavController;
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.navigation.ui.NavigationUI;
|
import androidx.navigation.ui.NavigationUI;
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
|
||||||
import androidx.work.PeriodicWorkRequest;
|
|
||||||
import androidx.work.WorkManager;
|
|
||||||
|
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn;
|
import com.google.android.gms.auth.api.signin.GoogleSignIn;
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
||||||
|
|
@ -32,8 +28,6 @@ import com.google.android.gms.auth.api.signin.GoogleSignInClient;
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
|
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
public static final String GOOGLE_WEB_CLIENT_ID = "738325360287-cidl3plnqv9ei74vm9vm5muustj6eenb.apps.googleusercontent.com"; // Bytt med din egen hvis denne er feil
|
public static final String GOOGLE_WEB_CLIENT_ID = "738325360287-cidl3plnqv9ei74vm9vm5muustj6eenb.apps.googleusercontent.com"; // Bytt med din egen hvis denne er feil
|
||||||
|
|
@ -60,10 +54,8 @@ public class MainActivity extends AppCompatActivity {
|
||||||
if (bottomNav != null) {
|
if (bottomNav != null) {
|
||||||
NavigationUI.setupWithNavController(bottomNav, navController);
|
NavigationUI.setupWithNavController(bottomNav, navController);
|
||||||
|
|
||||||
// --- NYTT: Håndter "Reselection" (Klikk på fanen man allerede er i) ---
|
// Håndter "Reselection" (Klikk på fanen man allerede er i)
|
||||||
bottomNav.setOnItemReselectedListener(item -> {
|
bottomNav.setOnItemReselectedListener(item -> {
|
||||||
// Dette fjerner alt som ligger "oppå" hovedsiden i stabelen.
|
|
||||||
// F.eks: Hjem -> Kalender Full -> (Klikk Hjem) -> Hjem
|
|
||||||
navController.popBackStack(item.getItemId(), false);
|
navController.popBackStack(item.getItemId(), false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +86,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
checkNotificationPermission();
|
checkNotificationPermission();
|
||||||
checkExactAlarmPermission();
|
checkExactAlarmPermission();
|
||||||
|
|
||||||
scheduleCalendarWork();
|
// GAMMEL METODE FJERNET HERFRA (NotificationWorker)
|
||||||
|
|
||||||
// --- 3. AUTENTISERING ---
|
// --- 3. AUTENTISERING ---
|
||||||
checkLoginState();
|
checkLoginState();
|
||||||
|
|
@ -174,8 +166,8 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private void checkNotificationPermission() {
|
private void checkNotificationPermission() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
|
requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,15 +189,4 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleCalendarWork() {
|
|
||||||
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
|
|
||||||
"KbsCalendarWork",
|
|
||||||
ExistingPeriodicWorkPolicy.UPDATE,
|
|
||||||
workRequest
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -15,51 +15,47 @@ import androidx.core.app.NotificationManagerCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||||
import com.google.firebase.messaging.RemoteMessage;
|
import com.google.firebase.messaging.RemoteMessage;
|
||||||
|
import java.util.List;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
|
|
||||||
private static final String TAG = "FCMService";
|
private static final String TAG = "FCMService";
|
||||||
private static final String CHANNEL_ID = "kbs_calendar_channel"; // Samme kanal som før
|
private static final String CHANNEL_ID = "kbs_calendar_channel";
|
||||||
|
|
||||||
/**
|
|
||||||
* Kalles når en ny token genereres (f.eks. ved ny installasjon).
|
|
||||||
* Denne tokenen MÅ sendes til WordPress-backend slik at serveren vet hvem den skal sende til.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewToken(@NonNull String token) {
|
public void onNewToken(@NonNull String token) {
|
||||||
super.onNewToken(token);
|
super.onNewToken(token);
|
||||||
Log.d(TAG, "Ny FCM Token: " + token);
|
Log.d(TAG, "Ny FCM Token: " + token);
|
||||||
|
AuthRepository.updateDeviceToken(token);
|
||||||
// TODO: Send denne tokenen til din WordPress-backend via AuthRepository eller RetrofitClient.
|
|
||||||
// F.eks: AuthRepository.updateDeviceToken(token);
|
|
||||||
// Vi lagrer den midlertidig i UserManager eller SharedPreferences hvis brukeren ikke er logget inn enda.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Kalles når en melding mottas mens appen er i forgrunnen,
|
|
||||||
* ELLER hvis det er en "Data Message" (som er det vi bør bruke for bakgrunnsoppdateringer).
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
|
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
|
||||||
super.onMessageReceived(remoteMessage);
|
super.onMessageReceived(remoteMessage);
|
||||||
|
|
||||||
Log.d(TAG, "Melding mottatt fra: " + remoteMessage.getFrom());
|
Log.d(TAG, "Melding mottatt fra: " + remoteMessage.getFrom());
|
||||||
|
|
||||||
// Sjekk om meldingen inneholder data (payload)
|
// 1. Sjekk data payload (Bakgrunnsoppdatering)
|
||||||
if (remoteMessage.getData().size() > 0) {
|
if (remoteMessage.getData().size() > 0) {
|
||||||
Log.d(TAG, "Melding data payload: " + remoteMessage.getData());
|
String forceRefresh = remoteMessage.getData().get("force_refresh");
|
||||||
// Her kan du trigge en oppdatering av kalenderen i bakgrunnen uten å vise varsel,
|
|
||||||
// eller vise et varsel basert på dataene.
|
|
||||||
|
|
||||||
|
if ("true".equalsIgnoreCase(forceRefresh)) {
|
||||||
|
Log.d(TAG, "Mottok 'force_refresh' - oppdaterer kalender og alarmer...");
|
||||||
|
updateCalendarAndAlarms();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hvis meldingen også har egne titler i data-feltet (valgfritt)
|
||||||
String title = remoteMessage.getData().get("title");
|
String title = remoteMessage.getData().get("title");
|
||||||
String body = remoteMessage.getData().get("body");
|
String body = remoteMessage.getData().get("body");
|
||||||
|
|
||||||
if (title != null && body != null) {
|
if (title != null && body != null) {
|
||||||
showNotification(title, body);
|
showNotification(title, body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sjekk om meldingen er en ren varslingsmelding (Notification payload)
|
// 2. Sjekk notification payload (Vises automatisk når app er i bakgrunn, men vi håndterer den her for forgrunn)
|
||||||
if (remoteMessage.getNotification() != null) {
|
if (remoteMessage.getNotification() != null) {
|
||||||
Log.d(TAG, "Melding varsel body: " + remoteMessage.getNotification().getBody());
|
Log.d(TAG, "Melding varsel body: " + remoteMessage.getNotification().getBody());
|
||||||
showNotification(
|
showNotification(
|
||||||
|
|
@ -69,11 +65,31 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateCalendarAndAlarms() {
|
||||||
|
// Vi bruker Retrofit for å hente kalenderen på nytt
|
||||||
|
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
// Lagre til cache først (god praksis)
|
||||||
|
CacheManager.saveCalendarEvents(getApplicationContext(), response.body());
|
||||||
|
|
||||||
|
// Oppdater alarmer lokalt
|
||||||
|
AlarmScheduler.scheduleAlarmsForEvents(getApplicationContext(), response.body());
|
||||||
|
Log.d(TAG, "Kalender og alarmer oppdatert via Push.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
||||||
|
Log.e(TAG, "Feil ved push-oppdatering av kalender", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void showNotification(String title, String message) {
|
private void showNotification(String title, String message) {
|
||||||
// Gjenbruk logikk for kanalopprettelse (sikkerhetsnett)
|
|
||||||
createNotificationChannel();
|
createNotificationChannel();
|
||||||
|
|
||||||
// Sjekk rettigheter for Android 13+
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -99,7 +115,6 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
.setAutoCancel(true);
|
.setAutoCancel(true);
|
||||||
|
|
||||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||||
// Bruk systemtid som ID for å unngå at varsler overskriver hverandre, eller en fast ID hvis ønskelig
|
|
||||||
notificationManager.notify((int) System.currentTimeMillis(), builder.build());
|
notificationManager.notify((int) System.currentTimeMillis(), builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.kbs.kbsintranett;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class RegisterDeviceRequest {
|
||||||
|
@SerializedName("fcm_token")
|
||||||
|
public String fcmToken;
|
||||||
|
|
||||||
|
@SerializedName("platform")
|
||||||
|
public String platform;
|
||||||
|
|
||||||
|
public RegisterDeviceRequest(String fcmToken) {
|
||||||
|
this.fcmToken = fcmToken;
|
||||||
|
this.platform = "android";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,9 @@ public class UserManager {
|
||||||
private String stilling;
|
private String stilling;
|
||||||
private String mobiltelefon;
|
private String mobiltelefon;
|
||||||
|
|
||||||
|
// FCM Token (Push)
|
||||||
|
private String fcmToken;
|
||||||
|
|
||||||
// NYTT:
|
// NYTT:
|
||||||
private List<String> writeableCalendars = new ArrayList<>();
|
private List<String> writeableCalendars = new ArrayList<>();
|
||||||
|
|
||||||
|
|
@ -79,6 +82,9 @@ public class UserManager {
|
||||||
public String getStilling() { return stilling != null ? stilling : ""; }
|
public String getStilling() { return stilling != null ? stilling : ""; }
|
||||||
public String getMobiltelefon() { return mobiltelefon != null ? mobiltelefon : ""; }
|
public String getMobiltelefon() { return mobiltelefon != null ? mobiltelefon : ""; }
|
||||||
|
|
||||||
|
public void setFcmToken(String token) { this.fcmToken = token; }
|
||||||
|
public String getFcmToken() { return fcmToken; }
|
||||||
|
|
||||||
public boolean isLoggedIn() { return userEmail != null && !userEmail.isEmpty(); }
|
public boolean isLoggedIn() { return userEmail != null && !userEmail.isEmpty(); }
|
||||||
public boolean isAdmin() { return "administrator".equalsIgnoreCase(userRole); }
|
public boolean isAdmin() { return "administrator".equalsIgnoreCase(userRole); }
|
||||||
public boolean isEditorOrAbove() {
|
public boolean isEditorOrAbove() {
|
||||||
|
|
@ -98,5 +104,6 @@ public class UserManager {
|
||||||
stilling = null;
|
stilling = null;
|
||||||
mobiltelefon = null;
|
mobiltelefon = null;
|
||||||
writeableCalendars.clear();
|
writeableCalendars.clear();
|
||||||
|
// Vi sletter ikke fcmToken ved logout, da enheten fortsatt er den samme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,6 @@ public interface WordPressApiService {
|
||||||
@GET("wp-json/kbs/v1/calendar/events")
|
@GET("wp-json/kbs/v1/calendar/events")
|
||||||
Call<List<CalendarEvent>> getCalendarEvents();
|
Call<List<CalendarEvent>> getCalendarEvents();
|
||||||
|
|
||||||
// DETTE ER METODEN SOM MANGLER:
|
|
||||||
@POST("wp-json/kbs/v1/calendar/create")
|
@POST("wp-json/kbs/v1/calendar/create")
|
||||||
Call<JsonElement> createCalendarEvent(@Body CreateEventRequest request);
|
Call<JsonElement> createCalendarEvent(@Body CreateEventRequest request);
|
||||||
|
|
||||||
|
|
@ -72,7 +71,9 @@ public interface WordPressApiService {
|
||||||
Call<JsonElement> updateCalendarEvent(@Body CreateEventRequest request);
|
Call<JsonElement> updateCalendarEvent(@Body CreateEventRequest request);
|
||||||
|
|
||||||
@POST("wp-json/kbs/v1/calendar/delete")
|
@POST("wp-json/kbs/v1/calendar/delete")
|
||||||
Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request); // Sender kun ID og cal_type
|
Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request);
|
||||||
|
|
||||||
|
|
||||||
|
// NYTT: Registrer enhet for push-varsler
|
||||||
|
@POST("wp-json/kbs/v1/device/register")
|
||||||
|
Call<JsonElement> registerDevice(@Body RegisterDeviceRequest request);
|
||||||
}
|
}
|
||||||
|
|
@ -58,8 +58,8 @@ android {
|
||||||
applicationId = "com.kbs.kbsintranett"
|
applicationId = "com.kbs.kbsintranett"
|
||||||
minSdk = 28
|
minSdk = 28
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 3
|
versionCode = 4
|
||||||
versionName = "1.4"
|
versionName = "1.5.0"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
@ -117,8 +117,10 @@ dependencies {
|
||||||
|
|
||||||
// NY LINJE: (Valgfritt, men lurt for statistikk)
|
// NY LINJE: (Valgfritt, men lurt for statistikk)
|
||||||
implementation("com.google.firebase:firebase-analytics")
|
implementation("com.google.firebase:firebase-analytics")
|
||||||
}
|
|
||||||
|
|
||||||
|
// NYTT: Firebase Cloud Messaging lagt til her
|
||||||
|
implementation("com.google.firebase:firebase-messaging")
|
||||||
|
}
|
||||||
|
|
||||||
============================================================
|
============================================================
|
||||||
FILSTI: app\proguard-rules.pro
|
FILSTI: app\proguard-rules.pro
|
||||||
|
|
@ -229,6 +231,15 @@ FILSTI: app\src\main\AndroidManifest.xml
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<!-- NYTT: Registrering av Firebase Messaging Service -->
|
||||||
|
<service
|
||||||
|
android:name=".MyFirebaseMessagingService"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="com.kbs.kbsintranett.fileprovider"
|
android:authorities="com.kbs.kbsintranett.fileprovider"
|
||||||
|
|
@ -340,6 +351,7 @@ FILSTI: app\src\main\java\com\kbs\kbsintranett\AuthRepository.java
|
||||||
package com.kbs.kbsintranett;
|
package com.kbs.kbsintranett;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
@ -395,6 +407,12 @@ public class AuthRepository {
|
||||||
// Lagre listen over skrivbare kalendere
|
// Lagre listen over skrivbare kalendere
|
||||||
UserManager.getInstance().setWriteableCalendars(response.body().writeableCalendars);
|
UserManager.getInstance().setWriteableCalendars(response.body().writeableCalendars);
|
||||||
|
|
||||||
|
// NYTT: Hvis vi har en ventende FCM-token, send den nå som vi er logget inn
|
||||||
|
String pendingToken = UserManager.getInstance().getFcmToken();
|
||||||
|
if (pendingToken != null && !pendingToken.isEmpty()) {
|
||||||
|
updateDeviceToken(pendingToken);
|
||||||
|
}
|
||||||
|
|
||||||
callback.onSuccess(role);
|
callback.onSuccess(role);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -410,6 +428,35 @@ public class AuthRepository {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sender FCM-token til WordPress for å registrere enheten for push-varsler.
|
||||||
|
*/
|
||||||
|
public static void updateDeviceToken(String token) {
|
||||||
|
if (!UserManager.getInstance().isLoggedIn()) {
|
||||||
|
// Hvis ikke logget inn, bare lagre den til senere
|
||||||
|
UserManager.getInstance().setFcmToken(token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send til server
|
||||||
|
RegisterDeviceRequest request = new RegisterDeviceRequest(token);
|
||||||
|
RetrofitClient.getApiService().registerDevice(request).enqueue(new Callback<JsonElement>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Log.d(TAG, "FCM Token registrert på server OK.");
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Feil ved registrering av FCM Token. Kode: " + response.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<JsonElement> call, Throwable t) {
|
||||||
|
Log.e(TAG, "Nettverksfeil ved sending av FCM token", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
============================================================
|
============================================================
|
||||||
|
|
@ -5761,6 +5808,131 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
FILSTI: app\src\main\java\com\kbs\kbsintranett\MyFirebaseMessagingService.java
|
||||||
|
============================================================
|
||||||
|
package com.kbs.kbsintranett;
|
||||||
|
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||||
|
import com.google.firebase.messaging.RemoteMessage;
|
||||||
|
|
||||||
|
public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
|
|
||||||
|
private static final String TAG = "FCMService";
|
||||||
|
private static final String CHANNEL_ID = "kbs_calendar_channel"; // Samme kanal som før
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kalles når en ny token genereres (f.eks. ved ny installasjon).
|
||||||
|
* Denne tokenen MÅ sendes til WordPress-backend slik at serveren vet hvem den skal sende til.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onNewToken(@NonNull String token) {
|
||||||
|
super.onNewToken(token);
|
||||||
|
Log.d(TAG, "Ny FCM Token: " + token);
|
||||||
|
|
||||||
|
// Oppdater token via AuthRepository.
|
||||||
|
// Hvis brukeren er logget inn, sendes den direkte.
|
||||||
|
// Hvis ikke, lagres den i UserManager og sendes ved neste login.
|
||||||
|
AuthRepository.updateDeviceToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kalles når en melding mottas mens appen er i forgrunnen,
|
||||||
|
* ELLER hvis det er en "Data Message" (som er det vi bør bruke for bakgrunnsoppdateringer).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
|
||||||
|
super.onMessageReceived(remoteMessage);
|
||||||
|
|
||||||
|
Log.d(TAG, "Melding mottatt fra: " + remoteMessage.getFrom());
|
||||||
|
|
||||||
|
// Sjekk om meldingen inneholder data (payload)
|
||||||
|
if (remoteMessage.getData().size() > 0) {
|
||||||
|
Log.d(TAG, "Melding data payload: " + remoteMessage.getData());
|
||||||
|
// Her kan du trigge en oppdatering av kalenderen i bakgrunnen uten å vise varsel,
|
||||||
|
// eller vise et varsel basert på dataene.
|
||||||
|
|
||||||
|
String title = remoteMessage.getData().get("title");
|
||||||
|
String body = remoteMessage.getData().get("body");
|
||||||
|
|
||||||
|
if (title != null && body != null) {
|
||||||
|
showNotification(title, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sjekk om meldingen er en ren varslingsmelding (Notification payload)
|
||||||
|
if (remoteMessage.getNotification() != null) {
|
||||||
|
Log.d(TAG, "Melding varsel body: " + remoteMessage.getNotification().getBody());
|
||||||
|
showNotification(
|
||||||
|
remoteMessage.getNotification().getTitle(),
|
||||||
|
remoteMessage.getNotification().getBody()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showNotification(String title, String message) {
|
||||||
|
// Gjenbruk logikk for kanalopprettelse (sikkerhetsnett)
|
||||||
|
createNotificationChannel();
|
||||||
|
|
||||||
|
// Sjekk rettigheter for Android 13+
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent tapIntent = new Intent(this, MainActivity.class);
|
||||||
|
tapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||||
|
this,
|
||||||
|
0,
|
||||||
|
tapIntent,
|
||||||
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_kbs)
|
||||||
|
.setColor(ContextCompat.getColor(this, R.color.kbs_logo_blue))
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(message)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setAutoCancel(true);
|
||||||
|
|
||||||
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||||
|
// Bruk systemtid som ID for å unngå at varsler overskriver hverandre, eller en fast ID hvis ønskelig
|
||||||
|
notificationManager.notify((int) System.currentTimeMillis(), builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNotificationChannel() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
NotificationChannel channel = new NotificationChannel(
|
||||||
|
CHANNEL_ID,
|
||||||
|
"KBS Kalendervarsler",
|
||||||
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
|
);
|
||||||
|
channel.setDescription("Varsler fra KBS Intranett");
|
||||||
|
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||||
|
if (manager != null) {
|
||||||
|
manager.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
============================================================
|
============================================================
|
||||||
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsAdapter.java
|
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsAdapter.java
|
||||||
============================================================
|
============================================================
|
||||||
|
|
@ -6405,6 +6577,26 @@ public class ProfileFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
FILSTI: app\src\main\java\com\kbs\kbsintranett\RegisterDeviceRequest.java
|
||||||
|
============================================================
|
||||||
|
package com.kbs.kbsintranett;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class RegisterDeviceRequest {
|
||||||
|
@SerializedName("fcm_token")
|
||||||
|
public String fcmToken;
|
||||||
|
|
||||||
|
@SerializedName("platform")
|
||||||
|
public String platform;
|
||||||
|
|
||||||
|
public RegisterDeviceRequest(String fcmToken) {
|
||||||
|
this.fcmToken = fcmToken;
|
||||||
|
this.platform = "android";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
============================================================
|
============================================================
|
||||||
FILSTI: app\src\main\java\com\kbs\kbsintranett\RetrofitClient.java
|
FILSTI: app\src\main\java\com\kbs\kbsintranett\RetrofitClient.java
|
||||||
============================================================
|
============================================================
|
||||||
|
|
@ -6511,6 +6703,9 @@ public class UserManager {
|
||||||
private String stilling;
|
private String stilling;
|
||||||
private String mobiltelefon;
|
private String mobiltelefon;
|
||||||
|
|
||||||
|
// FCM Token (Push)
|
||||||
|
private String fcmToken;
|
||||||
|
|
||||||
// NYTT:
|
// NYTT:
|
||||||
private List<String> writeableCalendars = new ArrayList<>();
|
private List<String> writeableCalendars = new ArrayList<>();
|
||||||
|
|
||||||
|
|
@ -6561,6 +6756,9 @@ public class UserManager {
|
||||||
public String getStilling() { return stilling != null ? stilling : ""; }
|
public String getStilling() { return stilling != null ? stilling : ""; }
|
||||||
public String getMobiltelefon() { return mobiltelefon != null ? mobiltelefon : ""; }
|
public String getMobiltelefon() { return mobiltelefon != null ? mobiltelefon : ""; }
|
||||||
|
|
||||||
|
public void setFcmToken(String token) { this.fcmToken = token; }
|
||||||
|
public String getFcmToken() { return fcmToken; }
|
||||||
|
|
||||||
public boolean isLoggedIn() { return userEmail != null && !userEmail.isEmpty(); }
|
public boolean isLoggedIn() { return userEmail != null && !userEmail.isEmpty(); }
|
||||||
public boolean isAdmin() { return "administrator".equalsIgnoreCase(userRole); }
|
public boolean isAdmin() { return "administrator".equalsIgnoreCase(userRole); }
|
||||||
public boolean isEditorOrAbove() {
|
public boolean isEditorOrAbove() {
|
||||||
|
|
@ -6580,6 +6778,7 @@ public class UserManager {
|
||||||
stilling = null;
|
stilling = null;
|
||||||
mobiltelefon = null;
|
mobiltelefon = null;
|
||||||
writeableCalendars.clear();
|
writeableCalendars.clear();
|
||||||
|
// Vi sletter ikke fcmToken ved logout, da enheten fortsatt er den samme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6688,7 +6887,6 @@ public interface WordPressApiService {
|
||||||
@GET("wp-json/kbs/v1/calendar/events")
|
@GET("wp-json/kbs/v1/calendar/events")
|
||||||
Call<List<CalendarEvent>> getCalendarEvents();
|
Call<List<CalendarEvent>> getCalendarEvents();
|
||||||
|
|
||||||
// DETTE ER METODEN SOM MANGLER:
|
|
||||||
@POST("wp-json/kbs/v1/calendar/create")
|
@POST("wp-json/kbs/v1/calendar/create")
|
||||||
Call<JsonElement> createCalendarEvent(@Body CreateEventRequest request);
|
Call<JsonElement> createCalendarEvent(@Body CreateEventRequest request);
|
||||||
|
|
||||||
|
|
@ -6718,9 +6916,11 @@ public interface WordPressApiService {
|
||||||
Call<JsonElement> updateCalendarEvent(@Body CreateEventRequest request);
|
Call<JsonElement> updateCalendarEvent(@Body CreateEventRequest request);
|
||||||
|
|
||||||
@POST("wp-json/kbs/v1/calendar/delete")
|
@POST("wp-json/kbs/v1/calendar/delete")
|
||||||
Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request); // Sender kun ID og cal_type
|
Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request);
|
||||||
|
|
||||||
|
|
||||||
|
// NYTT: Registrer enhet for push-varsler
|
||||||
|
@POST("wp-json/kbs/v1/device/register")
|
||||||
|
Call<JsonElement> registerDevice(@Body RegisterDeviceRequest request);
|
||||||
}
|
}
|
||||||
|
|
||||||
============================================================
|
============================================================
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue