Før nytt forsøk på Google Kalender
This commit is contained in:
parent
29a36e8a61
commit
d9d128245c
10 changed files with 714 additions and 174 deletions
|
|
@ -13,6 +13,7 @@
|
|||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
@ -38,6 +39,8 @@
|
|||
|
||||
<activity android:name=".WebViewActivity" />
|
||||
|
||||
<receiver android:name=".AlarmReceiver" android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.kbs.kbsintranett.fileprovider"
|
||||
|
|
|
|||
65
app/src/main/java/com/kbs/kbsintranett/AlarmReceiver.java
Normal file
65
app/src/main/java/com/kbs/kbsintranett/AlarmReceiver.java
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package com.kbs.kbsintranett;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
public class AlarmReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = "KBS_DEBUG";
|
||||
private static final String CHANNEL_ID = "kbs_calendar_channel";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String title = intent.getStringExtra("TITLE");
|
||||
String message = intent.getStringExtra("MESSAGE");
|
||||
int notificationId = intent.getIntExtra("ID", 0);
|
||||
|
||||
Log.i(TAG, "AlarmReceiver: WAKE UP! Mottok alarm for: " + title);
|
||||
|
||||
showNotification(context, title, message, notificationId);
|
||||
}
|
||||
|
||||
private void showNotification(Context context, String title, String message, int notificationId) {
|
||||
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "KBS Kalender", NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setDescription("Varsler for kalenderhendelser");
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
Intent openAppIntent = new Intent(context, MainActivity.class);
|
||||
openAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
openAppIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_EVENT)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true);
|
||||
|
||||
try {
|
||||
manager.notify(notificationId, builder.build());
|
||||
Log.d(TAG, "AlarmReceiver: Varsel sendt til systemet.");
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "AlarmReceiver: Feil - Mangler tillatelse til å sende varsel!", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "AlarmReceiver: Ukjent feil ved visning av varsel", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,6 @@ public class CalendarFullFragment extends Fragment {
|
|||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
recyclerView = view.findViewById(R.id.recycler_full_calendar);
|
||||
progressBar = view.findViewById(R.id.loading_full_calendar);
|
||||
emptyView = view.findViewById(R.id.empty_view_calendar);
|
||||
|
|
@ -46,7 +45,6 @@ public class CalendarFullFragment extends Fragment {
|
|||
|
||||
layoutManager = new LinearLayoutManager(getContext());
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
backBtn.setOnClickListener(v -> Navigation.findNavController(view).navigateUp());
|
||||
|
||||
fetchAllEvents();
|
||||
|
|
@ -54,22 +52,21 @@ public class CalendarFullFragment extends Fragment {
|
|||
|
||||
private void fetchAllEvents() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
// Hent personlige hendelser (Nå med historikk)
|
||||
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext());
|
||||
|
||||
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
||||
// NYTT: Hent fra Google direkte
|
||||
String url = CalendarManager.getGoogleCalendarUrl();
|
||||
RetrofitClient.getApiService().getDirectGoogleEvents(url).enqueue(new Callback<GoogleCalendarModels.Response>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
||||
public void onResponse(Call<GoogleCalendarModels.Response> call, Response<GoogleCalendarModels.Response> response) {
|
||||
if (!isAdded()) return;
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
List<CalendarEvent> apiEvents = new ArrayList<>();
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
for (CalendarEvent e : response.body()) {
|
||||
CalendarManager.formatEventForUI(e);
|
||||
apiEvents.add(e);
|
||||
}
|
||||
// Konverter og formatér
|
||||
apiEvents = CalendarManager.convertGoogleResponse(response.body());
|
||||
}
|
||||
|
||||
// Flett og vis
|
||||
|
|
@ -94,7 +91,7 @@ public class CalendarFullFragment extends Fragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
||||
public void onFailure(Call<GoogleCalendarModels.Response> call, Throwable t) {
|
||||
if (!isAdded()) return;
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,15 +15,86 @@ import java.util.TimeZone;
|
|||
|
||||
public class CalendarManager {
|
||||
|
||||
// --- KONFIGURASJON FOR GOOGLE DIREKTE-KOBLING ---
|
||||
private static final String GOOGLE_CALENDAR_ID = "kbservice.no_o2bmp5f9f540vedveit51optfo@group.calendar.google.com";
|
||||
|
||||
// TODO: Sett inn din API-nøkkel her!
|
||||
private static final String GOOGLE_API_KEY = "AIzaSyCos8VW5mClUcuhs86gbSJo8uitY0fVPus";
|
||||
|
||||
public static String getGoogleCalendarUrl() {
|
||||
// Hent hendelser fra 1 år tilbake i tid
|
||||
long oneYearAgo = System.currentTimeMillis() - (365L * 24 * 60 * 60 * 1000);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
String timeMin = dateFormat.format(new Date(oneYearAgo));
|
||||
|
||||
return "https://www.googleapis.com/calendar/v3/calendars/"
|
||||
+ GOOGLE_CALENDAR_ID
|
||||
+ "/events?key=" + GOOGLE_API_KEY
|
||||
+ "&singleEvents=true"
|
||||
+ "&orderBy=startTime"
|
||||
+ "&maxResults=250"
|
||||
+ "&timeMin=" + timeMin; // URL-encoded er ikke nødvendig her siden Retrofit/OkHttp håndterer det, men timeMin bør være formatert
|
||||
}
|
||||
|
||||
// Konverterer Google Response til vår interne CalendarEvent
|
||||
public static List<CalendarEvent> convertGoogleResponse(GoogleCalendarModels.Response response) {
|
||||
List<CalendarEvent> events = new ArrayList<>();
|
||||
if (response == null || response.items == null) return events;
|
||||
|
||||
for (GoogleCalendarModels.Item item : response.items) {
|
||||
String title = item.summary != null ? item.summary : "(Uten tittel)";
|
||||
String desc = item.description;
|
||||
String loc = item.location;
|
||||
|
||||
// Dato-logikk
|
||||
String start = null;
|
||||
String end = null;
|
||||
|
||||
if (item.start != null) {
|
||||
if (item.start.dateTime != null) start = item.start.dateTime;
|
||||
else start = item.start.date; // Heldags
|
||||
}
|
||||
if (item.end != null) {
|
||||
if (item.end.dateTime != null) end = item.end.dateTime;
|
||||
else end = item.end.date; // Heldags
|
||||
}
|
||||
|
||||
// Varslings-logikk (Henter de sanne innstillingene)
|
||||
int reminderMinutes = 15; // Default fallback
|
||||
if (item.reminders != null) {
|
||||
if (item.reminders.useDefault) {
|
||||
reminderMinutes = 15; // Standard i Google er ofte 10 eller 15, vi antar 15
|
||||
} else if (item.reminders.overrides != null) {
|
||||
for (GoogleCalendarModels.Override override : item.reminders.overrides) {
|
||||
if ("popup".equalsIgnoreCase(override.method) || "alert".equalsIgnoreCase(override.method)) {
|
||||
reminderMinutes = override.minutes;
|
||||
break; // Bruk den første popup-varslingen vi finner
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CalendarEvent event = new CalendarEvent(title, start, end, desc, loc);
|
||||
event.setReminderMinutes(reminderMinutes);
|
||||
|
||||
// Formatér for UI med en gang
|
||||
formatEventForUI(event);
|
||||
|
||||
events.add(event);
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
// --- EKSISTERENDE KODE (UENDRET UNDER) ---
|
||||
|
||||
// NY HJELPEMETODE: Finner ID-ene til kalendere som tilhører @kbs.no
|
||||
private static List<String> getKbsCalendarIds(Context context) {
|
||||
List<String> ids = new ArrayList<>();
|
||||
|
||||
String[] projection = new String[] {
|
||||
CalendarContract.Calendars._ID,
|
||||
CalendarContract.Calendars.ACCOUNT_NAME
|
||||
};
|
||||
|
||||
// Vi ser etter kontoer som slutter på @kbs.no
|
||||
// (SQL: account_name LIKE '%@kbs.no')
|
||||
String selection = CalendarContract.Calendars.ACCOUNT_NAME + " LIKE ?";
|
||||
|
|
@ -38,7 +109,8 @@ public class CalendarManager {
|
|||
)) {
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
ids.add(cursor.getString(0)); // Legg til kalender-ID
|
||||
ids.add(cursor.getString(0));
|
||||
// Legg til kalender-ID
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -49,7 +121,6 @@ public class CalendarManager {
|
|||
|
||||
public static List<CalendarEvent> getDeviceEvents(Context context) {
|
||||
List<CalendarEvent> deviceEvents = new ArrayList<>();
|
||||
|
||||
if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_CALENDAR)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
return deviceEvents;
|
||||
|
|
@ -57,7 +128,6 @@ public class CalendarManager {
|
|||
|
||||
// 1. Finn først ID-ene til KBS-kalenderne
|
||||
List<String> kbsCalendarIds = getKbsCalendarIds(context);
|
||||
|
||||
// Hvis ingen kbs-kalendere finnes på telefonen, returner tom liste
|
||||
if (kbsCalendarIds.isEmpty()) {
|
||||
return deviceEvents;
|
||||
|
|
@ -67,7 +137,6 @@ public class CalendarManager {
|
|||
long now = System.currentTimeMillis();
|
||||
long startMillis = now - (365L * 24 * 60 * 60 * 1000);
|
||||
long endMillis = now + (365L * 24 * 60 * 60 * 1000);
|
||||
|
||||
String[] projection = new String[]{
|
||||
CalendarContract.Events.TITLE,
|
||||
CalendarContract.Events.DTSTART,
|
||||
|
|
@ -90,7 +159,6 @@ public class CalendarManager {
|
|||
}
|
||||
}
|
||||
selection.append(") AND ");
|
||||
|
||||
selection.append(CalendarContract.Events.DTSTART).append(" >= ? AND ");
|
||||
selection.append(CalendarContract.Events.DTSTART).append(" <= ?");
|
||||
|
||||
|
|
@ -98,7 +166,6 @@ public class CalendarManager {
|
|||
selectionArgsList.add(String.valueOf(endMillis));
|
||||
|
||||
String[] selectionArgs = selectionArgsList.toArray(new String[0]);
|
||||
|
||||
try (Cursor cursor = context.getContentResolver().query(
|
||||
CalendarContract.Events.CONTENT_URI,
|
||||
projection,
|
||||
|
|
@ -108,7 +175,6 @@ public class CalendarManager {
|
|||
)) {
|
||||
if (cursor != null) {
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
String title = cursor.getString(0);
|
||||
long dtStart = cursor.getLong(1);
|
||||
|
|
@ -116,7 +182,6 @@ public class CalendarManager {
|
|||
String desc = cursor.getString(3);
|
||||
String loc = cursor.getString(4);
|
||||
int allDay = cursor.getInt(5);
|
||||
|
||||
String rawStart;
|
||||
String rawEnd;
|
||||
|
||||
|
|
@ -140,14 +205,11 @@ public class CalendarManager {
|
|||
return deviceEvents;
|
||||
}
|
||||
|
||||
// --- (formatEventForUI og mergeAndSort er uendret fra forrige versjon) ---
|
||||
public static void formatEventForUI(CalendarEvent event) {
|
||||
if (event.getRawDate() == null) return;
|
||||
|
||||
SimpleDateFormat outputDay = new SimpleDateFormat("dd", Locale.getDefault());
|
||||
SimpleDateFormat outputMonth = new SimpleDateFormat("MMM", new Locale("no", "NO"));
|
||||
SimpleDateFormat outputTime = new SimpleDateFormat("HH:mm", Locale.getDefault());
|
||||
|
||||
outputMonth.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
||||
outputTime.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
||||
|
||||
|
|
@ -157,7 +219,6 @@ public class CalendarManager {
|
|||
boolean isAllDay = false;
|
||||
|
||||
String raw = event.getRawDate();
|
||||
|
||||
if (raw.length() == 10 && !raw.contains("T") && !raw.contains(" ")) {
|
||||
SimpleDateFormat shortFmt = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
date = shortFmt.parse(raw);
|
||||
|
|
@ -215,7 +276,6 @@ public class CalendarManager {
|
|||
String d2 = e2.getRawDate() != null ? e2.getRawDate() : "";
|
||||
return d1.compareTo(d2);
|
||||
});
|
||||
|
||||
return all;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.kbs.kbsintranett;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hjelpeklasser for å parse JSON direkte fra Google Calendar API v3.
|
||||
*/
|
||||
public class GoogleCalendarModels {
|
||||
|
||||
public static class Response {
|
||||
@SerializedName("items")
|
||||
public List<Item> items;
|
||||
}
|
||||
|
||||
public static class Item {
|
||||
@SerializedName("summary")
|
||||
public String summary;
|
||||
|
||||
@SerializedName("description")
|
||||
public String description;
|
||||
|
||||
@SerializedName("location")
|
||||
public String location;
|
||||
|
||||
@SerializedName("start")
|
||||
public TimePoint start;
|
||||
|
||||
@SerializedName("end")
|
||||
public TimePoint end;
|
||||
|
||||
@SerializedName("reminders")
|
||||
public Reminders reminders;
|
||||
}
|
||||
|
||||
public static class TimePoint {
|
||||
@SerializedName("dateTime")
|
||||
public String dateTime; // Format: 2025-12-15T10:00:00+01:00
|
||||
|
||||
@SerializedName("date")
|
||||
public String date; // Format: 2025-12-15 (for heldags)
|
||||
}
|
||||
|
||||
public static class Reminders {
|
||||
@SerializedName("useDefault")
|
||||
public boolean useDefault;
|
||||
|
||||
@SerializedName("overrides")
|
||||
public List<Override> overrides;
|
||||
}
|
||||
|
||||
public static class Override {
|
||||
@SerializedName("method")
|
||||
public String method; // f.eks "popup"
|
||||
|
||||
@SerializedName("minutes")
|
||||
public int minutes;
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,6 @@ public class HomeFragment extends Fragment {
|
|||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Håndter svar på kalendertillatelse
|
||||
requestPermissionLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.RequestPermission(),
|
||||
|
|
@ -48,7 +47,6 @@ public class HomeFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Start bakgrunnsjobb for varsling (kjører hver 15. minutt)
|
||||
startNotificationWorker();
|
||||
}
|
||||
|
|
@ -62,7 +60,6 @@ public class HomeFragment extends Fragment {
|
|||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// 0. Profil-knapp
|
||||
View profileBtn = view.findViewById(R.id.btn_profile);
|
||||
if (profileBtn != null) {
|
||||
|
|
@ -74,7 +71,6 @@ public class HomeFragment extends Fragment {
|
|||
calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
// Sett tom adapter midlertidig
|
||||
calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {}));
|
||||
|
||||
// "Se alle" knapp for kalender
|
||||
TextView viewAllCalendar = view.findViewById(R.id.btn_view_all_calendar);
|
||||
if (viewAllCalendar != null) {
|
||||
|
|
@ -102,7 +98,6 @@ public class HomeFragment extends Fragment {
|
|||
newsRecycler.setNestedScrollingEnabled(false);
|
||||
// Sett tom adapter midlertidig
|
||||
newsRecycler.setAdapter(new NewsAdapter(new ArrayList<>(), item -> {}));
|
||||
|
||||
// "Se alle" knapp for nyheter
|
||||
TextView viewAllNews = view.findViewById(R.id.btn_view_all_news);
|
||||
if (viewAllNews != null) {
|
||||
|
|
@ -118,26 +113,23 @@ public class HomeFragment extends Fragment {
|
|||
// 1. Hent personlige hendelser først (fra CalendarManager)
|
||||
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext());
|
||||
|
||||
// 2. Hent API-hendelser fra WordPress
|
||||
WordPressApiService apiService = RetrofitClient.getApiService();
|
||||
apiService.getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
||||
// 2. Hent API-hendelser DIREKTE fra Google
|
||||
String url = CalendarManager.getGoogleCalendarUrl();
|
||||
RetrofitClient.getApiService().getDirectGoogleEvents(url).enqueue(new Callback<GoogleCalendarModels.Response>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
||||
public void onResponse(Call<GoogleCalendarModels.Response> call, Response<GoogleCalendarModels.Response> response) {
|
||||
if (!isAdded()) return;
|
||||
|
||||
List<CalendarEvent> apiEvents = new ArrayList<>();
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
for (CalendarEvent e : response.body()) {
|
||||
CalendarManager.formatEventForUI(e); // Formatér datoer
|
||||
apiEvents.add(e);
|
||||
}
|
||||
// Konverter fra Google-modell til KBS-modell
|
||||
apiEvents = CalendarManager.convertGoogleResponse(response.body());
|
||||
}
|
||||
|
||||
// 3. Flett listene (API + Personlig) og sorter
|
||||
List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
|
||||
|
||||
// 4. Filtrer ut hendelser som har passert (vis kun fremtidige + i dag)
|
||||
// (CalendarManager henter 1 år bakover, så vi må filtrere for "Topp 5 kommende")
|
||||
List<CalendarEvent> upcomingEvents = new ArrayList<>();
|
||||
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
|
||||
|
||||
|
|
@ -160,14 +152,12 @@ public class HomeFragment extends Fragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
||||
public void onFailure(Call<GoogleCalendarModels.Response> call, Throwable t) {
|
||||
if (!isAdded()) return;
|
||||
// Hvis API feiler, vis bare personlige events hvis vi har noen
|
||||
if (!deviceEvents.isEmpty()) {
|
||||
List<CalendarEvent> top5 = new ArrayList<>();
|
||||
// Filtrer og plukk topp 5 fra lokale også
|
||||
for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) top5.add(deviceEvents.get(i));
|
||||
|
||||
recyclerView.setAdapter(new CalendarAdapter(top5, event -> {
|
||||
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
|
||||
sheet.show(getParentFragmentManager(), "CalendarDetails");
|
||||
|
|
@ -201,7 +191,8 @@ public class HomeFragment extends Fragment {
|
|||
for (WpPost post : wpPosts) {
|
||||
try {
|
||||
Date date = rawFormat.parse(post.date);
|
||||
post.date = targetFormat.format(date); // Setter pen dato
|
||||
post.date = targetFormat.format(date);
|
||||
// Setter pen dato
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
package com.kbs.kbsintranett;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
|
|
@ -8,6 +15,8 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
||||
|
|
@ -32,6 +41,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
bottomNav = findViewById(R.id.bottom_nav_view);
|
||||
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.nav_host_fragment);
|
||||
|
||||
if (navHostFragment != null) {
|
||||
navController = navHostFragment.getNavController();
|
||||
NavigationUI.setupWithNavController(bottomNav, navController);
|
||||
|
|
@ -47,10 +57,50 @@ public class MainActivity extends AppCompatActivity {
|
|||
});
|
||||
}
|
||||
|
||||
// --- NYTT: Sjekk tillatelse for nøyaktige alarmer (Android 12+) ---
|
||||
checkExactAlarmPermission();
|
||||
|
||||
// 2. Start Silent Sign-In ved oppstart
|
||||
checkLoginState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sjekker om appen har lov til å sette nøyaktige alarmer (SCHEDULE_EXACT_ALARM).
|
||||
* Hvis ikke, spør brukeren om å gå til innstillinger.
|
||||
*/
|
||||
private void checkExactAlarmPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||
if (alarmManager != null && !alarmManager.canScheduleExactAlarms()) {
|
||||
// Vi mangler tillatelse. Vis dialog.
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Varslingstillatelse kreves")
|
||||
.setMessage("For at kalenderen skal kunne varsle deg nøyaktig når et møte starter, må du gi appen tilgang til å sette alarmer.")
|
||||
.setPositiveButton("Gå til Innstillinger", (dialog, which) -> {
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
|
||||
intent.setData(Uri.parse("package:" + getPackageName()));
|
||||
startActivity(intent);
|
||||
})
|
||||
.setNegativeButton("Senere", null)
|
||||
.show();
|
||||
} else {
|
||||
// Vi har tillatelse (eller er på eldre Android). Kjør logikk!
|
||||
runNotificationWorker();
|
||||
}
|
||||
} else {
|
||||
// Eldre Android-versjoner trenger ikke denne tillatelsen
|
||||
runNotificationWorker();
|
||||
}
|
||||
}
|
||||
|
||||
private void runNotificationWorker() {
|
||||
// --- DEBUG: TVING KJØRING AV KALENDER-SJEKK ---
|
||||
// Denne linjen kjører NotificationWorker umiddelbart ved oppstart for feilsøking.
|
||||
// Fjern eller kommenter ut denne når testingen er ferdig.
|
||||
WorkManager.getInstance(this).enqueue(OneTimeWorkRequest.from(NotificationWorker.class));
|
||||
// ----------------------------------------------
|
||||
}
|
||||
|
||||
private void checkLoginState() {
|
||||
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
|
||||
if (account == null) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.kbs.kbsintranett;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import java.io.IOException;
|
||||
|
|
@ -18,8 +18,7 @@ import retrofit2.Response;
|
|||
|
||||
public class NotificationWorker extends Worker {
|
||||
|
||||
private static final String CHANNEL_ID = "kbs_calendar_channel";
|
||||
private static final String PREFS_NAME = "KBSNotificationPrefs";
|
||||
private static final String TAG = "KBS_DEBUG";
|
||||
|
||||
public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
|
|
@ -28,72 +27,108 @@ public class NotificationWorker extends Worker {
|
|||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
// Dette kjører i bakgrunnen
|
||||
Log.d(TAG, "NotificationWorker: Starter sjekk av kalender...");
|
||||
|
||||
try {
|
||||
// Hent events synkront (ikke enqueue)
|
||||
Response<List<CalendarEvent>> response = RetrofitClient.getApiService().getCalendarEvents().execute();
|
||||
String url = CalendarManager.getGoogleCalendarUrl();
|
||||
// Hent events synkront
|
||||
Response<GoogleCalendarModels.Response> response = RetrofitClient.getApiService().getDirectGoogleEvents(url).execute();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
checkAndNotify(response.body());
|
||||
List<CalendarEvent> events = CalendarManager.convertGoogleResponse(response.body());
|
||||
scheduleAlarms(events);
|
||||
return Result.success();
|
||||
} else {
|
||||
Log.e(TAG, "NotificationWorker: API-kall feilet. Kode: " + response.code());
|
||||
return Result.retry();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "NotificationWorker: Nettverksfeil", e);
|
||||
return Result.retry();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndNotify(List<CalendarEvent> events) {
|
||||
SharedPreferences prefs = getApplicationContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
long now = System.currentTimeMillis();
|
||||
long fifteenMinutes = 15 * 60 * 1000;
|
||||
private void scheduleAlarms(List<CalendarEvent> events) {
|
||||
Context context = getApplicationContext();
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
// Sjekk rettigheter (Android 12+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (!alarmManager.canScheduleExactAlarms()) {
|
||||
Log.e(TAG, "NotificationWorker: MANGLER fortsatt tillatelse! Gå til Innstillinger -> Apper -> KBS -> Alarmer og påminnelser.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
// Bruker en parser som er litt mer fleksibel for datoer
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||
SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||
|
||||
int countSet = 0;
|
||||
|
||||
for (CalendarEvent event : events) {
|
||||
try {
|
||||
Date eventDate;
|
||||
if (event.getRawDate().contains("T")) eventDate = isoFormat.parse(event.getRawDate());
|
||||
else eventDate = sqlFormat.parse(event.getRawDate());
|
||||
// Hopp over heldagshendelser (dato uten klokkeslett)
|
||||
if (event.getRawDate().length() == 10) continue;
|
||||
|
||||
Date eventDate = null;
|
||||
// Enkel parsing. Merk: Google sender med tidssone (+01:00),
|
||||
// men SimpleDateFormat uten 'X' vil parse dette som lokal tid hvis formatet stemmer.
|
||||
// For optimal tidssone-håndtering burde vi brukt java.time (Android 8+),
|
||||
// men dette fungerer greit så lenge telefonen er i samme sone som kalenderen.
|
||||
if (event.getRawDate().contains("T")) {
|
||||
// Kutter vekk tidssone-offset for enkel parsing til lokal tid
|
||||
String raw = event.getRawDate();
|
||||
if (raw.length() > 19) raw = raw.substring(0, 19);
|
||||
eventDate = isoFormat.parse(raw);
|
||||
}
|
||||
|
||||
if (eventDate == null) continue;
|
||||
|
||||
long diff = eventDate.getTime() - now;
|
||||
// Beregn når alarmen skal gå
|
||||
long triggerTime = eventDate.getTime() - (event.getReminderMinutes() * 60 * 1000L);
|
||||
|
||||
// Hvis eventet starter innen de neste 30 min, og ikke allerede varslet
|
||||
if (diff > 0 && diff < (30 * 60 * 1000)) {
|
||||
String eventId = event.getTitle() + event.getRawDate(); // Enkel ID
|
||||
boolean alreadyNotified = prefs.getBoolean(eventId, false);
|
||||
// Vi setter alarmen hvis tidspunktet er i fremtiden
|
||||
// Vi sjekker også at det ikke er mer enn 24 timer frem i tid (for å spare ressurser)
|
||||
if (triggerTime > now && triggerTime < (now + 24 * 60 * 60 * 1000L)) {
|
||||
|
||||
if (!alreadyNotified) {
|
||||
sendNotification(event.getTitle(), "Starter kl " + event.getTime());
|
||||
// Lagre at vi har varslet
|
||||
prefs.edit().putBoolean(eventId, true).apply();
|
||||
// Lag en unik ID for alarmen
|
||||
String uniqueIdString = event.getTitle() + event.getRawDate();
|
||||
int alarmId = uniqueIdString.hashCode();
|
||||
|
||||
Intent intent = new Intent(context, AlarmReceiver.class);
|
||||
intent.putExtra("TITLE", event.getTitle());
|
||||
intent.putExtra("MESSAGE", "Starter kl " + event.getTime());
|
||||
intent.putExtra("ID", alarmId);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
alarmId,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
// VIKTIG ENDRING: Vi setter alarmen PÅ NYTT hver gang.
|
||||
// AlarmManager overskriver automatisk hvis ID er lik.
|
||||
// Dette sikrer at alarmen faktisk ligger der, selv etter omstart av tlf.
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
||||
} else {
|
||||
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
||||
}
|
||||
|
||||
Log.i(TAG, ">>> ALARM SATT (Oppdatert): " + event.getTitle() + " -> Skal ringe: " + new Date(triggerTime));
|
||||
countSet++;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "Feil ved behandling av event: " + event.getTitle(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotification(String title, String content) {
|
||||
NotificationManager manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "KBS Kalender", NotificationManager.IMPORTANCE_HIGH);
|
||||
manager.createNotificationChannel(channel);
|
||||
if (countSet == 0) {
|
||||
Log.d(TAG, "Ingen kommende alarmer (innenfor neste 24t) funnet akkurat nå.");
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher) // Sørg for at du har et ikon her
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true);
|
||||
|
||||
manager.notify((int) System.currentTimeMillis(), builder.build());
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import retrofit2.http.Multipart;
|
|||
import retrofit2.http.Part;
|
||||
import retrofit2.http.PartMap;
|
||||
import retrofit2.http.Query;
|
||||
import retrofit2.http.Url; // NY IMPORT
|
||||
|
||||
public interface WordPressApiService {
|
||||
@GET("wp-json/wp/v2/posts?per_page=10&_embed")
|
||||
|
|
@ -40,9 +41,14 @@ public interface WordPressApiService {
|
|||
@Part List<MultipartBody.Part> files
|
||||
);
|
||||
|
||||
// ENDRET: Denne brukes ikke lenger for kalender, men beholdes for bakoverkompatibilitet
|
||||
@GET("wp-json/kbs/v1/calendar/events")
|
||||
Call<List<CalendarEvent>> getCalendarEvents();
|
||||
|
||||
// NY: Direkte kall mot Google (bruker @Url for å override base URL)
|
||||
@GET
|
||||
Call<GoogleCalendarModels.Response> getDirectGoogleEvents(@Url String fullUrl);
|
||||
|
||||
@GET("wp-json/gf/v2/entries")
|
||||
Call<GravityEntryResponse> getEntries(
|
||||
@Query("form_ids") int formId,
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ FILSTI: app\src\main\AndroidManifest.xml
|
|||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
@ -202,6 +203,8 @@ FILSTI: app\src\main\AndroidManifest.xml
|
|||
|
||||
<activity android:name=".WebViewActivity" />
|
||||
|
||||
<receiver android:name=".AlarmReceiver" android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.kbs.kbsintranett.fileprovider"
|
||||
|
|
@ -216,6 +219,75 @@ FILSTI: app\src\main\AndroidManifest.xml
|
|||
|
||||
</manifest>
|
||||
|
||||
============================================================
|
||||
FILSTI: app\src\main\java\com\kbs\kbsintranett\AlarmReceiver.java
|
||||
============================================================
|
||||
package com.kbs.kbsintranett;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
public class AlarmReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = "KBS_DEBUG";
|
||||
private static final String CHANNEL_ID = "kbs_calendar_channel";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String title = intent.getStringExtra("TITLE");
|
||||
String message = intent.getStringExtra("MESSAGE");
|
||||
int notificationId = intent.getIntExtra("ID", 0);
|
||||
|
||||
Log.i(TAG, "AlarmReceiver: WAKE UP! Mottok alarm for: " + title);
|
||||
|
||||
showNotification(context, title, message, notificationId);
|
||||
}
|
||||
|
||||
private void showNotification(Context context, String title, String message, int notificationId) {
|
||||
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "KBS Kalender", NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setDescription("Varsler for kalenderhendelser");
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
Intent openAppIntent = new Intent(context, MainActivity.class);
|
||||
openAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
openAppIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_EVENT)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true);
|
||||
|
||||
try {
|
||||
manager.notify(notificationId, builder.build());
|
||||
Log.d(TAG, "AlarmReceiver: Varsel sendt til systemet.");
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "AlarmReceiver: Feil - Mangler tillatelse til å sende varsel!", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "AlarmReceiver: Ukjent feil ved visning av varsel", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
============================================================
|
||||
FILSTI: app\src\main\java\com\kbs\kbsintranett\AuthRepository.java
|
||||
============================================================
|
||||
|
|
@ -568,7 +640,6 @@ public class CalendarFullFragment extends Fragment {
|
|||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
recyclerView = view.findViewById(R.id.recycler_full_calendar);
|
||||
progressBar = view.findViewById(R.id.loading_full_calendar);
|
||||
emptyView = view.findViewById(R.id.empty_view_calendar);
|
||||
|
|
@ -576,7 +647,6 @@ public class CalendarFullFragment extends Fragment {
|
|||
|
||||
layoutManager = new LinearLayoutManager(getContext());
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
backBtn.setOnClickListener(v -> Navigation.findNavController(view).navigateUp());
|
||||
|
||||
fetchAllEvents();
|
||||
|
|
@ -584,22 +654,21 @@ public class CalendarFullFragment extends Fragment {
|
|||
|
||||
private void fetchAllEvents() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
// Hent personlige hendelser (Nå med historikk)
|
||||
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext());
|
||||
|
||||
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
||||
// NYTT: Hent fra Google direkte
|
||||
String url = CalendarManager.getGoogleCalendarUrl();
|
||||
RetrofitClient.getApiService().getDirectGoogleEvents(url).enqueue(new Callback<GoogleCalendarModels.Response>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
||||
public void onResponse(Call<GoogleCalendarModels.Response> call, Response<GoogleCalendarModels.Response> response) {
|
||||
if (!isAdded()) return;
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
List<CalendarEvent> apiEvents = new ArrayList<>();
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
for (CalendarEvent e : response.body()) {
|
||||
CalendarManager.formatEventForUI(e);
|
||||
apiEvents.add(e);
|
||||
}
|
||||
// Konverter og formatér
|
||||
apiEvents = CalendarManager.convertGoogleResponse(response.body());
|
||||
}
|
||||
|
||||
// Flett og vis
|
||||
|
|
@ -624,7 +693,7 @@ public class CalendarFullFragment extends Fragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
||||
public void onFailure(Call<GoogleCalendarModels.Response> call, Throwable t) {
|
||||
if (!isAdded()) return;
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
|
|
@ -684,15 +753,86 @@ import java.util.TimeZone;
|
|||
|
||||
public class CalendarManager {
|
||||
|
||||
// --- KONFIGURASJON FOR GOOGLE DIREKTE-KOBLING ---
|
||||
private static final String GOOGLE_CALENDAR_ID = "kbservice.no_o2bmp5f9f540vedveit51optfo@group.calendar.google.com";
|
||||
|
||||
// TODO: Sett inn din API-nøkkel her!
|
||||
private static final String GOOGLE_API_KEY = "AIzaSyCos8VW5mClUcuhs86gbSJo8uitY0fVPus";
|
||||
|
||||
public static String getGoogleCalendarUrl() {
|
||||
// Hent hendelser fra 1 år tilbake i tid
|
||||
long oneYearAgo = System.currentTimeMillis() - (365L * 24 * 60 * 60 * 1000);
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
String timeMin = dateFormat.format(new Date(oneYearAgo));
|
||||
|
||||
return "https://www.googleapis.com/calendar/v3/calendars/"
|
||||
+ GOOGLE_CALENDAR_ID
|
||||
+ "/events?key=" + GOOGLE_API_KEY
|
||||
+ "&singleEvents=true"
|
||||
+ "&orderBy=startTime"
|
||||
+ "&maxResults=250"
|
||||
+ "&timeMin=" + timeMin; // URL-encoded er ikke nødvendig her siden Retrofit/OkHttp håndterer det, men timeMin bør være formatert
|
||||
}
|
||||
|
||||
// Konverterer Google Response til vår interne CalendarEvent
|
||||
public static List<CalendarEvent> convertGoogleResponse(GoogleCalendarModels.Response response) {
|
||||
List<CalendarEvent> events = new ArrayList<>();
|
||||
if (response == null || response.items == null) return events;
|
||||
|
||||
for (GoogleCalendarModels.Item item : response.items) {
|
||||
String title = item.summary != null ? item.summary : "(Uten tittel)";
|
||||
String desc = item.description;
|
||||
String loc = item.location;
|
||||
|
||||
// Dato-logikk
|
||||
String start = null;
|
||||
String end = null;
|
||||
|
||||
if (item.start != null) {
|
||||
if (item.start.dateTime != null) start = item.start.dateTime;
|
||||
else start = item.start.date; // Heldags
|
||||
}
|
||||
if (item.end != null) {
|
||||
if (item.end.dateTime != null) end = item.end.dateTime;
|
||||
else end = item.end.date; // Heldags
|
||||
}
|
||||
|
||||
// Varslings-logikk (Henter de sanne innstillingene)
|
||||
int reminderMinutes = 15; // Default fallback
|
||||
if (item.reminders != null) {
|
||||
if (item.reminders.useDefault) {
|
||||
reminderMinutes = 15; // Standard i Google er ofte 10 eller 15, vi antar 15
|
||||
} else if (item.reminders.overrides != null) {
|
||||
for (GoogleCalendarModels.Override override : item.reminders.overrides) {
|
||||
if ("popup".equalsIgnoreCase(override.method) || "alert".equalsIgnoreCase(override.method)) {
|
||||
reminderMinutes = override.minutes;
|
||||
break; // Bruk den første popup-varslingen vi finner
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CalendarEvent event = new CalendarEvent(title, start, end, desc, loc);
|
||||
event.setReminderMinutes(reminderMinutes);
|
||||
|
||||
// Formatér for UI med en gang
|
||||
formatEventForUI(event);
|
||||
|
||||
events.add(event);
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
// --- EKSISTERENDE KODE (UENDRET UNDER) ---
|
||||
|
||||
// NY HJELPEMETODE: Finner ID-ene til kalendere som tilhører @kbs.no
|
||||
private static List<String> getKbsCalendarIds(Context context) {
|
||||
List<String> ids = new ArrayList<>();
|
||||
|
||||
String[] projection = new String[] {
|
||||
CalendarContract.Calendars._ID,
|
||||
CalendarContract.Calendars.ACCOUNT_NAME
|
||||
};
|
||||
|
||||
// Vi ser etter kontoer som slutter på @kbs.no
|
||||
// (SQL: account_name LIKE '%@kbs.no')
|
||||
String selection = CalendarContract.Calendars.ACCOUNT_NAME + " LIKE ?";
|
||||
|
|
@ -707,7 +847,8 @@ public class CalendarManager {
|
|||
)) {
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
ids.add(cursor.getString(0)); // Legg til kalender-ID
|
||||
ids.add(cursor.getString(0));
|
||||
// Legg til kalender-ID
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -718,7 +859,6 @@ public class CalendarManager {
|
|||
|
||||
public static List<CalendarEvent> getDeviceEvents(Context context) {
|
||||
List<CalendarEvent> deviceEvents = new ArrayList<>();
|
||||
|
||||
if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_CALENDAR)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
return deviceEvents;
|
||||
|
|
@ -726,7 +866,6 @@ public class CalendarManager {
|
|||
|
||||
// 1. Finn først ID-ene til KBS-kalenderne
|
||||
List<String> kbsCalendarIds = getKbsCalendarIds(context);
|
||||
|
||||
// Hvis ingen kbs-kalendere finnes på telefonen, returner tom liste
|
||||
if (kbsCalendarIds.isEmpty()) {
|
||||
return deviceEvents;
|
||||
|
|
@ -736,7 +875,6 @@ public class CalendarManager {
|
|||
long now = System.currentTimeMillis();
|
||||
long startMillis = now - (365L * 24 * 60 * 60 * 1000);
|
||||
long endMillis = now + (365L * 24 * 60 * 60 * 1000);
|
||||
|
||||
String[] projection = new String[]{
|
||||
CalendarContract.Events.TITLE,
|
||||
CalendarContract.Events.DTSTART,
|
||||
|
|
@ -759,7 +897,6 @@ public class CalendarManager {
|
|||
}
|
||||
}
|
||||
selection.append(") AND ");
|
||||
|
||||
selection.append(CalendarContract.Events.DTSTART).append(" >= ? AND ");
|
||||
selection.append(CalendarContract.Events.DTSTART).append(" <= ?");
|
||||
|
||||
|
|
@ -767,7 +904,6 @@ public class CalendarManager {
|
|||
selectionArgsList.add(String.valueOf(endMillis));
|
||||
|
||||
String[] selectionArgs = selectionArgsList.toArray(new String[0]);
|
||||
|
||||
try (Cursor cursor = context.getContentResolver().query(
|
||||
CalendarContract.Events.CONTENT_URI,
|
||||
projection,
|
||||
|
|
@ -777,7 +913,6 @@ public class CalendarManager {
|
|||
)) {
|
||||
if (cursor != null) {
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
String title = cursor.getString(0);
|
||||
long dtStart = cursor.getLong(1);
|
||||
|
|
@ -785,7 +920,6 @@ public class CalendarManager {
|
|||
String desc = cursor.getString(3);
|
||||
String loc = cursor.getString(4);
|
||||
int allDay = cursor.getInt(5);
|
||||
|
||||
String rawStart;
|
||||
String rawEnd;
|
||||
|
||||
|
|
@ -809,14 +943,11 @@ public class CalendarManager {
|
|||
return deviceEvents;
|
||||
}
|
||||
|
||||
// --- (formatEventForUI og mergeAndSort er uendret fra forrige versjon) ---
|
||||
public static void formatEventForUI(CalendarEvent event) {
|
||||
if (event.getRawDate() == null) return;
|
||||
|
||||
SimpleDateFormat outputDay = new SimpleDateFormat("dd", Locale.getDefault());
|
||||
SimpleDateFormat outputMonth = new SimpleDateFormat("MMM", new Locale("no", "NO"));
|
||||
SimpleDateFormat outputTime = new SimpleDateFormat("HH:mm", Locale.getDefault());
|
||||
|
||||
outputMonth.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
||||
outputTime.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
||||
|
||||
|
|
@ -826,7 +957,6 @@ public class CalendarManager {
|
|||
boolean isAllDay = false;
|
||||
|
||||
String raw = event.getRawDate();
|
||||
|
||||
if (raw.length() == 10 && !raw.contains("T") && !raw.contains(" ")) {
|
||||
SimpleDateFormat shortFmt = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
date = shortFmt.parse(raw);
|
||||
|
|
@ -884,7 +1014,6 @@ public class CalendarManager {
|
|||
String d2 = e2.getRawDate() != null ? e2.getRawDate() : "";
|
||||
return d1.compareTo(d2);
|
||||
});
|
||||
|
||||
return all;
|
||||
}
|
||||
}
|
||||
|
|
@ -2946,6 +3075,69 @@ public class FormSubmission {
|
|||
}
|
||||
}
|
||||
|
||||
============================================================
|
||||
FILSTI: app\src\main\java\com\kbs\kbsintranett\GoogleCalendarModels.java
|
||||
============================================================
|
||||
package com.kbs.kbsintranett;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hjelpeklasser for å parse JSON direkte fra Google Calendar API v3.
|
||||
*/
|
||||
public class GoogleCalendarModels {
|
||||
|
||||
public static class Response {
|
||||
@SerializedName("items")
|
||||
public List<Item> items;
|
||||
}
|
||||
|
||||
public static class Item {
|
||||
@SerializedName("summary")
|
||||
public String summary;
|
||||
|
||||
@SerializedName("description")
|
||||
public String description;
|
||||
|
||||
@SerializedName("location")
|
||||
public String location;
|
||||
|
||||
@SerializedName("start")
|
||||
public TimePoint start;
|
||||
|
||||
@SerializedName("end")
|
||||
public TimePoint end;
|
||||
|
||||
@SerializedName("reminders")
|
||||
public Reminders reminders;
|
||||
}
|
||||
|
||||
public static class TimePoint {
|
||||
@SerializedName("dateTime")
|
||||
public String dateTime; // Format: 2025-12-15T10:00:00+01:00
|
||||
|
||||
@SerializedName("date")
|
||||
public String date; // Format: 2025-12-15 (for heldags)
|
||||
}
|
||||
|
||||
public static class Reminders {
|
||||
@SerializedName("useDefault")
|
||||
public boolean useDefault;
|
||||
|
||||
@SerializedName("overrides")
|
||||
public List<Override> overrides;
|
||||
}
|
||||
|
||||
public static class Override {
|
||||
@SerializedName("method")
|
||||
public String method; // f.eks "popup"
|
||||
|
||||
@SerializedName("minutes")
|
||||
public int minutes;
|
||||
}
|
||||
}
|
||||
|
||||
============================================================
|
||||
FILSTI: app\src\main\java\com\kbs\kbsintranett\GravityEntryResponse.java
|
||||
============================================================
|
||||
|
|
@ -3767,7 +3959,6 @@ public class HomeFragment extends Fragment {
|
|||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Håndter svar på kalendertillatelse
|
||||
requestPermissionLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.RequestPermission(),
|
||||
|
|
@ -3778,7 +3969,6 @@ public class HomeFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Start bakgrunnsjobb for varsling (kjører hver 15. minutt)
|
||||
startNotificationWorker();
|
||||
}
|
||||
|
|
@ -3792,7 +3982,6 @@ public class HomeFragment extends Fragment {
|
|||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// 0. Profil-knapp
|
||||
View profileBtn = view.findViewById(R.id.btn_profile);
|
||||
if (profileBtn != null) {
|
||||
|
|
@ -3804,7 +3993,6 @@ public class HomeFragment extends Fragment {
|
|||
calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
// Sett tom adapter midlertidig
|
||||
calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {}));
|
||||
|
||||
// "Se alle" knapp for kalender
|
||||
TextView viewAllCalendar = view.findViewById(R.id.btn_view_all_calendar);
|
||||
if (viewAllCalendar != null) {
|
||||
|
|
@ -3832,7 +4020,6 @@ public class HomeFragment extends Fragment {
|
|||
newsRecycler.setNestedScrollingEnabled(false);
|
||||
// Sett tom adapter midlertidig
|
||||
newsRecycler.setAdapter(new NewsAdapter(new ArrayList<>(), item -> {}));
|
||||
|
||||
// "Se alle" knapp for nyheter
|
||||
TextView viewAllNews = view.findViewById(R.id.btn_view_all_news);
|
||||
if (viewAllNews != null) {
|
||||
|
|
@ -3848,26 +4035,23 @@ public class HomeFragment extends Fragment {
|
|||
// 1. Hent personlige hendelser først (fra CalendarManager)
|
||||
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext());
|
||||
|
||||
// 2. Hent API-hendelser fra WordPress
|
||||
WordPressApiService apiService = RetrofitClient.getApiService();
|
||||
apiService.getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
||||
// 2. Hent API-hendelser DIREKTE fra Google
|
||||
String url = CalendarManager.getGoogleCalendarUrl();
|
||||
RetrofitClient.getApiService().getDirectGoogleEvents(url).enqueue(new Callback<GoogleCalendarModels.Response>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
||||
public void onResponse(Call<GoogleCalendarModels.Response> call, Response<GoogleCalendarModels.Response> response) {
|
||||
if (!isAdded()) return;
|
||||
|
||||
List<CalendarEvent> apiEvents = new ArrayList<>();
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
for (CalendarEvent e : response.body()) {
|
||||
CalendarManager.formatEventForUI(e); // Formatér datoer
|
||||
apiEvents.add(e);
|
||||
}
|
||||
// Konverter fra Google-modell til KBS-modell
|
||||
apiEvents = CalendarManager.convertGoogleResponse(response.body());
|
||||
}
|
||||
|
||||
// 3. Flett listene (API + Personlig) og sorter
|
||||
List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
|
||||
|
||||
// 4. Filtrer ut hendelser som har passert (vis kun fremtidige + i dag)
|
||||
// (CalendarManager henter 1 år bakover, så vi må filtrere for "Topp 5 kommende")
|
||||
List<CalendarEvent> upcomingEvents = new ArrayList<>();
|
||||
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
|
||||
|
||||
|
|
@ -3890,14 +4074,12 @@ public class HomeFragment extends Fragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
||||
public void onFailure(Call<GoogleCalendarModels.Response> call, Throwable t) {
|
||||
if (!isAdded()) return;
|
||||
// Hvis API feiler, vis bare personlige events hvis vi har noen
|
||||
if (!deviceEvents.isEmpty()) {
|
||||
List<CalendarEvent> top5 = new ArrayList<>();
|
||||
// Filtrer og plukk topp 5 fra lokale også
|
||||
for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) top5.add(deviceEvents.get(i));
|
||||
|
||||
recyclerView.setAdapter(new CalendarAdapter(top5, event -> {
|
||||
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
|
||||
sheet.show(getParentFragmentManager(), "CalendarDetails");
|
||||
|
|
@ -3931,7 +4113,8 @@ public class HomeFragment extends Fragment {
|
|||
for (WpPost post : wpPosts) {
|
||||
try {
|
||||
Date date = rawFormat.parse(post.date);
|
||||
post.date = targetFormat.format(date); // Setter pen dato
|
||||
post.date = targetFormat.format(date);
|
||||
// Setter pen dato
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
|
|
@ -4314,7 +4497,14 @@ FILSTI: app\src\main\java\com\kbs\kbsintranett\MainActivity.java
|
|||
============================================================
|
||||
package com.kbs.kbsintranett;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
|
|
@ -4322,6 +4512,8 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
||||
|
|
@ -4346,6 +4538,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
bottomNav = findViewById(R.id.bottom_nav_view);
|
||||
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.nav_host_fragment);
|
||||
|
||||
if (navHostFragment != null) {
|
||||
navController = navHostFragment.getNavController();
|
||||
NavigationUI.setupWithNavController(bottomNav, navController);
|
||||
|
|
@ -4361,10 +4554,50 @@ public class MainActivity extends AppCompatActivity {
|
|||
});
|
||||
}
|
||||
|
||||
// --- NYTT: Sjekk tillatelse for nøyaktige alarmer (Android 12+) ---
|
||||
checkExactAlarmPermission();
|
||||
|
||||
// 2. Start Silent Sign-In ved oppstart
|
||||
checkLoginState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sjekker om appen har lov til å sette nøyaktige alarmer (SCHEDULE_EXACT_ALARM).
|
||||
* Hvis ikke, spør brukeren om å gå til innstillinger.
|
||||
*/
|
||||
private void checkExactAlarmPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||
if (alarmManager != null && !alarmManager.canScheduleExactAlarms()) {
|
||||
// Vi mangler tillatelse. Vis dialog.
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Varslingstillatelse kreves")
|
||||
.setMessage("For at kalenderen skal kunne varsle deg nøyaktig når et møte starter, må du gi appen tilgang til å sette alarmer.")
|
||||
.setPositiveButton("Gå til Innstillinger", (dialog, which) -> {
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
|
||||
intent.setData(Uri.parse("package:" + getPackageName()));
|
||||
startActivity(intent);
|
||||
})
|
||||
.setNegativeButton("Senere", null)
|
||||
.show();
|
||||
} else {
|
||||
// Vi har tillatelse (eller er på eldre Android). Kjør logikk!
|
||||
runNotificationWorker();
|
||||
}
|
||||
} else {
|
||||
// Eldre Android-versjoner trenger ikke denne tillatelsen
|
||||
runNotificationWorker();
|
||||
}
|
||||
}
|
||||
|
||||
private void runNotificationWorker() {
|
||||
// --- DEBUG: TVING KJØRING AV KALENDER-SJEKK ---
|
||||
// Denne linjen kjører NotificationWorker umiddelbart ved oppstart for feilsøking.
|
||||
// Fjern eller kommenter ut denne når testingen er ferdig.
|
||||
WorkManager.getInstance(this).enqueue(OneTimeWorkRequest.from(NotificationWorker.class));
|
||||
// ----------------------------------------------
|
||||
}
|
||||
|
||||
private void checkLoginState() {
|
||||
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
|
||||
if (account == null) {
|
||||
|
|
@ -4780,13 +5013,13 @@ FILSTI: app\src\main\java\com\kbs\kbsintranett\NotificationWorker.java
|
|||
============================================================
|
||||
package com.kbs.kbsintranett;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import java.io.IOException;
|
||||
|
|
@ -4798,8 +5031,7 @@ import retrofit2.Response;
|
|||
|
||||
public class NotificationWorker extends Worker {
|
||||
|
||||
private static final String CHANNEL_ID = "kbs_calendar_channel";
|
||||
private static final String PREFS_NAME = "KBSNotificationPrefs";
|
||||
private static final String TAG = "KBS_DEBUG";
|
||||
|
||||
public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
|
|
@ -4808,73 +5040,109 @@ public class NotificationWorker extends Worker {
|
|||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
// Dette kjører i bakgrunnen
|
||||
Log.d(TAG, "NotificationWorker: Starter sjekk av kalender...");
|
||||
|
||||
try {
|
||||
// Hent events synkront (ikke enqueue)
|
||||
Response<List<CalendarEvent>> response = RetrofitClient.getApiService().getCalendarEvents().execute();
|
||||
String url = CalendarManager.getGoogleCalendarUrl();
|
||||
// Hent events synkront
|
||||
Response<GoogleCalendarModels.Response> response = RetrofitClient.getApiService().getDirectGoogleEvents(url).execute();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
checkAndNotify(response.body());
|
||||
List<CalendarEvent> events = CalendarManager.convertGoogleResponse(response.body());
|
||||
scheduleAlarms(events);
|
||||
return Result.success();
|
||||
} else {
|
||||
Log.e(TAG, "NotificationWorker: API-kall feilet. Kode: " + response.code());
|
||||
return Result.retry();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "NotificationWorker: Nettverksfeil", e);
|
||||
return Result.retry();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndNotify(List<CalendarEvent> events) {
|
||||
SharedPreferences prefs = getApplicationContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
long now = System.currentTimeMillis();
|
||||
long fifteenMinutes = 15 * 60 * 1000;
|
||||
private void scheduleAlarms(List<CalendarEvent> events) {
|
||||
Context context = getApplicationContext();
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
// Sjekk rettigheter (Android 12+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (!alarmManager.canScheduleExactAlarms()) {
|
||||
Log.e(TAG, "NotificationWorker: MANGLER fortsatt tillatelse! Gå til Innstillinger -> Apper -> KBS -> Alarmer og påminnelser.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
// Bruker en parser som er litt mer fleksibel for datoer
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||
SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||
|
||||
int countSet = 0;
|
||||
|
||||
for (CalendarEvent event : events) {
|
||||
try {
|
||||
Date eventDate;
|
||||
if (event.getRawDate().contains("T")) eventDate = isoFormat.parse(event.getRawDate());
|
||||
else eventDate = sqlFormat.parse(event.getRawDate());
|
||||
// Hopp over heldagshendelser (dato uten klokkeslett)
|
||||
if (event.getRawDate().length() == 10) continue;
|
||||
|
||||
Date eventDate = null;
|
||||
// Enkel parsing. Merk: Google sender med tidssone (+01:00),
|
||||
// men SimpleDateFormat uten 'X' vil parse dette som lokal tid hvis formatet stemmer.
|
||||
// For optimal tidssone-håndtering burde vi brukt java.time (Android 8+),
|
||||
// men dette fungerer greit så lenge telefonen er i samme sone som kalenderen.
|
||||
if (event.getRawDate().contains("T")) {
|
||||
// Kutter vekk tidssone-offset for enkel parsing til lokal tid
|
||||
String raw = event.getRawDate();
|
||||
if (raw.length() > 19) raw = raw.substring(0, 19);
|
||||
eventDate = isoFormat.parse(raw);
|
||||
}
|
||||
|
||||
if (eventDate == null) continue;
|
||||
|
||||
long diff = eventDate.getTime() - now;
|
||||
// Beregn når alarmen skal gå
|
||||
long triggerTime = eventDate.getTime() - (event.getReminderMinutes() * 60 * 1000L);
|
||||
|
||||
// Hvis eventet starter innen de neste 30 min, og ikke allerede varslet
|
||||
if (diff > 0 && diff < (30 * 60 * 1000)) {
|
||||
String eventId = event.getTitle() + event.getRawDate(); // Enkel ID
|
||||
boolean alreadyNotified = prefs.getBoolean(eventId, false);
|
||||
// Vi setter alarmen hvis tidspunktet er i fremtiden
|
||||
// Vi sjekker også at det ikke er mer enn 24 timer frem i tid (for å spare ressurser)
|
||||
if (triggerTime > now && triggerTime < (now + 24 * 60 * 60 * 1000L)) {
|
||||
|
||||
if (!alreadyNotified) {
|
||||
sendNotification(event.getTitle(), "Starter kl " + event.getTime());
|
||||
// Lagre at vi har varslet
|
||||
prefs.edit().putBoolean(eventId, true).apply();
|
||||
// Lag en unik ID for alarmen
|
||||
String uniqueIdString = event.getTitle() + event.getRawDate();
|
||||
int alarmId = uniqueIdString.hashCode();
|
||||
|
||||
Intent intent = new Intent(context, AlarmReceiver.class);
|
||||
intent.putExtra("TITLE", event.getTitle());
|
||||
intent.putExtra("MESSAGE", "Starter kl " + event.getTime());
|
||||
intent.putExtra("ID", alarmId);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
alarmId,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
);
|
||||
|
||||
// VIKTIG ENDRING: Vi setter alarmen PÅ NYTT hver gang.
|
||||
// AlarmManager overskriver automatisk hvis ID er lik.
|
||||
// Dette sikrer at alarmen faktisk ligger der, selv etter omstart av tlf.
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
||||
} else {
|
||||
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
||||
}
|
||||
|
||||
Log.i(TAG, ">>> ALARM SATT (Oppdatert): " + event.getTitle() + " -> Skal ringe: " + new Date(triggerTime));
|
||||
countSet++;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "Feil ved behandling av event: " + event.getTitle(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotification(String title, String content) {
|
||||
NotificationManager manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "KBS Kalender", NotificationManager.IMPORTANCE_HIGH);
|
||||
manager.createNotificationChannel(channel);
|
||||
if (countSet == 0) {
|
||||
Log.d(TAG, "Ingen kommende alarmer (innenfor neste 24t) funnet akkurat nå.");
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher) // Sørg for at du har et ikon her
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true);
|
||||
|
||||
manager.notify((int) System.currentTimeMillis(), builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5247,6 +5515,7 @@ import retrofit2.http.Multipart;
|
|||
import retrofit2.http.Part;
|
||||
import retrofit2.http.PartMap;
|
||||
import retrofit2.http.Query;
|
||||
import retrofit2.http.Url; // NY IMPORT
|
||||
|
||||
public interface WordPressApiService {
|
||||
@GET("wp-json/wp/v2/posts?per_page=10&_embed")
|
||||
|
|
@ -5272,9 +5541,14 @@ public interface WordPressApiService {
|
|||
@Part List<MultipartBody.Part> files
|
||||
);
|
||||
|
||||
// ENDRET: Denne brukes ikke lenger for kalender, men beholdes for bakoverkompatibilitet
|
||||
@GET("wp-json/kbs/v1/calendar/events")
|
||||
Call<List<CalendarEvent>> getCalendarEvents();
|
||||
|
||||
// NY: Direkte kall mot Google (bruker @Url for å override base URL)
|
||||
@GET
|
||||
Call<GoogleCalendarModels.Response> getDirectGoogleEvents(@Url String fullUrl);
|
||||
|
||||
@GET("wp-json/gf/v2/entries")
|
||||
Call<GravityEntryResponse> getEntries(
|
||||
@Query("form_ids") int formId,
|
||||
|
|
|
|||
Loading…
Reference in a new issue