Dette er kildekoden til et Android Studio-prosjekt.
Hver fil er separert med overskrifter.


============================================================
FILSTI: build.gradle.kts
============================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    alias(libs.plugins.android.application) apply false
}

============================================================
FILSTI: settings.gradle.kts
============================================================
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "KBS Intranett"
include(":app")
 

============================================================
FILSTI: app\build.gradle.kts
============================================================
plugins {
    alias(libs.plugins.android.application)
}

android {
    namespace = "com.kbs.kbsintranett"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.kbs.kbsintranett"
        minSdk = 28
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    // NYTT: Dette må til for å kunne bruke BuildConfig.DEBUG i koden
    buildFeatures {
        buildConfig = true
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
}

dependencies {
    implementation(libs.appcompat)
    implementation(libs.material)
    implementation(libs.activity)
    implementation(libs.constraintlayout)
    testImplementation(libs.junit)
    androidTestImplementation(libs.ext.junit)
    androidTestImplementation(libs.espresso.core)

    // Nettverk og JSON-håndtering
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation("com.google.code.gson:gson:2.10.1")

    // Navigation Component
    val navVersion = "2.8.5"
    implementation("androidx.navigation:navigation-fragment:$navVersion")
    implementation("androidx.navigation:navigation-ui:$navVersion")

    implementation("com.google.android.gms:play-services-auth:20.7.0")
    implementation("com.github.bumptech.glide:glide:4.16.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")

    implementation("androidx.work:work-runtime:2.9.0")

    // Swipe Refresh Layout
    implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
}

============================================================
FILSTI: app\proguard-rules.pro
============================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

============================================================
FILSTI: app\src\androidTest\java\com\kbs\kbsintranett\ExampleInstrumentedTest.java
============================================================
package com.kbs.kbsintranett;

import android.content.Context;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    @Test
    public void useAppContext() {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        assertEquals("com.kbs.kbsintranett", appContext.getPackageName());
    }
}

============================================================
FILSTI: app\src\main\AndroidManifest.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    <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" />
    <uses-permission android:name="android.permission.USE_EXACT_ALARM" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:name=".KbsApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.KBSIntranett"
        android:usesCleartextTraffic="true"
        tools:targetApi="31">

        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:exported="true"
            android:theme="@style/Theme.KBSIntranett">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".WebViewActivity" />

        <receiver
            android:name=".AlarmReceiver"
            android:enabled="true"
            android:exported="false" />

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.kbs.kbsintranett.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

    </application>

</manifest>

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\AlarmReceiver.java
============================================================
package com.kbs.kbsintranett;

import android.Manifest;
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.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

public class AlarmReceiver extends BroadcastReceiver {

    private static final String CHANNEL_ID = "kbs_calendar_channel";
    private static final String CHANNEL_NAME = "KBS Kalendervarsler";

    @Override
    public void onReceive(Context context, Intent intent) {
        String title = intent.getStringExtra("TITLE");
        String message = intent.getStringExtra("MESSAGE");
        int notificationId = intent.getIntExtra("ID", 0);

        createNotificationChannel(context);
        showNotification(context, title, message, notificationId);
    }

    private void showNotification(Context context, String title, String message, int notificationId) {
        // Sjekk rettigheter for Android 13+
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                // Vi kan ikke vise varsel uten rettighet.
                return;
            }
        }

        // Intent for hva som skjer når man trykker på varselet (åpne appen)
        Intent tapIntent = new Intent(context, MainActivity.class);
        tapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                context,
                0,
                tapIntent,
                PendingIntent.FLAG_IMMUTABLE
        );

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_foreground) // Pass på at du har et ikon her, ellers bruk R.mipmap.ic_launcher
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true);

        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
        notificationManager.notify(notificationId, builder.build());
    }

    private void createNotificationChannel(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, importance);
            channel.setDescription("Varsler for kalenderhendelser i KBS Intranett");

            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(channel);
            }
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\AuthRepository.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\AuthRepository.java
package com.kbs.kbsintranett;

import android.util.Log;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class AuthRepository {

    private static final String TAG = "AuthRepository";

    // Interface for å gi beskjed tilbake til Activity/Fragment
    public interface AuthCallback {
        void onSuccess(String role);
        void onError(String message);
    }

    /**
     * Utfører selve API-kallet mot WordPress.
     * Denne brukes nå av både MainActivity (Silent Sign-In) og LoginFragment (Manuell).
     */
    public static void loginToWordPress(String googleIdToken, String displayName, String email, String photoUrl, AuthCallback callback) {

        // 1. Lagre Google-info midlertidig
        UserManager.getInstance().setUserData(displayName, email, googleIdToken, photoUrl);

        // 2. Gjør klar request
        LoginRequest request = new LoginRequest(googleIdToken);

        // 3. Send til WordPress
        RetrofitClient.getApiService().googleLogin(request).enqueue(new Callback<LoginResponse>() {
            @Override
            public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
                if (response.isSuccessful() && response.body() != null && response.body().success) {
                    // SUKSESS!
                    String cookie = response.body().fullCookie;
                    String role = response.body().role;

                    // NYTT: Hent utvidet info fra responsen
                    int userId = response.body().userId;
                    String fName = response.body().firstName;
                    String lName = response.body().lastName;
                    String stilling = response.body().stilling;
                    String mobil = response.body().mobiltelefon;

                    Log.d(TAG, "WordPress Login suksess! Rolle: " + role + ", UserID: " + userId);

                    // Lagre cookie, rolle og ID
                    UserManager.getInstance().setCookie(cookie);
                    UserManager.getInstance().setUserRole(role);
                    UserManager.getInstance().setUserId(userId);

                    // Lagre utvidet info i UserManager
                    UserManager.getInstance().setExtendedUserInfo(fName, lName, stilling, mobil);

                    // Lagre listen over skrivbare kalendere
                    UserManager.getInstance().setWriteableCalendars(response.body().writeableCalendars);

                    callback.onSuccess(role);

                } else {
                    Log.e(TAG, "WordPress Login nektet. Kode: " + response.code());
                    callback.onError("Kunne ikke logge inn på Intranettet (Kode: " + response.code() + ")");
                }
            }

            @Override
            public void onFailure(Call<LoginResponse> call, Throwable t) {
                Log.e(TAG, "Nettverksfeil mot WP", t);
                callback.onError("Nettverksfeil: " + t.getMessage());
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarAdapter.java
============================================================
package com.kbs.kbsintranett;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.ViewHolder> {

    private List<CalendarEvent> events;
    private final OnItemClickListener listener;

    public interface OnItemClickListener {
        void onItemClick(CalendarEvent event);
    }

    // Oppdatert konstruktør som tar imot en listener
    public CalendarAdapter(List<CalendarEvent> events, OnItemClickListener listener) {
        this.events = events;
        this.listener = listener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_calendar, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        CalendarEvent event = events.get(position);
        holder.day.setText(event.getDay());
        holder.month.setText(event.getMonth());
        holder.time.setText(event.getTime());
        holder.title.setText(event.getTitle());

        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onItemClick(event);
            }
        });
    }

    @Override
    public int getItemCount() {
        return events.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView day, month, title, time;

        public ViewHolder(View view) {
            super(view);
            day = view.findViewById(R.id.cal_day);
            month = view.findViewById(R.id.cal_month);
            title = view.findViewById(R.id.cal_title);
            time = view.findViewById(R.id.cal_time);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarDetailsBottomSheet.java
============================================================
package com.kbs.kbsintranett;

import android.app.AlertDialog;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.navigation.Navigation;
import androidx.navigation.fragment.NavHostFragment;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.gson.JsonElement;
import java.util.ArrayList;
import java.util.Collections;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
    private CalendarEvent event;

    public CalendarDetailsBottomSheet(CalendarEvent event) {
        this.event = event;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.bottom_sheet_calendar_details, container, false);

        TextView title = view.findViewById(R.id.sheet_title);
        TextView time = view.findViewById(R.id.sheet_time);
        TextView desc = view.findViewById(R.id.sheet_desc);
        TextView loc = view.findViewById(R.id.sheet_location);
        LinearLayout adminLayout = view.findViewById(R.id.layout_admin_buttons);
        Button btnDelete = view.findViewById(R.id.btn_delete);
        Button btnEdit = view.findViewById(R.id.btn_edit);

        title.setText(event.getTitle());
        time.setText(event.getTime() + " (" + event.getDay() + ". " + event.getMonth() + ")");

        if (!event.getDescription().isEmpty()) {
            // Skjul #varsel-taggen for visning
            String cleanDesc = event.getDescription().replaceAll("#varsel:[\\d,]+", "").trim();
            desc.setText(Html.fromHtml(cleanDesc, Html.FROM_HTML_MODE_COMPACT));
            desc.setVisibility(View.VISIBLE);
            desc.setMovementMethod(LinkMovementMethod.getInstance());
        } else {
            desc.setVisibility(View.GONE);
        }

        if (!event.getLocation().isEmpty()) {
            loc.setText("Sted: " + event.getLocation());
            loc.setVisibility(View.VISIBLE);
        } else {
            loc.setVisibility(View.GONE);
        }

        // Sjekk admin-rettigheter
        if (UserManager.getInstance().isEditorOrAbove()) {
            adminLayout.setVisibility(View.VISIBLE);

            btnDelete.setOnClickListener(v -> confirmDelete());

            btnEdit.setOnClickListener(v -> {
                // Send eventet videre til redigering
                Bundle bundle = new Bundle();
                bundle.putSerializable("edit_event", event);
                // Vi må navigere via parent fragmentets navController
                NavHostFragment.findNavController(this).navigate(R.id.navigation_create_event, bundle);
                dismiss();
            });
        }

        return view;
    }

    private void confirmDelete() {
        new AlertDialog.Builder(getContext())
                .setTitle("Slett hendelse")
                .setMessage("Er du sikker på at du vil slette '" + event.getTitle() + "'?")
                .setPositiveButton("Slett", (dialog, which) -> deleteEvent())
                .setNegativeButton("Avbryt", null)
                .show();
    }

    private void deleteEvent() {
        // Vi må sende en CreateEventRequest med ID for å slette
        // Vi trenger ikke fylle ut alt, bare ID og kalendertype
        // Siden vi ikke vet nøyaktig hvilken kalender den kom fra (APIet gir ikke det),
        // prøver vi "Felles" som default, eller prøver å slette fra ID.
        // PHP-koden vi lagde støtter sletting basert på ID hvis vi sender riktig kalendertype.
        // For nå antar vi "Felles" eller looper i backend. I V11.3 PHP scriptet sletter den basert på ID i valgt kalender.

        CreateEventRequest req = new CreateEventRequest(
                "", "", "", "", "", "Felles", new ArrayList<>(), false, ""
        );
        req.id = event.getId();

        RetrofitClient.getApiService().deleteCalendarEvent(req).enqueue(new Callback<JsonElement>() {
            @Override
            public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
                if (response.isSuccessful()) {
                    Toast.makeText(getContext(), "Slettet!", Toast.LENGTH_SHORT).show();
                    dismiss();
                    // Her burde vi ideelt sett oppdatert listen bak, men brukeren kan dra for å oppdatere
                } else {
                    Toast.makeText(getContext(), "Kunne ikke slette (Er det en felles-hendelse?)", Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onFailure(Call<JsonElement> call, Throwable t) {
                Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarEvent.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class CalendarEvent implements Serializable  {
    @SerializedName("id")
    private String id;
    @SerializedName("title")
    private String title;

    @SerializedName("start_date")
    private String rawDate;

    @SerializedName("end_date")
    private String rawEndDate;

    @SerializedName("description")
    private String description;

    @SerializedName("location")
    private String location;

    // V11.0: Liste av minutter (f.eks [15, 60])
    @SerializedName("reminders")
    private List<Integer> reminders = new ArrayList<>();

    // UI-hjelpefelter
    private String day;
    private String month;
    private String time;

    // Konstruktør
    public CalendarEvent(String title, String rawDate, String rawEndDate, String description, String location) {
        this.title = title;
        this.rawDate = rawDate;
        this.rawEndDate = rawEndDate;
        this.description = description;
        this.location = location;
    }

    public String getId() { return id; }
    public String getTitle() { return title; }
    public String getRawDate() { return rawDate; }
    public String getRawEndDate() { return rawEndDate; }
    public String getDescription() { return description != null ? description : ""; }
    public String getLocation() { return location != null ? location : ""; }

    // Henter listen. Hvis den er null (gamle data), returner tom liste.
    public List<Integer> getReminders() {
        return reminders != null ? reminders : new ArrayList<>();
    }

// --- KOMPATIBILITETS-METODER (For å fikse build-feil i CalendarManager og Worker) ---

    // Brukes av CalendarManager.java for lokale events
    public void setReminderMinutes(int minutes) {
        this.reminders = new ArrayList<>();
        if (minutes > 0) {
            this.reminders.add(minutes);
        }
    }

    // Brukes hvis gammel kode prøver å hente ett tall. Returnerer det første i listen.
    public int getReminderMinutes() {
        if (reminders != null && !reminders.isEmpty()) {
            return reminders.get(0);
        }
        return 0;
    }

    // --- UI SETTERS/GETTERS ---
    public String getDay() { return day; }
    public void setDay(String day) { this.day = day; }
    public String getMonth() { return month; }
    public void setMonth(String month) { this.month = month; }
    public String getTime() { return time; }
    public void setTime(String time) { this.time = time; }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarFullFragment.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class CalendarFullFragment extends Fragment {

    private RecyclerView recyclerView;
    private ProgressBar progressBar;
    private TextView emptyView;
    private LinearLayoutManager layoutManager;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_calendar_full, container, false);
    }

    @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);
        ImageView backBtn = view.findViewById(R.id.btn_back_calendar);

        layoutManager = new LinearLayoutManager(getContext());
        recyclerView.setLayoutManager(layoutManager);
        backBtn.setOnClickListener(v -> Navigation.findNavController(view).navigateUp());

        fetchAllEvents();
    }

    private void fetchAllEvents() {
        progressBar.setVisibility(View.VISIBLE);
        // Hent personlige hendelser
        List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext());

        // Hent felles hendelser fra WordPress Proxy (sikrer at vi får reminders)
        RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
            @Override
            public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                List<CalendarEvent> apiEvents = new ArrayList<>();
                if (response.isSuccessful() && response.body() != null) {
                    apiEvents = response.body();
                    // Formater data for visning
                    for (CalendarEvent e : apiEvents) {
                        CalendarManager.formatEventForUI(e);
                    }
                }

                List<CalendarEvent> allEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents);

                if (allEvents.isEmpty()) {
                    emptyView.setVisibility(View.VISIBLE);
                    recyclerView.setVisibility(View.GONE);
                } else {
                    emptyView.setVisibility(View.GONE);
                    recyclerView.setVisibility(View.VISIBLE);

                    CalendarAdapter adapter = new CalendarAdapter(allEvents, event -> {
                        CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
                        sheet.show(getParentFragmentManager(), "CalendarDetails");
                    });
                    recyclerView.setAdapter(adapter);

                    scrollToToday(allEvents);
                }
            }

            @Override
            public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                if (!deviceEvents.isEmpty()) {
                    CalendarAdapter adapter = new CalendarAdapter(deviceEvents, event -> {
                        CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
                        sheet.show(getParentFragmentManager(), "CalendarDetails");
                    });
                    recyclerView.setAdapter(adapter);
                    scrollToToday(deviceEvents);
                } else {
                    emptyView.setText("Ingen hendelser funnet.");
                    emptyView.setVisibility(View.VISIBLE);
                }
            }
        });
    }

    private void scrollToToday(List<CalendarEvent> events) {
        String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
        int scrollIndex = 0;

        for (int i = 0; i < events.size(); i++) {
            String raw = events.get(i).getRawDate();
            if (raw != null && raw.compareTo(today) >= 0) {
                scrollIndex = i;
                break;
            }
        }

        if (scrollIndex > 0) {
            layoutManager.scrollToPositionWithOffset(scrollIndex, 0);
        }
    }

}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CalendarManager.java
============================================================
package com.kbs.kbsintranett;

import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.provider.CalendarContract;
import androidx.core.content.ContextCompat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

public class CalendarManager {
    private static List<String> getKbsCalendarIds(Context context) {
        List<String> ids = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
            return ids;
        }
        String[] projection = new String[] {
                CalendarContract.Calendars._ID,
                CalendarContract.Calendars.ACCOUNT_NAME
        };
        String selection = CalendarContract.Calendars.ACCOUNT_NAME + " LIKE ?";
        String[] selectionArgs = new String[] {"%@kbs.no"};

        try (Cursor cursor = context.getContentResolver().query(
                CalendarContract.Calendars.CONTENT_URI,
                projection,
                selection,
                selectionArgs,
                null
        )) {
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    ids.add(cursor.getString(0));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ids;
    }

    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;
        }

        List<String> kbsCalendarIds = getKbsCalendarIds(context);
        if (kbsCalendarIds.isEmpty()) {
            return deviceEvents;
        }

        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,
                CalendarContract.Events.DTEND,
                CalendarContract.Events.DESCRIPTION,
                CalendarContract.Events.EVENT_LOCATION,
                CalendarContract.Events.ALL_DAY
        };

        StringBuilder selection = new StringBuilder("(");
        List<String> selectionArgsList = new ArrayList<>();

        for (int i = 0; i < kbsCalendarIds.size(); i++) {
            selection.append(CalendarContract.Events.CALENDAR_ID).append(" = ?");
            selectionArgsList.add(kbsCalendarIds.get(i));
            if (i < kbsCalendarIds.size() - 1) {
                selection.append(" OR ");
            }
        }
        selection.append(") AND ");
        selection.append(CalendarContract.Events.DTSTART).append(" >= ? AND ");
        selection.append(CalendarContract.Events.DTSTART).append(" <= ?");

        selectionArgsList.add(String.valueOf(startMillis));
        selectionArgsList.add(String.valueOf(endMillis));

        String[] selectionArgs = selectionArgsList.toArray(new String[0]);

        try (Cursor cursor = context.getContentResolver().query(
                CalendarContract.Events.CONTENT_URI,
                projection,
                selection.toString(),
                selectionArgs,
                CalendarContract.Events.DTSTART + " ASC"
        )) {
            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);
                    long dtEnd = cursor.getLong(2);
                    String desc = cursor.getString(3);
                    String loc = cursor.getString(4);
                    int allDay = cursor.getInt(5);

                    String rawStart;
                    String rawEnd;

                    if (allDay == 1) {
                        SimpleDateFormat shortFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
                        rawStart = shortFormat.format(new Date(dtStart));
                        rawEnd = shortFormat.format(new Date(dtEnd));
                    } else {
                        rawStart = isoFormat.format(new Date(dtStart));
                        rawEnd = isoFormat.format(new Date(dtEnd));
                    }

                    CalendarEvent event = new CalendarEvent(title, rawStart, rawEnd, desc, loc);
                    // Denne metoden eksisterer nå i CalendarEvent (se fil 1)
                    event.setReminderMinutes(0);

                    formatEventForUI(event);
                    deviceEvents.add(event);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return deviceEvents;
    }

    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"));

        try {
            Date date = null;
            Date endDate = null;
            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);
                isAllDay = true;
                if (event.getRawEndDate() != null && event.getRawEndDate().length() == 10) {
                    endDate = shortFmt.parse(event.getRawEndDate());
                }
            }
            else if (raw.contains("T")) {
                SimpleDateFormat isoFmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
                isoFmt.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
                date = isoFmt.parse(raw);
                if (event.getRawEndDate() != null && event.getRawEndDate().contains("T")) {
                    endDate = isoFmt.parse(event.getRawEndDate());
                }
            }
            else {
                SimpleDateFormat sqlFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
                sqlFmt.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
                date = sqlFmt.parse(raw);
                if (event.getRawEndDate() != null) {
                    endDate = sqlFmt.parse(event.getRawEndDate());
                }
            }

            if (date != null) {
                event.setDay(outputDay.format(date));
                event.setMonth(outputMonth.format(date).toUpperCase());

                if (isAllDay) {
                    event.setTime("Hele dagen");
                } else {
                    String timeStr = outputTime.format(date);
                    if (endDate != null) {
                        timeStr += " - " + outputTime.format(endDate);
                    }
                    event.setTime("Kl. " + timeStr);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            event.setDay("??");
            event.setMonth("???");
            event.setTime("Feil dato");
        }
    }

    public static List<CalendarEvent> mergeAndSort(List<CalendarEvent> apiEvents, List<CalendarEvent> deviceEvents) {
        List<CalendarEvent> all = new ArrayList<>(apiEvents);
        all.addAll(deviceEvents);

        Collections.sort(all, (e1, e2) -> {
            String d1 = e1.getRawDate() != null ? e1.getRawDate() : "";
            String d2 = e2.getRawDate() != null ? e2.getRawDate() : "";
            return d1.compareTo(d2);
        });

        return all;
    }

}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CategoryAdapter.java
============================================================
package com.kbs.kbsintranett;

import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.ViewHolder> {

    private List<String> categories;
    private String selectedCategory = "Alle"; // Standardvalg
    private OnCategoryClickListener listener;

    public interface OnCategoryClickListener {
        void onCategoryClick(String category);
    }

    public CategoryAdapter(List<String> categories, OnCategoryClickListener listener) {
        this.categories = categories;
        this.listener = listener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_category, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        String category = categories.get(position);
        holder.name.setText(category);

        if (category.equals(selectedCategory)) {
            // Valgt stil (Blå bakgrunn, hvit tekst)
            holder.name.setBackgroundResource(R.drawable.bg_category_selected);
            holder.name.setTextColor(Color.WHITE);
        } else {
            // Ikke valgt stil (Hvit bakgrunn, mørk tekst)
            holder.name.setBackgroundResource(R.drawable.bg_category_unselected);
            holder.name.setTextColor(Color.parseColor("#333333"));
        }

        holder.itemView.setOnClickListener(v -> {
            selectedCategory = category;
            notifyDataSetChanged(); // Oppdater alle for å flytte markering
            listener.onCategoryClick(category);
        });
    }

    @Override
    public int getItemCount() {
        return categories.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView name;
        public ViewHolder(View view) {
            super(view);
            name = view.findViewById(R.id.category_name);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\ChoicesAdapter.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class ChoicesAdapter implements JsonDeserializer<List<GravityField.Choice>> {
    @Override
    public List<GravityField.Choice> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        // Hvis feltet er "null" eller en tom tekststreng, returner en tom liste
        if (json.isJsonNull() || (json.isJsonPrimitive() && json.getAsString().isEmpty())) {
            return new ArrayList<>();
        }

        // Hvis det faktisk er en liste (Array), les den som vanlig
        if (json.isJsonArray()) {
            List<GravityField.Choice> list = new ArrayList<>();
            for (JsonElement e : json.getAsJsonArray()) {
                list.add(context.deserialize(e, GravityField.Choice.class));
            }
            return list;
        }

        // Hvis vi får noe annet rart (f.eks. en tekst som ikke er tom), ignorer det for å unngå krasj
        return new ArrayList<>();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\ConditionalLogicAdapter.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;

public class ConditionalLogicAdapter implements JsonDeserializer<GravityField.ConditionalLogic> {
    @Override
    public GravityField.ConditionalLogic deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        // Hvis feltet er en streng (f.eks tom streng ""), returner null
        if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) {
            return null;
        }

        // Hvis det er et objekt, bruk standard deserialisering
        if (json.isJsonObject()) {
            // Vi må manuelt deserialisere for å unngå uendelig løkke hvis vi bare kaller context.deserialize på samme type
            // Enkleste måte er å la Gson gjøre jobben på innholdet
            return new com.google.gson.Gson().fromJson(json, GravityField.ConditionalLogic.class);
        }

        return null;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CreateEventFragment.java
============================================================
package com.kbs.kbsintranett;

import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.gson.JsonElement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class CreateEventFragment extends Fragment {
    private EditText etTitle, etDesc, etLocation;
    private Spinner spinnerCalendar, spinnerRecurrence;
    private ChipGroup chipGroupReminders;
    private Switch switchAllDay;
    private TextView txtPreview;
    private Button btnStartDate, btnStartTime, btnEndDate, btnEndTime, btnSave;

    private Calendar startCal = Calendar.getInstance();
    private Calendar endCal = Calendar.getInstance();

    private String selectedRRule = null;
    private boolean isCustomRecurrence = false;

    // EDIT MODE
    private CalendarEvent eventToEdit = null;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_create_event, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        etTitle = view.findViewById(R.id.et_title);
        etDesc = view.findViewById(R.id.et_desc);
        etLocation = view.findViewById(R.id.et_location);
        switchAllDay = view.findViewById(R.id.switch_all_day);

        spinnerCalendar = view.findViewById(R.id.spinner_calendar);
        spinnerRecurrence = view.findViewById(R.id.spinner_recurrence);
        chipGroupReminders = view.findViewById(R.id.chip_group_reminders);

        txtPreview = view.findViewById(R.id.txt_time_preview);

        btnStartDate = view.findViewById(R.id.btn_start_date);
        btnStartTime = view.findViewById(R.id.btn_start_time);
        btnEndDate = view.findViewById(R.id.btn_end_date);
        btnEndTime = view.findViewById(R.id.btn_end_time);

        btnSave = view.findViewById(R.id.btn_save_event);

        // Initialiser tid (neste hele time)
        startCal.add(Calendar.HOUR_OF_DAY, 1);
        startCal.set(Calendar.MINUTE, 0);
        endCal.setTime(startCal.getTime());
        endCal.add(Calendar.HOUR_OF_DAY, 1);

        setupCalendarSpinner();
        setupReminderChips();

        // SJEKK OM VI ER I REDIGERINGS-MODUS
        if (getArguments() != null && getArguments().containsKey("edit_event")) {
            eventToEdit = (CalendarEvent) getArguments().getSerializable("edit_event");
            prefillForm(eventToEdit);
            btnSave.setText("Oppdater Hendelse");
        }

        updateRecurrenceSpinner();
        updateUI();

        // Listeners
        switchAllDay.setOnCheckedChangeListener((btn, isChecked) -> updateUI());

        btnStartDate.setOnClickListener(v -> pickDate(startCal, true));
        btnEndDate.setOnClickListener(v -> pickDate(endCal, false));

        btnStartTime.setOnClickListener(v -> pickTime(startCal));
        btnEndTime.setOnClickListener(v -> pickTime(endCal));

        btnSave.setOnClickListener(v -> submitEvent());

        spinnerRecurrence.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            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;

                String selected = parent.getItemAtPosition(position).toString();
                if (selected.equals("Egendefinert...")) {
                    showCustomRecurrenceDialog();
                } else if (selected.startsWith("Ikke gjenta")) {
                    selectedRRule = null;
                } else {
                    generateStandardRRule(position);
                }
            }
            @Override public void onNothingSelected(AdapterView<?> parent) {}
        });
    }

    private void prefillForm(CalendarEvent event) {
        etTitle.setText(event.getTitle());

        // Rens beskrivelsen for #varsel tag
        String cleanDesc = event.getDescription().replaceAll("#varsel:[\\d,]+", "").trim();
        etDesc.setText(cleanDesc);
        etLocation.setText(event.getLocation());

        // Dato-parsing
        try {
            SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
            SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());

            String start = event.getRawDate();
            if (start != null) {
                if (start.length() == 10) {
                    // Heldags
                    switchAllDay.setChecked(true);
                    Date d = simpleFormat.parse(start);
                    startCal.setTime(d);

                    if (event.getRawEndDate() != null) {
                        Date e = simpleFormat.parse(event.getRawEndDate());
                        Calendar c = Calendar.getInstance();
                        c.setTime(e);
                        c.add(Calendar.DAY_OF_MONTH, -1);
                        endCal.setTime(c.getTime());
                    } else {
                        endCal.setTime(d);
                    }
                } else if (start.contains("T")) {
                    // Vanlig tid (kutt tidssone)
                    if (start.length() > 19) start = start.substring(0, 19);
                    startCal.setTime(isoFormat.parse(start));

                    String end = event.getRawEndDate();
                    if (end != null && end.contains("T")) {
                        if (end.length() > 19) end = end.substring(0, 19);
                        endCal.setTime(isoFormat.parse(end));
                    } else {
                        endCal.setTime(startCal.getTime());
                        endCal.add(Calendar.HOUR_OF_DAY, 1);
                    }
                }
            }

            // Varsler
            List<Integer> existingReminders = event.getReminders();
            if (!existingReminders.isEmpty()) {
                // Fjern alle sjekkmerker først (15 min er default checked)
                for (int i = 0; i < chipGroupReminders.getChildCount(); i++) {
                    ((Chip) chipGroupReminders.getChildAt(i)).setChecked(false);
                }
                for (int min : existingReminders) {
                    checkChipByMinutes(min);
                }
            }

        } catch (Exception e) {
            Log.e("KBS_EDIT", "Feil ved prefill", e);
        }
    }

    private void checkChipByMinutes(int minutes) {
        for (int i = 0; i < chipGroupReminders.getChildCount(); i++) {
            Chip chip = (Chip) chipGroupReminders.getChildAt(i);
            if ((int)chip.getTag() == minutes) {
                chip.setChecked(true);
            }
        }
    }

    private void setupCalendarSpinner() {
        // HENT DYNAMISK LISTE FRA USERMANAGER
        List<String> calendars = UserManager.getInstance().getWriteableCalendars();

        if (calendars.isEmpty()) calendars.add("Felles");

        spinnerCalendar.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, calendars));
    }

    private void setupReminderChips() {
        addChip("Ved start", 0);
        addChip("5 min", 5);
        addChip("10 min", 10);
        addChip("15 min", 15);
        addChip("30 min", 30);
        addChip("1 time", 60);
        addChip("2 timer", 120);
        addChip("1 dag", 1440);
        addChip("2 dager", 2880);
        addChip("1 uke", 10080);

        if (eventToEdit == null) checkChipByMinutes(15);
    }

    private void addChip(String text, int minutes) {
        Chip chip = new Chip(getContext());
        chip.setText(text);
        chip.setTag(minutes);
        chip.setCheckable(true);
        chip.setClickable(true);
        chipGroupReminders.addView(chip);
    }

    private List<Integer> getSelectedReminders() {
        List<Integer> selected = new ArrayList<>();
        for (int i = 0; i < chipGroupReminders.getChildCount(); i++) {
            Chip chip = (Chip) chipGroupReminders.getChildAt(i);
            if (chip.isChecked()) {
                selected.add((Integer) chip.getTag());
            }
        }
        return selected;
    }

    private void updateRecurrenceSpinner() {
        String dayName = new SimpleDateFormat("EEEE", new Locale("no")).format(startCal.getTime());
        int dayOfMonth = startCal.get(Calendar.DAY_OF_MONTH);
        String monthName = new SimpleDateFormat("MMMM", new Locale("no")).format(startCal.getTime());

        int weekNo = (startCal.get(Calendar.DAY_OF_MONTH) - 1) / 7 + 1;
        String nthDayString = "månedlig den " + weekNo + ". " + dayName + "en";

        List<String> options = new ArrayList<>();
        options.add("Ikke gjenta");
        options.add("Daglig");
        options.add("Ukentlig på " + dayName);
        options.add("Månedlig den " + dayOfMonth + ".");
        options.add(capitalize(nthDayString));
        options.add("Årlig den " + dayOfMonth + ". " + monthName);
        options.add("Hver ukedag (man-fre)");
        options.add("Egendefinert...");

        ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, options);
        spinnerRecurrence.setAdapter(adapter);
    }

    private void generateStandardRRule(int position) {
        switch (position) {
            case 1: selectedRRule = "RRULE:FREQ=DAILY"; break;
            case 2: selectedRRule = "RRULE:FREQ=WEEKLY"; break;
            case 3: selectedRRule = "RRULE:FREQ=MONTHLY"; break;
            case 4:
                int weekNo = (startCal.get(Calendar.DAY_OF_MONTH) - 1) / 7 + 1;
                String dayCode = getDayCode(startCal.get(Calendar.DAY_OF_WEEK));
                selectedRRule = "RRULE:FREQ=MONTHLY;BYDAY=" + weekNo + dayCode;
                break;
            case 5: selectedRRule = "RRULE:FREQ=YEARLY"; break;
            case 6: selectedRRule = "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR"; break;
            default: selectedRRule = null;
        }
    }

    private void showCustomRecurrenceDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_custom_recurrence, null);

        EditText etInterval = view.findViewById(R.id.et_interval);
        Spinner spinnerFreq = view.findViewById(R.id.spinner_freq);
        View layoutWeekdays = view.findViewById(R.id.layout_weekdays);

        ToggleButton[] toggles = {
                view.findViewById(R.id.tg_mon), view.findViewById(R.id.tg_tue),
                view.findViewById(R.id.tg_wed), view.findViewById(R.id.tg_thu),
                view.findViewById(R.id.tg_fri), view.findViewById(R.id.tg_sat),
                view.findViewById(R.id.tg_sun)
        };
        String[] labels = {"M", "T", "O", "T", "F", "L", "S"};
        for(int i=0; i<7; i++) { toggles[i].setText(labels[i]); toggles[i].setTextOn(labels[i]); toggles[i].setTextOff(labels[i]); }

        RadioGroup rgEnd = view.findViewById(R.id.rg_end);
        Button btnEndDatePicker = view.findViewById(R.id.btn_end_date_picker);
        EditText etCount = view.findViewById(R.id.et_count);
        Calendar customEndCal = Calendar.getInstance();
        customEndCal.add(Calendar.MONTH, 1);

        SimpleDateFormat sdf = new SimpleDateFormat("d. MMM yyyy", Locale.getDefault());
        btnEndDatePicker.setText(sdf.format(customEndCal.getTime()));

        btnEndDatePicker.setOnClickListener(v -> {
            rgEnd.check(R.id.rb_date);
            new DatePickerDialog(getContext(), (p, y, m, d) -> {
                customEndCal.set(y, m, d);
                btnEndDatePicker.setText(sdf.format(customEndCal.getTime()));
            }, customEndCal.get(Calendar.YEAR), customEndCal.get(Calendar.MONTH), customEndCal.get(Calendar.DAY_OF_MONTH)).show();
        });

        spinnerFreq.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                layoutWeekdays.setVisibility(position == 1 ? View.VISIBLE : View.GONE);
            }
            @Override public void onNothingSelected(AdapterView<?> parent) {}
        });

        builder.setView(view);
        AlertDialog dialog = builder.create();

        view.findViewById(R.id.btn_cancel).setOnClickListener(v -> {
            dialog.dismiss();
            spinnerRecurrence.setSelection(0);
        });

        view.findViewById(R.id.btn_done).setOnClickListener(v -> {
            StringBuilder rrule = new StringBuilder("RRULE:");
            int freqPos = spinnerFreq.getSelectedItemPosition();
            String freq = "";
            switch (freqPos) {
                case 0: freq = "DAILY"; break;
                case 1: freq = "WEEKLY"; break;
                case 2: freq = "MONTHLY"; break;
                case 3: freq = "YEARLY"; break;
            }
            rrule.append("FREQ=").append(freq);

            String interval = etInterval.getText().toString();
            if (!interval.isEmpty() && !interval.equals("1")) {
                rrule.append(";INTERVAL=").append(interval);
            }

            if (freq.equals("WEEKLY")) {
                List<String> days = new ArrayList<>();
                String[] codes = {"MO", "TU", "WE", "TH", "FR", "SA", "SU"};
                for (int i=0; i<7; i++) {
                    if (toggles[i].isChecked()) days.add(codes[i]);
                }
                if (!days.isEmpty()) {
                    rrule.append(";BYDAY=").append(String.join(",", days));
                }
            }

            int checkedId = rgEnd.getCheckedRadioButtonId();
            if (checkedId == R.id.rb_date) {
                customEndCal.set(Calendar.HOUR_OF_DAY, 23);
                customEndCal.set(Calendar.MINUTE, 59);
                customEndCal.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
                SimpleDateFormat utc = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US);
                utc.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
                rrule.append(";UNTIL=").append(utc.format(customEndCal.getTime()));
            } else if (checkedId == R.id.rb_count) {
                rrule.append(";COUNT=").append(etCount.getText().toString());
            }

            selectedRRule = rrule.toString();
            isCustomRecurrence = true;
            dialog.dismiss();
        });

        dialog.show();
    }

    private void pickDate(Calendar cal, boolean isStart) {
        new DatePickerDialog(getContext(), (view, y, m, d) -> {
            cal.set(y, m, d);
            if (isStart) {
                if (endCal.before(startCal)) {
                    endCal.setTime(startCal.getTime());
                    if (!switchAllDay.isChecked()) endCal.add(Calendar.HOUR_OF_DAY, 1);
                }
                updateRecurrenceSpinner();
                if (!isCustomRecurrence) spinnerRecurrence.setSelection(0);
            }
            updateUI();
        }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
    }

    private void pickTime(Calendar cal) {
        new TimePickerDialog(getContext(), (view, h, m) -> {
            cal.set(Calendar.HOUR_OF_DAY, h);
            cal.set(Calendar.MINUTE, m);
            if (cal == startCal && endCal.before(startCal)) {
                endCal.setTime(startCal.getTime());
                endCal.add(Calendar.HOUR_OF_DAY, 1);
            }
            updateUI();
        }, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show();
    }

    private void updateUI() {
        boolean isAllDay = switchAllDay.isChecked();

        btnStartTime.setVisibility(isAllDay ? View.GONE : View.VISIBLE);
        btnEndTime.setVisibility(isAllDay ? View.GONE : View.VISIBLE);

        SimpleDateFormat dateFmt = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
        SimpleDateFormat timeFmt = new SimpleDateFormat("HH:mm", Locale.getDefault());

        btnStartDate.setText(dateFmt.format(startCal.getTime()));
        btnEndDate.setText(dateFmt.format(endCal.getTime()));
        btnStartTime.setText(timeFmt.format(startCal.getTime()));
        btnEndTime.setText(timeFmt.format(endCal.getTime()));

        String preview = dateFmt.format(startCal.getTime());
        if (!isAllDay) preview += " " + timeFmt.format(startCal.getTime());
        preview += " - ";

        if (isSameDay(startCal, endCal)) {
            if (isAllDay) preview += " (Samme dag)";
            else preview += timeFmt.format(endCal.getTime());
        } else {
            preview += dateFmt.format(endCal.getTime());
            if (!isAllDay) preview += " " + timeFmt.format(endCal.getTime());
        }
        txtPreview.setText(preview);
    }

    private boolean isSameDay(Calendar c1, Calendar c2) {
        return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) &&
                c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
    }

    private String capitalize(String s) {
        if (s == null || s.isEmpty()) return s;
        return s.substring(0, 1).toUpperCase() + s.substring(1);
    }

    private String getDayCode(int calendarDay) {
        switch (calendarDay) {
            case Calendar.MONDAY: return "MO";
            case Calendar.TUESDAY: return "TU";
            case Calendar.WEDNESDAY: return "WE";
            case Calendar.THURSDAY: return "TH";
            case Calendar.FRIDAY: return "FR";
            case Calendar.SATURDAY: return "SA";
            case Calendar.SUNDAY: return "SU";
            default: return "MO";
        }
    }

    // NY: Henter navnet på kalenderen direkte fra Spinneren
    private String getCalendarSlug() {
        if (spinnerCalendar.getSelectedItem() != null) {
            return spinnerCalendar.getSelectedItem().toString();
        }
        return "Felles";
    }

    private void submitEvent() {
        String title = etTitle.getText().toString().trim();
        if (title.isEmpty()) {
            etTitle.setError("Mangler tittel");
            return;
        }

        boolean isAllDay = switchAllDay.isChecked();
        String format = isAllDay ? "yyyy-MM-dd" : "yyyy-MM-dd'T'HH:mm";
        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());

        CreateEventRequest req = new CreateEventRequest(
                title,
                etDesc.getText().toString(),
                etLocation.getText().toString(),
                sdf.format(startCal.getTime()),
                sdf.format(endCal.getTime()),
                getCalendarSlug(),
                getSelectedReminders(),
                isAllDay,
                selectedRRule
        );

        if (eventToEdit != null) {
            req.id = eventToEdit.getId();
        }

        Toast.makeText(getContext(), eventToEdit != null ? "Oppdaterer..." : "Oppretter...", Toast.LENGTH_SHORT).show();

        Call<JsonElement> call;
        if (eventToEdit != null) {
            call = RetrofitClient.getApiService().updateCalendarEvent(req);
        } else {
            call = RetrofitClient.getApiService().createCalendarEvent(req);
        }

        call.enqueue(new Callback<JsonElement>() {
            @Override
            public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
                if (response.isSuccessful()) {
                    Toast.makeText(getContext(), eventToEdit != null ? "Hendelse oppdatert!" : "Hendelse opprettet!", Toast.LENGTH_LONG).show();
                    Navigation.findNavController(getView()).navigateUp();
                } else {
                    String errorMsg = "Ukjent feil";
                    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
            public void onFailure(Call<JsonElement> call, Throwable t) {
                Log.e("KBS_ERROR", "Nettverksfeil", t);
                Toast.makeText(getContext(), "Nettverksfeil: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\CreateEventRequest.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;
public class CreateEventRequest {
    @SerializedName("id")
    public String id;
    @SerializedName("title")
    public String title;

    @SerializedName("description")
    public String description;

    @SerializedName("location")
    public String location;

    @SerializedName("start_time")
    public String startTime;

    @SerializedName("end_time")
    public String endTime;

    @SerializedName("calendar_type")
    public String calendarType;

    @SerializedName("reminders")
    public List<Integer> reminders; // Liste, ikke int

    @SerializedName("is_all_day")
    public boolean isAllDay;

    @SerializedName("recurrence")
    public String recurrence;

    // Oppdatert konstruktør som tar imot List<Integer>
    public CreateEventRequest(String title, String description, String location, String startTime, String endTime, String calendarType, List<Integer> reminders, boolean isAllDay, String recurrence) {
        this.title = title;
        this.description = description;
        this.location = location;
        this.startTime = startTime;
        this.endTime = endTime;
        this.calendarType = calendarType;
        this.reminders = reminders;
        this.isAllDay = isAllDay;
        this.recurrence = recurrence;
    }
}


============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\FormsFragment.java
============================================================
package com.kbs.kbsintranett;

import android.Manifest;
import android.animation.LayoutTransition;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.OpenableColumns;
import android.text.Editable;
import android.text.Html;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;

public class FormsFragment extends Fragment {

    private static final String TAG = "FormsFragment";
    private static final String BASE_URL_GF = "https://intranet.kbs.no/wp-json/gf/v2";

    // SKJEMA ID-er
    private static final int ID_ANSATTEOPPLYSNINGER = 1;
    private static final int ID_RUH = 4;
    private static final int ID_SIKKERHETSKURS = 9;
    private static final int ID_HMS_BEKREFTELSE = 10;
    private static final int ID_EGENMELDING = 11;
    private static final int ID_SJA = 14;
    private static final int ID_FRAVARSVARSEL = 15;
    private static final int ID_REFUSJON_UTLEGG = 16;

    private int formId = 1;

    private LinearLayout formContainer;
    private LinearLayout historyContainer;
    private View historyWrapper; // Wrapper for historikk-modulen
    private TextView txtStatus;
    private TextView lblHistory;
    private ProgressBar loadingSpinner;
    private ImageView btnToggleHistory;

    // --- HOVEDSKJEMA STATE ---
    private Map<String, View> fieldWrappers = new HashMap<>();
    private Map<String, View> inputViews = new HashMap<>();
    private Map<String, Boolean> requiredFieldsMap = new HashMap<>();
    private Map<String, Uri> fileUploads = new HashMap<>();

    // --- NESTED FORM (BARN) STATE ---
    private Map<String, View> childInputViews = new HashMap<>();
    private Map<String, Boolean> childRequiredFieldsMap = new HashMap<>();
    private Map<String, Uri> childFileUploads = new HashMap<>();
    // Lagring av Nested Entries
    private List<NestedEntry> nestedEntries = new ArrayList<>();
    private LinearLayout nestedEntriesContainer;
    private TextView totalAmountView;

    // --- FILOPPLASTING & KAMERA ---
    private String pendingFileFieldId = null;
    private boolean isSelectingForChild = false;
    private Uri currentPhotoUri = null;

    private ActivityResultLauncher<Intent> filePickerLauncher;
    private ActivityResultLauncher<Uri> takePictureLauncher;
    private ActivityResultLauncher<String> requestPermissionLauncher;

    private GravityForm currentForm;
    private final OkHttpClient client = new OkHttpClient();

    private static final Pattern TITLE_PATTERN = Pattern.compile("^(\\d+)[.\\s-]+\\s*(.*)");

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        filePickerLauncher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                result -> {
                    if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                        Uri uri = result.getData().getData();
                        if (uri != null && pendingFileFieldId != null) {
                            handleFileSelection(pendingFileFieldId, uri, isSelectingForChild);
                        }
                    }
                }
        );
        takePictureLauncher = registerForActivityResult(
                new ActivityResultContracts.TakePicture(),
                success -> {
                    if (success && currentPhotoUri != null && pendingFileFieldId != null) {
                        handleFileSelection(pendingFileFieldId, currentPhotoUri, isSelectingForChild);
                    } else if (!success) {
                        currentPhotoUri = null;
                    }
                }
        );
        requestPermissionLauncher = registerForActivityResult(
                new ActivityResultContracts.RequestPermission(),
                isGranted -> {
                    if (isGranted) {
                        openCamera();
                    } else {
                        Toast.makeText(getContext(), "Kameratillatelse er påkrevd for å ta bilde.", Toast.LENGTH_LONG).show();
                    }
                }
        );
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_forms, container, false);
        formContainer = view.findViewById(R.id.form_container);
        historyContainer = view.findViewById(R.id.historyContainer);
        historyWrapper = view.findViewById(R.id.history_wrapper);
        txtStatus = view.findViewById(R.id.txt_status);
        lblHistory = view.findViewById(R.id.lbl_history);
        loadingSpinner = view.findViewById(R.id.loading_spinner);

        btnToggleHistory = view.findViewById(R.id.btn_toggle_history);
        if (btnToggleHistory != null) {
            btnToggleHistory.setOnClickListener(v -> toggleHistoryVisibility());
        }

        // --- FIKS FOR NULLPOINTER EXCEPTION PÅ LAYOUTTRANSITION ---
        if (view instanceof ViewGroup) {
            LayoutTransition transition = ((ViewGroup) view).getLayoutTransition();
            if (transition == null) {
                transition = new LayoutTransition();
                ((ViewGroup) view).setLayoutTransition(transition);
            }
            transition.enableTransitionType(LayoutTransition.CHANGING);
        }
        // ----------------------------------------------------------

        if (formContainer == null) {
            formContainer = new LinearLayout(getContext());
        }

        if (getArguments() != null) {
            int argId = getArguments().getInt("formId", 0);
            if (argId != 0) formId = argId;
        }

        fetchFormStructure();

        return view;
    }

    // --- UI LOGIKK FOR DELT SKJERM ---

    private void expandFormModule() {
        if (historyWrapper != null && historyWrapper.getVisibility() == View.VISIBLE) {
            historyWrapper.setVisibility(View.GONE);
            if (btnToggleHistory != null) {
                btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float);
            }
        }
    }

    private void toggleHistoryVisibility() {
        if (historyWrapper == null || btnToggleHistory == null) return;

        if (historyWrapper.getVisibility() == View.VISIBLE) {
            // Skjul historikk
            historyWrapper.setVisibility(View.GONE);
            btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float);
        } else {
            // Vis historikk
            historyWrapper.setVisibility(View.VISIBLE);
            btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float);
        }
    }

    private void attachInteractionListener(View view) {
        if (view == null) return;
        view.setOnTouchListener((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                expandFormModule();
            }
            return false;
        });
        view.setOnFocusChangeListener((v, hasFocus) -> {
            if (hasFocus) {
                expandFormModule();
            }
        });
        if (view.isClickable()) {
            view.setOnClickListener(v -> expandFormModule());
        }
    }

    // ----------------------------------

    private void fetchFormStructure() {
        if (loadingSpinner != null) loadingSpinner.setVisibility(View.VISIBLE);
        updateStatus("Laster skjema...");

        WordPressApiService api = RetrofitClient.getApiService();
        api.getForm(formId).enqueue(new retrofit2.Callback<GravityForm>() {
            @Override
            public void onResponse(retrofit2.Call<GravityForm> call, retrofit2.Response<GravityForm> response) {
                if (response.isSuccessful() && response.body() != null) {
                    currentForm = response.body();
                    if (getActivity() != null) {
                        getActivity().runOnUiThread(() -> {
                            if (loadingSpinner != null) loadingSpinner.setVisibility(View.GONE);
                            renderDynamicForm(currentForm);
                            fetchFormEntries();
                        });
                    }
                } else {
                    updateStatus("Feil ved lasting av skjema.");
                    if (loadingSpinner != null) loadingSpinner.setVisibility(View.GONE);
                }
            }

            @Override
            public void onFailure(retrofit2.Call<GravityForm> call, Throwable t) {
                updateStatus("Nettverksfeil (Skjema): " + t.getMessage());
                if (loadingSpinner != null) loadingSpinner.setVisibility(View.GONE);
            }
        });
    }

    private void renderDynamicForm(GravityForm form) {
        if (formContainer == null) return;
        formContainer.removeAllViews();
        fieldWrappers.clear();
        inputViews.clear();
        requiredFieldsMap.clear();
        fileUploads.clear();
        nestedEntries.clear();
        updateStatus("");

        // Reset visibility of history on new load
        if (historyWrapper != null) {
            historyWrapper.setVisibility(View.VISIBLE);
            if (btnToggleHistory != null) btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float);
        }

        TextView title = new TextView(getContext());
        title.setText(getCleanTitle(form.title));
        title.setTextSize(24);
        title.setTypeface(null, Typeface.BOLD);
        title.setTextColor(Color.BLACK);
        title.setPadding(0, 0, 0, 20);
        formContainer.addView(title);

        if (form.description != null && !form.description.isEmpty()) {
            TextView formDesc = new TextView(getContext());
            String cleanDesc = form.description.replaceFirst("^\\d+\\.\\s*", "");
            formDesc.setText(cleanDesc);
            formDesc.setPadding(0, 0, 0, 40);
            formContainer.addView(formDesc);
        }

        if (form.fields == null) return;
        for (GravityField field : form.fields) {
            if ("hidden".equals(field.type) || field.isHidden || "hidden".equals(field.visibility)) {
                continue;
            }

            LinearLayout fieldWrapper = new LinearLayout(getContext());
            fieldWrapper.setOrientation(LinearLayout.VERTICAL);
            fieldWrapper.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            fieldWrapper.setPadding(0, 10, 0, 20);

            fieldWrappers.put(String.valueOf(field.id), fieldWrapper);

            if ("section".equals(field.type)) {
                addSectionHeader(fieldWrapper, field.label, field.description);
                formContainer.addView(fieldWrapper);
                continue;
            }

            if ("html".equals(field.type)) {
                if (field.content != null && !field.content.isEmpty()) {
                    TextView htmlView = new TextView(getContext());
                    htmlView.setText(Html.fromHtml(field.content, Html.FROM_HTML_MODE_COMPACT));
                    fieldWrapper.addView(htmlView);
                }
                formContainer.addView(fieldWrapper);
                continue;
            }

            TextView label = new TextView(getContext());
            String labelText = field.label;
            if (field.isRequired) labelText += " *";
            label.setText(labelText);
            label.setTextColor(Color.DKGRAY);
            label.setTypeface(null, Typeface.BOLD);
            label.setPadding(0, 10, 0, 5);
            fieldWrapper.addView(label);

            if ("form".equals(field.type)) {
                renderNestedFormField(fieldWrapper, field);
            }
            else if ("product".equals(field.type) && "calculation".equals(field.inputType)) {
                renderTotalSumField(fieldWrapper, field);
            }
            else if ("time".equals(field.type)) {
                renderTimeField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("fileupload".equals(field.type)) {
                renderFileUploadField(fieldWrapper, field, inputViews, requiredFieldsMap, false);
            } else if (field.inputs != null && !field.inputs.isEmpty()) {
                if ("consent".equals(field.type)) {
                    renderConsentField(fieldWrapper, field, inputViews, requiredFieldsMap);
                } else if ("checkbox".equals(field.type) || "multi_choice".equals(field.type)) {
                    renderCheckboxField(fieldWrapper, field, inputViews, requiredFieldsMap);
                } else {
                    renderCompositeField(fieldWrapper, field, inputViews, requiredFieldsMap);
                }
            } else if ("radio".equals(field.type)) {
                renderRadioField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("select".equals(field.type)) {
                renderSelectField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("textarea".equals(field.type)) {
                renderTextAreaField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("date".equals(field.type)) {
                renderDateField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else if ("consent".equals(field.type)) {
                renderConsentField(fieldWrapper, field, inputViews, requiredFieldsMap);
            } else {
                renderTextField(fieldWrapper, field, inputViews, requiredFieldsMap);
            }

            if (field.description != null && !field.description.isEmpty()) {
                TextView desc = new TextView(getContext());
                desc.setText(Html.fromHtml(field.description, Html.FROM_HTML_MODE_COMPACT));
                desc.setTextSize(12);
                desc.setTextColor(Color.GRAY);
                fieldWrapper.addView(desc);
            }

            formContainer.addView(fieldWrapper);
        }

        Button dynamicSubmit = new Button(getContext());
        dynamicSubmit.setText("Send inn skjema");
        dynamicSubmit.setTextColor(Color.WHITE);
        dynamicSubmit.setBackgroundColor(Color.parseColor("#0069B3")); // KBS Blå
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.setMargins(0, 60, 0, 20);
        dynamicSubmit.setLayoutParams(params);
        dynamicSubmit.setOnClickListener(v -> submitDynamicForm());
        formContainer.addView(dynamicSubmit);

        evaluateAllConditionalLogic();
    }

    // --- NESTED FORM LOGIKK ---

    private void renderNestedFormField(LinearLayout container, GravityField field) {
        nestedEntriesContainer = new LinearLayout(getContext());
        nestedEntriesContainer.setOrientation(LinearLayout.VERTICAL);
        nestedEntriesContainer.setPadding(0, 10, 0, 10);
        container.addView(nestedEntriesContainer);

        Button btnAdd = new Button(getContext());
        btnAdd.setText("Legg til vedlegg");
        btnAdd.setBackgroundColor(Color.parseColor("#53AFE9"));
        btnAdd.setTextColor(Color.WHITE);
        btnAdd.setOnClickListener(v -> {
            expandFormModule(); // Trigger expand
            int childFormId = 18;
            if (field.gpnfForm != null) {
                try {
                    childFormId = Integer.parseInt(field.gpnfForm);
                } catch (NumberFormatException e) { e.printStackTrace(); }
            }
            // NYTT: Sender med felt-ID fra hovedskjemaet (f.eks "25")
            openChildFormDialog(childFormId, field.id);
        });
        container.addView(btnAdd);

        EditText hiddenIds = new EditText(getContext());
        hiddenIds.setVisibility(View.GONE);
        inputViews.put(field.id, hiddenIds);
    }

    private void renderTotalSumField(LinearLayout container, GravityField field) {
        totalAmountView = new TextView(getContext());
        totalAmountView.setText("Kr 0,00");
        totalAmountView.setTextSize(18);
        totalAmountView.setTypeface(null, Typeface.BOLD);
        totalAmountView.setPadding(10, 10, 10, 10);
        container.addView(totalAmountView);
    }

    // NY SIGNATUR: Tar imot parentFieldId
    private void openChildFormDialog(int childFormId, String parentFieldId) {
        if (getActivity() == null) return;
        ProgressBar pBar = new ProgressBar(getContext());
        AlertDialog loadingDialog = new AlertDialog.Builder(getContext())
                .setView(pBar)
                .setMessage("Laster skjema...")
                .setCancelable(false)
                .show();
        RetrofitClient.getApiService().getForm(childFormId).enqueue(new retrofit2.Callback<GravityForm>() {
            @Override
            public void onResponse(retrofit2.Call<GravityForm> call, retrofit2.Response<GravityForm> response) {
                loadingDialog.dismiss();
                if (response.isSuccessful() && response.body() != null) {
                    showChildFormDialog(response.body(), parentFieldId);
                } else {
                    Toast.makeText(getContext(), "Kunne ikke hente underskjema", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(retrofit2.Call<GravityForm> call, Throwable t) {
                loadingDialog.dismiss();
                Toast.makeText(getContext(), "Feil: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    // NY SIGNATUR: Tar imot parentFieldId
    private void showChildFormDialog(GravityForm childForm, String parentFieldId) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        childInputViews.clear();
        childRequiredFieldsMap.clear();
        childFileUploads.clear();

        ScrollView scrollView = new ScrollView(getContext());
        LinearLayout layout = new LinearLayout(getContext());
        layout.setOrientation(LinearLayout.VERTICAL);
        layout.setPadding(30, 30, 30, 30);
        scrollView.addView(layout);
        for (GravityField field : childForm.fields) {
            if ("hidden".equals(field.type) || field.isHidden) continue;
            LinearLayout wrapper = new LinearLayout(getContext());
            wrapper.setOrientation(LinearLayout.VERTICAL);
            wrapper.setPadding(0, 10, 0, 20);

            TextView label = new TextView(getContext());
            String lText = field.label;
            if (field.isRequired) lText += " *";
            label.setText(lText);
            label.setTypeface(null, Typeface.BOLD);
            wrapper.addView(label);
            if ("fileupload".equals(field.type)) {
                renderFileUploadField(wrapper, field, childInputViews, childRequiredFieldsMap, true);
            } else if ("product".equals(field.type)) {
                EditText input = new EditText(getContext());
                input.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
                input.setHint("Kr 0.00");
                wrapper.addView(input);
                childInputViews.put(field.id, input);
                childRequiredFieldsMap.put(field.id, field.isRequired);
            } else {
                renderTextField(wrapper, field, childInputViews, childRequiredFieldsMap);
            }
            layout.addView(wrapper);
        }

        builder.setView(scrollView);
        builder.setPositiveButton("Legg til vedlegg", null);
        builder.setNegativeButton("Avbryt", (d, w) -> d.dismiss());
        AlertDialog dialog = builder.create();
        dialog.show();

        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
            submitChildForm(childForm.id, dialog, parentFieldId);
        });
    }

    // NY SIGNATUR: Tar imot parentFieldId og sender den som meta
    private void submitChildForm(int childFormId, AlertDialog dialog, String parentFieldId) {
        JSONObject inputValues = new JSONObject();
        for (Map.Entry<String, View> entry : childInputViews.entrySet()) {
            String val = getInputValueGeneric(entry.getValue());
            if (!val.isEmpty()) {
                try {
                    inputValues.put("input_" + entry.getKey(), val);
                } catch (JSONException e) { e.printStackTrace(); }
            }
        }

        if (!childFileUploads.isEmpty()) {
            List<MultipartBody.Part> fileParts = new ArrayList<>();
            Map<String, RequestBody> textParts = new HashMap<>();

            try {
                JSONArray names = inputValues.names();
                if (names != null) {
                    for (int i = 0; i < names.length(); i++) {
                        String key = names.getString(i);
                        String val = inputValues.getString(key);
                        textParts.put(key, RequestBody.create(MultipartBody.FORM, val));
                    }
                }

                // --- HER ER FIKSEN FOR BUGGEN ---
                // Vi legger ved en ekstra parameter som forteller Gravity Forms at dette
                // er en nested entry som hører til et bestemt felt (f.eks "25").
                if (parentFieldId != null) {
                    textParts.put("gpnf_entry_nested_form_field", RequestBody.create(MultipartBody.FORM, parentFieldId));
                }
                // ---------------------------------

                for (Map.Entry<String, Uri> fileEntry : childFileUploads.entrySet()) {
                    String fieldId = fileEntry.getKey();
                    Uri uri = fileEntry.getValue();
                    if (uri != null) {
                        MultipartBody.Part part = getFilePart("input_" + fieldId, uri);
                        if (part != null) fileParts.add(part);
                    }
                }

                Toast.makeText(getContext(), "Laster opp vedlegg...", Toast.LENGTH_SHORT).show();
                RetrofitClient.getApiService().submitMultipartForm(childFormId, textParts, fileParts).enqueue(new retrofit2.Callback<JsonElement>() {
                    @Override
                    public void onResponse(retrofit2.Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
                        if (response.isSuccessful() && response.body() != null) {
                            try {
                                JsonObject json = response.body().getAsJsonObject();
                                if (json.has("is_valid") && json.get("is_valid").getAsBoolean()) {
                                    String entryId = json.has("entry_id") ? json.get("entry_id").getAsString() : "";

                                    // NB: Tilpass ID-ene her hvis skjema 18 endres.
                                    // ID 3 = Beskrivelse, ID 4 = Beløp
                                    String desc = getInputValueGeneric(childInputViews.get("3"));
                                    String price = getInputValueGeneric(childInputViews.get("4"));
                                    addNestedEntry(entryId, desc, price);
                                    dialog.dismiss();
                                } else {
                                    Toast.makeText(getContext(), "Ugyldig respons fra server", Toast.LENGTH_SHORT).show();
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                                Toast.makeText(getContext(), "Feil ved parsing av svar", Toast.LENGTH_SHORT).show();
                            }
                        } else {
                            Toast.makeText(getContext(), "Feil ved opplasting", Toast.LENGTH_SHORT).show();
                        }
                    }
                    @Override
                    public void onFailure(retrofit2.Call<JsonElement> call, Throwable t) {
                        Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
                    }
                });
            } catch (Exception e) { e.printStackTrace(); }
        }
    }

    private void addNestedEntry(String entryId, String description, String price) {
        nestedEntries.add(new NestedEntry(entryId, description, price));
        refreshNestedList();
    }

    private void refreshNestedList() {
        if (nestedEntriesContainer == null) return;
        nestedEntriesContainer.removeAllViews();

        double total = 0;
        List<String> ids = new ArrayList<>();
        for (NestedEntry entry : nestedEntries) {
            ids.add(entry.id);
            String cleanPrice = entry.price.replaceAll("[^0-9,.]", "").replace(",", ".");
            try {
                if (!cleanPrice.isEmpty()) total += Double.parseDouble(cleanPrice);
            } catch (NumberFormatException e) { }

            LinearLayout row = new LinearLayout(getContext());
            row.setOrientation(LinearLayout.HORIZONTAL);
            row.setPadding(10, 10, 10, 10);

            TextView txt = new TextView(getContext());
            txt.setText(entry.description + " (" + entry.price + ")");
            txt.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));

            row.addView(txt);
            nestedEntriesContainer.addView(row);
        }

        if (totalAmountView != null) {
            totalAmountView.setText("Totalt: Kr " + String.format("%.2f", total));
        }

        View hiddenField = inputViews.get("25");
        if (hiddenField instanceof EditText) {
            ((EditText)hiddenField).setText(TextUtils.join(",", ids));
        }
    }

    // --- FELLES METODER (FILE UPLOAD M/ CAMERA STØTTE) ---

    private void renderFileUploadField(LinearLayout container, GravityField field, Map<String, View> viewsMap, Map<String, Boolean> reqMap, boolean isChild) {
        LinearLayout fileLayout = new LinearLayout(getContext());
        fileLayout.setOrientation(LinearLayout.HORIZONTAL);

        Button btnUpload = new Button(getContext());
        btnUpload.setText("Velg fil / Ta bilde");
        btnUpload.setOnClickListener(v -> {
            // Setter state før vi viser dialog
            pendingFileFieldId = field.id;
            isSelectingForChild = isChild;
            expandFormModule(); // Trigger expand
            showFileSourceDialog();
        });
        TextView txtFileName = new TextView(getContext());
        txtFileName.setText("Ingen fil valgt");
        txtFileName.setPadding(20, 0, 0, 0);
        txtFileName.setTextColor(Color.GRAY);
        btnUpload.setTag(txtFileName);

        fileLayout.addView(btnUpload);
        fileLayout.addView(txtFileName);
        container.addView(fileLayout);

        viewsMap.put(field.id, btnUpload);
        reqMap.put(field.id, field.isRequired);
    }

    // Hjelpemetode for å vise dialog
    private void showFileSourceDialog() {
        String[] options = {"Ta bilde", "Velg fil"};
        new AlertDialog.Builder(getContext())
                .setTitle("Last opp vedlegg")
                .setItems(options, (dialog, which) -> {
                    if (which == 0) {
                        // Ta bilde - SJEKKER PERMISSION FØRST
                        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA)
                                == PackageManager.PERMISSION_GRANTED) {
                            openCamera();
                        } else {
                            // Spør om lov
                            requestPermissionLauncher.launch(Manifest.permission.CAMERA);
                        }
                    } else {
                        // Velg fil
                        if (filePickerLauncher != null) {
                            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                            intent.setType("*/*");
                            String[] mimeTypes = {"image/jpeg", "image/png", "application/pdf"};
                            intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
                            filePickerLauncher.launch(intent);
                        }
                    }
                })
                .show();
    }

    private void openCamera() {
        currentPhotoUri = createImageUri();
        if (currentPhotoUri != null && takePictureLauncher != null) {
            try {
                takePictureLauncher.launch(currentPhotoUri);
            } catch (Exception e) {
                Toast.makeText(getContext(), "Kunne ikke starte kamera: " + e.getMessage(), Toast.LENGTH_SHORT).show();
                Log.e(TAG, "Camera launch failed", e);
            }
        } else {
            Toast.makeText(getContext(), "Kunne ikke opprette bildefil", Toast.LENGTH_SHORT).show();
        }
    }

    private Uri createImageUri() {
        try {
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
            String imageFileName = "JPEG_" + timeStamp + "_";
            File storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            File image = File.createTempFile(imageFileName, ".jpg", storageDir);
            return FileProvider.getUriForFile(requireContext(), "com.kbs.kbsintranett.fileprovider", image);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private void handleFileSelection(String fieldId, Uri uri, boolean isChild) {
        if (isChild) {
            childFileUploads.put(fieldId, uri);
        } else {
            fileUploads.put(fieldId, uri);
        }

        Map<String, View> targetMap = isChild ? childInputViews : inputViews;
        View view = targetMap.get(fieldId);
        if (view instanceof Button) {
            TextView txtView = (TextView) view.getTag();
            if (txtView != null) {
                txtView.setText(getFileName(uri));
                txtView.setTextColor(Color.BLACK);
            }
        }
    }

    private String getFileName(Uri uri) {
        String result = null;
        if (uri.getScheme().equals("content")) {
            try (Cursor cursor = getContext().getContentResolver().query(uri, null, null, null, null)) {
                if (cursor != null && cursor.moveToFirst()) {
                    int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                    if(index >= 0) result = cursor.getString(index);
                }
            } catch (Exception e) {}
        }
        if (result == null) {
            result = uri.getPath();
            int cut = result.lastIndexOf('/');
            if (cut != -1) result = result.substring(cut + 1);
        }
        return result;
    }

    private String getCleanTitle(String title) {
        if (title == null) return "";
        Matcher m = TITLE_PATTERN.matcher(title.trim());
        if (m.find()) {
            return m.group(2);
        }
        return title;
    }

    // --- STANDARD RENDER METODER ---

    private void renderTimeField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        EditText timeInput = new EditText(getContext());
        timeInput.setFocusable(false);
        timeInput.setClickable(true);
        timeInput.setHint("00:00");
        timeInput.setOnClickListener(v -> {
            expandFormModule(); // Trigger expand
            Calendar mcurrentTime = Calendar.getInstance();
            int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY);
            int minute = mcurrentTime.get(Calendar.MINUTE);
            new TimePickerDialog(getContext(), (timePicker, selectedHour, selectedMinute) -> {
                timeInput.setText(String.format("%02d:%02d", selectedHour, selectedMinute));
                evaluateAllConditionalLogic();
            }, hour, minute, true).show();
        });
        container.addView(timeInput);
        views.put(field.id, timeInput);
        req.put(field.id, field.isRequired);
    }

    private void renderTextField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        EditText input = new EditText(getContext());
        input.setPadding(30, 30, 30, 30);
        input.setBackgroundResource(android.R.drawable.edit_text);

        if ("number".equals(field.type) || "phone".equals(field.type)) {
            input.setInputType(InputType.TYPE_CLASS_PHONE);
        } else if ("email".equals(field.type)) {
            input.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
        } else {
            input.setInputType(InputType.TYPE_CLASS_TEXT);
        }

        if (views == inputViews) {
            UserManager user = UserManager.getInstance();
            String lowerLabel = field.label.toLowerCase();
            if (lowerLabel.contains("e-post")) input.setText(user.getUserEmail());
            if (lowerLabel.contains("navn") || lowerLabel.contains("melder")) input.setText(user.getUserDisplayName());
            if (lowerLabel.contains("stilling")) input.setText(user.getStilling());
            if (lowerLabel.contains("mobil")) input.setText(user.getMobiltelefon());
        }

        input.addTextChangedListener(new TextWatcher() {
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
            public void onTextChanged(CharSequence s, int start, int before, int count) {}
            public void afterTextChanged(Editable s) { evaluateAllConditionalLogic(); }
        });
        attachInteractionListener(input); // Add listener

        container.addView(input);
        views.put(field.id, input);
        req.put(field.id, field.isRequired);
    }

    private void renderTextAreaField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        EditText input = new EditText(getContext());
        input.setBackgroundResource(android.R.drawable.edit_text);
        input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
        input.setMinLines(3);
        input.setGravity(android.view.Gravity.TOP | android.view.Gravity.START);

        attachInteractionListener(input); // Add listener

        container.addView(input);
        views.put(field.id, input);
        req.put(field.id, field.isRequired);
    }

    private void renderRadioField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        RadioGroup group = new RadioGroup(getContext());
        if (field.choices != null) {
            for (GravityField.Choice choice : field.choices) {
                RadioButton rb = new RadioButton(getContext());
                rb.setText(choice.text);
                rb.setTag(choice.value);
                // Also trigger expand on RadioButton click
                rb.setOnClickListener(v -> {
                    expandFormModule();
                    evaluateAllConditionalLogic();
                });
                group.addView(rb);
            }
        }
        // Fallback listener
        group.setOnCheckedChangeListener((g, i) -> {
            expandFormModule();
            evaluateAllConditionalLogic();
        });
        container.addView(group);
        views.put(field.id, group);
        req.put(field.id, field.isRequired);
    }

    private void renderSelectField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        Spinner spinner = new Spinner(getContext());
        // Spinner touch listener is tricky, usually set onTouchListener works
        spinner.setOnTouchListener((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                expandFormModule();
            }
            return false;
        });
        List<String> labels = new ArrayList<>();
        labels.add("- Velg -");
        if (field.choices != null) {
            for (GravityField.Choice c : field.choices) labels.add(c.text);
        }
        ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, labels);
        spinner.setAdapter(adapter);
        container.addView(spinner);
        views.put(field.id, spinner);
        req.put(field.id, field.isRequired);
    }

    private void renderConsentField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        CheckBox checkBox = new CheckBox(getContext());
        String cbText = (field.checkboxLabel != null && !field.checkboxLabel.isEmpty()) ? field.checkboxLabel : field.label;
        checkBox.setText(cbText);
        String inputId = (field.inputs != null && !field.inputs.isEmpty()) ? field.inputs.get(0).id : field.id;

        checkBox.setTag("1");
        checkBox.setOnCheckedChangeListener((b, c) -> {
            expandFormModule();
            evaluateAllConditionalLogic();
        });
        container.addView(checkBox);
        views.put(inputId, checkBox);
        req.put(inputId, field.isRequired);
    }

    private void renderCheckboxField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        if (field.inputs != null) {
            for (int i = 0; i < field.inputs.size(); i++) {
                GravityField inputDef = field.inputs.get(i);
                CheckBox checkBox = new CheckBox(getContext());
                checkBox.setText(inputDef.label);

                String value = "1";
                // Fallback
                if (field.choices != null && i < field.choices.size()) {
                    value = field.choices.get(i).value;
                }
                checkBox.setTag(value);
                checkBox.setOnCheckedChangeListener((b, c) -> {
                    expandFormModule();
                    evaluateAllConditionalLogic();
                });
                container.addView(checkBox);
                views.put(inputDef.id, checkBox);
                req.put(inputDef.id, false);
            }
        }
    }

    private void renderDateField(LinearLayout container, GravityField field, Map<String, View> views, Map<String, Boolean> req) {
        EditText dateInput = new EditText(getContext());
        // --- DATO LØSNING (Fix for Read Only / Dagens Dato) ---
        if (field.readOnly || (formId == ID_REFUSJON_UTLEGG && "28".equals(field.id))) {
            // Sett dagens dato
            SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
            dateInput.setText(df.format(new Date()));

            // Gjør den "read-only" men synlig
            dateInput.setFocusable(false);
            dateInput.setClickable(false);
            dateInput.setEnabled(false);
            dateInput.setTextColor(Color.BLACK);
        } else {
            // Vanlig dato-velger
            dateInput.setFocusable(false);
            dateInput.setClickable(true);
            dateInput.setHint("dd.mm.yyyy");
            dateInput.setOnClickListener(v -> {
                expandFormModule(); // Trigger expand
                Calendar c = Calendar.getInstance();
                new DatePickerDialog(getContext(), (view, year, month, dayOfMonth) -> {
                    dateInput.setText(String.format("%02d.%02d.%d", dayOfMonth, month + 1, year));
                    evaluateAllConditionalLogic();
                }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show();
            });
        }

        dateInput.setPadding(30, 30, 30, 30);
        dateInput.setBackgroundResource(android.R.drawable.edit_text);

        container.addView(dateInput);
        views.put(field.id, dateInput);
        req.put(field.id, field.isRequired);
    }

    private void renderCompositeField(LinearLayout container, GravityField parentField, Map<String, View> views, Map<String, Boolean> req) {
        UserManager user = UserManager.getInstance();
        boolean isPersonalia = (formId == ID_ANSATTEOPPLYSNINGER);

        List<GravityField> inputs = new ArrayList<>(parentField.inputs);
        if ("address".equals(parentField.type)) {
            Collections.sort(inputs, (f1, f2) -> Integer.compare(getAddressScore(f1.label), getAddressScore(f2.label)));
        }

        for (GravityField subField : inputs) {
            if (subField.isHidden || "hidden".equals(subField.visibility)) continue;
            TextView subLabel = new TextView(getContext());
            String subLabelText = subField.label;
            boolean isSubRequired = parentField.isRequired;
            if ("address".equals(parentField.type) && subField.id.endsWith(".2")) {
                isSubRequired = false;
            }
            if (isSubRequired) subLabelText += " *";

            subLabel.setText(subLabelText);
            subLabel.setTextColor(Color.GRAY);
            subLabel.setTextSize(12);
            subLabel.setPadding(0, 10, 0, 0);
            container.addView(subLabel);

            EditText subInput = new EditText(getContext());
            subInput.setPadding(30, 30, 30, 30);
            subInput.setBackgroundResource(android.R.drawable.edit_text);
            subInput.setInputType(InputType.TYPE_CLASS_TEXT);
            if (isPersonalia && parentField.label.toLowerCase().contains("navn") && !parentField.label.toLowerCase().contains("pårørende")) {
                String lowerSub = subField.label.toLowerCase();
                if (lowerSub.contains("fornavn")) subInput.setText(user.getFirstName());
                else if (lowerSub.contains("etternavn")) subInput.setText(user.getLastName());
            }

            attachInteractionListener(subInput);
            container.addView(subInput);
            views.put(subField.id, subInput);
            req.put(subField.id, isSubRequired);
        }
    }

    private void addSectionHeader(LinearLayout container, String title, String descText) {
        TextView sectionHeader = new TextView(getContext());
        sectionHeader.setText(title);
        sectionHeader.setTextSize(18);
        sectionHeader.setTypeface(null, Typeface.BOLD);
        sectionHeader.setTextColor(Color.parseColor("#0069B3"));
        sectionHeader.setPadding(0, 20, 0, 5);
        container.addView(sectionHeader);
        if (descText != null && !descText.isEmpty()) {
            TextView desc = new TextView(getContext());
            desc.setText(Html.fromHtml(descText, Html.FROM_HTML_MODE_COMPACT));
            desc.setTextSize(12);
            desc.setTextColor(Color.GRAY);
            container.addView(desc);
        }

        View line = new View(getContext());
        line.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 2));
        line.setBackgroundColor(Color.LTGRAY);
        line.setPadding(0,0,0,20);
        container.addView(line);
    }

    private int getAddressScore(String label) {
        if (label == null) return 99;
        String l = label.toLowerCase();
        if (l.contains("adresselinje 1")) return 1;
        if (l.contains("adresselinje 2")) return 2;
        if (l.contains("postnummer") || l.contains("zip")) return 3;
        if (l.contains("poststed") || l.contains("city")) return 4;
        if (l.contains("land") || l.contains("country")) return 5;
        return 99;
    }

    private void evaluateAllConditionalLogic() {
        if (currentForm == null || currentForm.fields == null) return;
        for (GravityField field : currentForm.fields) {
            if (field.conditionalLogic == null) {
                setViewVisibility(field.id, true);
                continue;
            }

            boolean isMatch = evaluateLogic(field.conditionalLogic);
            boolean show = "show".equalsIgnoreCase(field.conditionalLogic.actionType);
            boolean shouldBeVisible = (show && isMatch) || (!show && !isMatch);
            setViewVisibility(field.id, shouldBeVisible);
        }
    }

    private boolean evaluateLogic(GravityField.ConditionalLogic logic) {
        if (logic.rules == null || logic.rules.isEmpty()) return true;
        boolean isAll = "all".equalsIgnoreCase(logic.logicType);
        boolean aggregatedResult = isAll;

        for (GravityField.Rule rule : logic.rules) {
            String val = getInputValue(rule.fieldId);
            boolean ruleMatch = checkRule(val, rule.operator, rule.value);

            if (isAll) {
                aggregatedResult = aggregatedResult && ruleMatch;
                if (!aggregatedResult) break;
            } else {
                aggregatedResult = aggregatedResult ||
                        ruleMatch;
                if (aggregatedResult) break;
            }
        }
        return aggregatedResult;
    }

    private boolean checkRule(String actualValue, String operator, String targetValue) {
        if (actualValue == null) actualValue = "";
        if (targetValue == null) targetValue = "";

        switch (operator.toLowerCase()) {
            case "is": return actualValue.equalsIgnoreCase(targetValue);
            case "isnot": return !actualValue.equalsIgnoreCase(targetValue);
            case "contains": return actualValue.toLowerCase().contains(targetValue.toLowerCase());
            case "starts_with": return actualValue.toLowerCase().startsWith(targetValue.toLowerCase());
            case "ends_with": return actualValue.toLowerCase().endsWith(targetValue.toLowerCase());
            default: return false;
        }
    }

    private String getInputValue(String fieldId) {
        View view = inputViews.get(fieldId);
        return getInputValueGeneric(view);
    }

    private String getInputValueGeneric(View view) {
        if (view == null) return "";
        if (view instanceof EditText) return ((EditText) view).getText().toString();
        if (view instanceof RadioGroup) {
            int id = ((RadioGroup) view).getCheckedRadioButtonId();
            if (id != -1) {
                View rb = view.findViewById(id);
                if (rb != null && rb.getTag() != null) return rb.getTag().toString();
            }
        }
        if (view instanceof Spinner) {
            if (((Spinner) view).getSelectedItemPosition() == 0) return "";
            Object item = ((Spinner) view).getSelectedItem();
            return item != null ? item.toString() : "";
        }
        if (view instanceof CheckBox) {
            CheckBox cb = (CheckBox) view;
            if (cb.isChecked()) {
                return cb.getTag() != null ?
                        cb.getTag().toString() : "1";
            }
            return "";
        }
        return "";
    }

    private void setViewVisibility(String fieldId, boolean visible) {
        View wrapper = fieldWrappers.get(fieldId);
        if (wrapper != null) {
            wrapper.setVisibility(visible ? View.VISIBLE : View.GONE);
        }
    }

    // --- SUBMISSION ---

    private void submitDynamicForm() {
        JSONObject inputValues = new JSONObject();
        boolean hasValues = false;

        Log.d(TAG, "submitDynamicForm: Starting validation...");

        for (Map.Entry<String, View> entry : inputViews.entrySet()) {
            String fieldId = entry.getKey();
            View view = entry.getValue();

            View wrapper = fieldWrappers.get(fieldId);
            if (wrapper == null) {
                if (!view.isShown()) continue;
            } else {
                if (wrapper.getVisibility() != View.VISIBLE) continue;
            }

            String val = getInputValueGeneric(view);
            Boolean req = requiredFieldsMap.get(fieldId);
            if (req != null && req && val.isEmpty() && !(view instanceof Button)) {
                Log.d(TAG, "Validation failed for field " + fieldId);
                if (view instanceof EditText) {
                    ((EditText)view).setError("Må fylles ut");
                    view.requestFocus();
                } else {
                    Toast.makeText(getContext(), "Fyll ut alle felt", Toast.LENGTH_SHORT).show();
                }
                return;
            }
            if (!val.isEmpty()) {
                try {
                    GravityField fieldDef = getGravityFieldById(fieldId);
                    if (fieldDef != null && "date".equals(fieldDef.type)) {
                        val = formatDateForApi(val);
                    }

                    inputValues.put("input_" + fieldId, val);
                    hasValues = true;
                } catch (JSONException e) {}
            }
        }

        if (!hasValues && fileUploads.isEmpty()) {
            Log.d(TAG, "Submit aborted: Form is empty");
            Toast.makeText(getContext(), "Skjemaet er tomt", Toast.LENGTH_SHORT).show();
            return;
        }

        updateStatus("Sender inn...");
        String cookie = UserManager.getInstance().getCookie();
        Log.d(TAG, "Preparing submission payload: " + inputValues.toString());

        if (!fileUploads.isEmpty()) {
            Log.d(TAG, "Submitting as Multipart...");
            sendMultipart(inputValues);
        } else {
            Log.d(TAG, "Submitting as JSON...");
            RequestBody body = RequestBody.create(MediaType.parse("application/json"), inputValues.toString());
            String url = BASE_URL_GF + "/forms/" + formId + "/submissions";
            Request request = new Request.Builder().url(url).post(body).header("Cookie", cookie).build();
            client.newCall(request).enqueue(new okhttp3.Callback() {
                public void onFailure(okhttp3.Call call, IOException e) {
                    Log.e(TAG, "JSON submit failed", e);
                    updateStatus("Feil: " + e.getMessage());
                }
                public void onResponse(okhttp3.Call call, Response response) {
                    Log.d(TAG, "JSON response code: " + response.code());
                    if (response.isSuccessful()) {
                        if (getActivity() != null) getActivity().runOnUiThread(() -> {
                            Toast.makeText(getContext(), "Sendt!", Toast.LENGTH_LONG).show();
                            fetchFormEntries();
                            updateStatus("OK");
                            clearInputs();
                        });
                    } else {
                        try {
                            String errBody = response.body() != null ? response.body().string() : "No body";
                            Log.e(TAG, "Server error body: " + errBody);
                            updateStatus("Feil (" + response.code() + "): " + errBody);
                        } catch(Exception e){}
                    }
                }
            });
        }
    }

    private String formatDateForApi(String dateStr) {
        if (dateStr == null || dateStr.isEmpty()) return "";
        try {
            SimpleDateFormat displayFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
            Date date = displayFormat.parse(dateStr);
            SimpleDateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
            return apiFormat.format(date);
        } catch (Exception e) {
            return dateStr;
        }
    }

    private GravityField getGravityFieldById(String id) {
        if (currentForm == null || currentForm.fields == null) return null;
        for (GravityField f : currentForm.fields) {
            if (f.id.equals(id)) return f;
            if (f.inputs != null) {
                for (GravityField sub : f.inputs) {
                    if (sub.id.equals(id)) return sub;
                }
            }
        }
        return null;
    }

    private void sendMultipart(JSONObject inputValues) {
        List<MultipartBody.Part> fileParts = new ArrayList<>();
        Map<String, RequestBody> textParts = new HashMap<>();
        try {
            JSONArray names = inputValues.names();
            if (names != null) {
                for(int i=0; i<names.length(); i++) {
                    String k = names.getString(i);
                    textParts.put(k, RequestBody.create(MultipartBody.FORM, inputValues.getString(k)));
                }
            }
            for (Map.Entry<String, Uri> entry : fileUploads.entrySet()) {
                MultipartBody.Part part = getFilePart("input_" + entry.getKey(), entry.getValue());
                if (part != null) fileParts.add(part);
            }
            RetrofitClient.getApiService().submitMultipartForm(formId, textParts, fileParts).enqueue(new retrofit2.Callback<JsonElement>() {
                public void onResponse(retrofit2.Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
                    if (response.isSuccessful()) {
                        if (getActivity() != null) getActivity().runOnUiThread(() -> {
                            Toast.makeText(getContext(), "Sendt!", Toast.LENGTH_LONG).show();
                            fetchFormEntries();
                            updateStatus("OK");
                            clearInputs();
                        });
                    } else {
                        updateStatus("Feil: " + response.code());
                    }
                }
                public void onFailure(retrofit2.Call<JsonElement> call, Throwable t) { updateStatus("Feil: " + t.getMessage()); }
            });
        } catch (Exception e) {}
    }

    private MultipartBody.Part getFilePart(String partName, Uri uri) {
        try {
            InputStream inputStream = getContext().getContentResolver().openInputStream(uri);
            String fileName = getFileName(uri);
            RequestBody requestBody = new RequestBody() {
                @Override public MediaType contentType() { return MediaType.parse("application/octet-stream");
                }
                @Override public void writeTo(BufferedSink sink) throws IOException {
                    try (Source source = Okio.source(inputStream)) { sink.writeAll(source);
                    }
                }
            };
            return MultipartBody.Part.createFormData(partName, fileName, requestBody);
        } catch (Exception e) { return null;
        }
    }

    private void fetchFormEntries() {
        UserManager user = UserManager.getInstance();
        String cookie = user.getCookie();
        int userId = user.getUserId();

        if (cookie == null) return;
        String searchJson = "{\"field_filters\":[{\"key\":\"created_by\",\"value\":\"" + userId + "\"}]}";
        String encodedSearch = "";
        try {
            encodedSearch = URLEncoder.encode(searchJson, "UTF-8");
        } catch (UnsupportedEncodingException e) { e.printStackTrace(); }

        String url = BASE_URL_GF + "/entries?form_ids=" + formId + "&search=" + encodedSearch;
        Request request = new Request.Builder().url(url).header("Cookie", cookie).build();

        client.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(@NonNull okhttp3.Call call, @NonNull IOException e) {
                Log.e(TAG, "Kunne ikke hente historikk", e);
            }

            @Override
            public void onResponse(@NonNull okhttp3.Call call, @NonNull Response response) throws IOException {
                if (response.isSuccessful()) {
                    String jsonStr = response.body().string();
                    try {
                        JSONObject json = new JSONObject(jsonStr);
                        if (json.has("entries")) {
                            JSONArray entries = json.getJSONArray("entries");
                            if (getActivity() != null) {
                                getActivity().runOnUiThread(() -> {
                                    showHistory(entries);
                                    if (formId == ID_ANSATTEOPPLYSNINGER && entries.length() > 0)
                                    {
                                        try {
                                            prefillFormFromHistory(entries.getJSONObject(0));
                                        } catch (JSONException e) { e.printStackTrace(); }
                                    }
                                });
                            }
                        }
                    } catch (JSONException e) { e.printStackTrace();
                    }
                }
            }
        });
    }

    private void showHistory(JSONArray entries) {
        if (historyContainer == null) return;
        historyContainer.removeAllViews();

        if (entries.length() == 0) {
            if (lblHistory != null) lblHistory.setVisibility(View.GONE);
            return;
        } else {
            if (lblHistory != null) lblHistory.setVisibility(View.VISIBLE);
        }

        try {
            // Vis flere oppføringer siden vi nå har scrolle-mulighet øverst
            int count = Math.min(entries.length(), 20);
            for (int i = 0; i < count; i++) {
                JSONObject entry = entries.getJSONObject(i);
                String date = entry.optString("date_created");

                // Prøv å finne en bedre tittel enn bare dato (f.eks Prosjektnavn eller Sted)
                String titleText = "Innsendt: " + date;
                TextView item = new TextView(getContext());
                item.setText(titleText);
                item.setPadding(10, 20, 10, 20);
                item.setBackgroundResource(android.R.drawable.list_selector_background);
                item.setTextSize(14);
                // Add click listener to show details
                item.setOnClickListener(v -> showEntryDetails(entry));
                View line = new View(getContext());
                line.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
                line.setBackgroundColor(Color.LTGRAY);

                historyContainer.addView(item);
                historyContainer.addView(line);
            }
        } catch (JSONException e) { e.printStackTrace();
        }
    }

    // NY METODE: Vis detaljer i dialog med delingsknapp
    private void showEntryDetails(JSONObject entry) {
        // HVIS dette er Form 16 (Refusjon), må vi først hente vedleggene (som ligger i Form 18)
        if (formId == ID_REFUSJON_UTLEGG) {
            Log.d(TAG, "Form 16 detected. Checking for child entries...");
            String nestedIds = entry.optString("25"); // Felt 25 i Form 16 inneholder ID-ene til vedleggene

            if (!nestedIds.isEmpty()) {
                Log.d(TAG, "Nested IDs found: " + nestedIds);
                List<String> ids = new ArrayList<>();

                // ROBUST PARSING AV ID-LISTE
                if (nestedIds.startsWith("[") && nestedIds.endsWith("]")) {
                    // Dette er en JSON-array streng (f.eks [101, 102])
                    try {
                        JSONArray jsonArray = new JSONArray(nestedIds);
                        for(int i=0; i<jsonArray.length(); i++) {
                            ids.add(jsonArray.getString(i));
                        }
                    } catch(JSONException e) {
                        Log.e(TAG, "Failed to parse nested IDs as JSON array", e);
                    }
                } else {
                    // Dette er en komma-separert streng (f.eks "101, 102")
                    for (String id : nestedIds.split(",")) {
                        ids.add(id.trim());
                    }
                }

                if (!ids.isEmpty()) {
                    // Vis en "Laster..." dialog mens vi henter data
                    AlertDialog loadingDialog = new AlertDialog.Builder(getContext())
                            .setMessage("Henter vedleggsdetaljer...")
                            .setCancelable(false)
                            .show();

                    // Start rekursiv henting
                    StringBuilder accumulatedHtml = new StringBuilder();
                    StringBuilder accumulatedText = new StringBuilder();

                    // Legg først til basis-info fra hovedskjemaet
                    appendBasicInfo(entry, accumulatedHtml, accumulatedText);

                    fetchChildEntriesRecursive(ids, 0, accumulatedHtml, accumulatedText, loadingDialog);
                    return; // Stopp her, dialogen vises når data er hentet
                }
            } else {
                Log.d(TAG, "No nested IDs found in field 25.");
            }
        }

        // --- STANDARD VISNING (For alle andre skjemaer) ---
        StringBuilder htmlBuilder = new StringBuilder();
        StringBuilder textBuilder = new StringBuilder();
        appendBasicInfo(entry, htmlBuilder, textBuilder);
        showFinalDialog(htmlBuilder, textBuilder);
    }

    // Hjelpemetode for å legge til info fra hovedskjemaet
    private void appendBasicInfo(JSONObject entry, StringBuilder html, StringBuilder text) {
        try {
            String date = entry.optString("date_created");
            html.append("<b>Innsendt:</b> ").append(date).append("<br><br>");
            text.append("Innsendt: ").append(date).append("\n\n");

            if (currentForm != null && currentForm.fields != null) {
                for (GravityField field : currentForm.fields) {
                    if ("section".equals(field.type) || "html".equals(field.type) || "captcha".equals(field.type)) continue;
                    // Hopp over felt 25 (Vedleggs-IDer) i Form 16, siden vi viser det bedre senere
                    if (formId == ID_REFUSJON_UTLEGG && "25".equals(field.id)) continue;

                    String value = "";
                    if (field.inputs != null && !field.inputs.isEmpty()) {
                        for (GravityField input : field.inputs) {
                            String subVal = entry.optString(input.id);
                            if (!subVal.isEmpty()) value += " " + subVal;
                        }
                    } else {
                        value = entry.optString(String.valueOf(field.id));
                    }

                    if (!value.trim().isEmpty()) {
                        if ("fileupload".equals(field.type)) {
                            // Håndter filopplastinger (single/multiple)
                            // Her antar vi enkel URL, men for sikkerhets skyld bruker vi extractUrl
                            String cleanUrl = extractUrl(value);
                            if (cleanUrl.startsWith("http")) {
                                html.append("<b>").append(field.label).append(":</b><br>")
                                        .append("<a href=\"").append(cleanUrl).append("\">Åpne fil</a><br><br>");
                                text.append(field.label).append(":\n").append(cleanUrl).append("\n\n");
                            } else {
                                html.append("<b>").append(field.label).append(":</b><br>").append(value).append("<br><br>");
                                text.append(field.label).append(":\n").append(value).append("\n\n");
                            }
                        } else {
                            html.append("<b>").append(field.label).append(":</b><br>").append(value).append("<br><br>");
                            text.append(field.label).append(":\n").append(value).append("\n\n");
                        }
                    }
                }
            }
        } catch (Exception e) {}
    }

    // Rekursiv metode for å hente barn-oppføringer (Vedlegg)
    private void fetchChildEntriesRecursive(List<String> ids, int index, StringBuilder html, StringBuilder text, AlertDialog loader) {
        if (index >= ids.size()) {
            // Alle ferdige! Vis dialogen.
            loader.dismiss();
            showFinalDialog(html, text);
            return;
        }

        String entryId = ids.get(index);
        Log.d(TAG, "Fetching child entry ID: " + entryId);

        RetrofitClient.getApiService().getSingleEntry(entryId).enqueue(new retrofit2.Callback<JsonElement>() {
            @Override
            public void onResponse(retrofit2.Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
                if (response.isSuccessful() && response.body() != null) {
                    try {
                        JsonObject json = response.body().getAsJsonObject();
                        // I Form 18: Felt 1 = Fil, Felt 3 = Beskrivelse, Felt 4 = Beløp

                        // Parse Description and Price
                        String desc = json.has("3") ? json.get("3").getAsString() : "Uten beskrivelse";
                        String price = json.has("4") ? json.get("4").getAsString() : "";

                        html.append("<b>Vedlegg ").append(index + 1).append(":</b><br>");
                        text.append("Vedlegg ").append(index + 1).append(":\n");

                        html.append(desc).append(" (").append(price).append(")<br>");
                        text.append(desc).append(" (").append(price).append(")\n");

                        // Parse File Field (ID 1) - Can be multiple files!
                        if (json.has("1")) {
                            JsonElement fileEl = json.get("1");
                            if (fileEl.isJsonArray()) {
                                // It is a real JSON array
                                JsonArray arr = fileEl.getAsJsonArray();
                                for (int i = 0; i < arr.size(); i++) {
                                    String url = arr.get(i).getAsString().replace("\\/", "/");
                                    html.append("<a href=\"").append(url).append("\">Åpne fil ").append(i+1).append("</a><br>");
                                    text.append(url).append("\n");
                                }
                            } else {
                                // It is a string. Check if it's a JSON string array like "[\"http...\"]"
                                String rawString = fileEl.getAsString();
                                if (rawString.startsWith("[") && rawString.endsWith("]")) {
                                    try {
                                        JSONArray arr = new JSONArray(rawString);
                                        for (int i = 0; i < arr.length(); i++) {
                                            String url = arr.getString(i).replace("\\/", "/");
                                            html.append("<a href=\"").append(url).append("\">Åpne fil ").append(i+1).append("</a><br>");
                                            text.append(url).append("\n");
                                        }
                                    } catch (JSONException ex) {
                                        // Fallback: simple cleanup
                                        String clean = extractUrl(rawString);
                                        html.append("<a href=\"").append(clean).append("\">Åpne fil</a><br>");
                                        text.append(clean).append("\n");
                                    }
                                } else {
                                    // Just a plain URL string
                                    String clean = extractUrl(rawString);
                                    if(clean.startsWith("http")) {
                                        html.append("<a href=\"").append(clean).append("\">Åpne fil</a><br>");
                                        text.append(clean).append("\n");
                                    }
                                }
                            }
                        }
                        html.append("<br>");
                        text.append("\n");

                    } catch (Exception e) {
                        Log.e(TAG, "Error parsing child entry", e);
                    }
                } else {
                    Log.e(TAG, "Failed to fetch child entry: " + response.code());
                }
                // Gå til neste (uansett om denne feilet eller ei)
                fetchChildEntriesRecursive(ids, index + 1, html, text, loader);
            }

            @Override
            public void onFailure(retrofit2.Call<JsonElement> call, Throwable t) {
                Log.e(TAG, "Network error fetching child entry", t);
                // Hopp over ved feil
                fetchChildEntriesRecursive(ids, index + 1, html, text, loader);
            }
        });
    }

    private void showFinalDialog(StringBuilder htmlBuilder, StringBuilder textBuilder) {
        ScrollView scroll = new ScrollView(getContext());
        TextView text = new TextView(getContext());
        text.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
        text.setText(Html.fromHtml(htmlBuilder.toString(), Html.FROM_HTML_MODE_COMPACT));
        text.setPadding(40, 40, 40, 40);
        scroll.addView(text);

        new AlertDialog.Builder(getContext())
                .setTitle("Detaljer")
                .setView(scroll)
                .setPositiveButton("Lukk", null)
                .setNeutralButton("Del", (d, w) -> shareEntryDetails(textBuilder.toString()))
                .create()
                .show();
    }

    // Hjelpemetode for å rydde opp i URLer fra JSON (f.eks ["http://..."] -> http://...)
    private String extractUrl(String rawValue) {
        if (rawValue == null) return "";
        String clean = rawValue.replace("[", "")
                .replace("]", "")
                .replace("\"", "")
                .replace("\\/", "/");
        return clean.trim();
    }

    private void shareEntryDetails(String text) {
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.putExtra(Intent.EXTRA_TEXT, text);
        sendIntent.setType("text/plain");

        Intent shareIntent = Intent.createChooser(sendIntent, "Del innsending via...");
        startActivity(shareIntent);
    }

    private void prefillFormFromHistory(JSONObject latestEntry) {
        if (latestEntry == null) return;
        for (Map.Entry<String, View> entry : inputViews.entrySet()) {
            String fieldId = entry.getKey();
            View view = entry.getValue();

            if (latestEntry.has(fieldId)) {
                String value = latestEntry.optString(fieldId);
                if (value == null || value.isEmpty()) continue;

                if (view instanceof EditText) {
                    ((EditText) view).setText(value);
                } else if (view instanceof RadioGroup) {
                    RadioGroup group = (RadioGroup) view;
                    for (int i = 0; i < group.getChildCount(); i++) {
                        View child = group.getChildAt(i);
                        if (child instanceof RadioButton) {
                            Object tag = child.getTag();
                            if (tag != null && tag.toString().equalsIgnoreCase(value)) {
                                ((RadioButton) child).setChecked(true);
                                break;
                            }
                        }
                    }
                } else if (view instanceof CheckBox) {
                    if ("1".equals(value) || "true".equalsIgnoreCase(value) || ((CheckBox)view).getText().toString().equals(value)) {
                        ((CheckBox) view).setChecked(true);
                    }
                }
            }
        }
        updateStatus("Skjemaet er forhåndsutfylt fra din siste innsending.");
        evaluateAllConditionalLogic();
    }

    private void clearInputs() {
        for (View view : inputViews.values()) {
            if (view instanceof EditText) {
                ((EditText) view).setText("");
            } else if (view instanceof CheckBox) {
                ((CheckBox) view).setChecked(false);
            } else if (view instanceof RadioGroup) {
                ((RadioGroup) view).clearCheck();
            } else if (view instanceof Button) {
                Object tag = view.getTag();
                if (tag instanceof TextView) {
                    ((TextView) tag).setText("Ingen fil valgt");
                }
            }
        }
        fileUploads.clear();
        nestedEntries.clear();
        if (nestedEntriesContainer != null) nestedEntriesContainer.removeAllViews();
        if (totalAmountView != null) totalAmountView.setText("Kr 0,00");
    }

    private static class NestedEntry {
        String id;
        String description;
        String price;
        NestedEntry(String id, String d, String p) { this.id = id; this.description = d; this.price = p;
        }
    }

    private void updateStatus(String msg) {
        if (getActivity() != null && txtStatus != null) {
            getActivity().runOnUiThread(() -> txtStatus.setText(msg));
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\FormsListFragment.java
============================================================
package com.kbs.kbsintranett;

import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class FormsListFragment extends Fragment {

    private LinearLayout formsContainer;
    private ProgressBar progressBar;
    private TextView errorText;
    private SwipeRefreshLayout swipeRefreshLayout;
    private static final Pattern TITLE_NUMBER_PATTERN = Pattern.compile("^(\\d+)[.\\s-]+\\s*(.*)");

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_forms_list, container, false);

        formsContainer = view.findViewById(R.id.forms_container);
        swipeRefreshLayout = view.findViewById(R.id.swipe_refresh);

        // Opprett en ProgressBar manuelt for første gangs lasting
        progressBar = new ProgressBar(getContext());
        LinearLayout.LayoutParams progressParams = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        progressParams.gravity = Gravity.CENTER;
        progressBar.setLayoutParams(progressParams);

        formsContainer.addView(progressBar);

        errorText = new TextView(getContext());
        errorText.setTextColor(Color.RED);
        errorText.setVisibility(View.GONE);
        errorText.setPadding(20, 20, 20, 20);
        formsContainer.addView(errorText);

        // Sett opp listener for swipe
        swipeRefreshLayout.setOnRefreshListener(() -> {
            fetchFormsList();
        });

        // Hent data første gang
        fetchFormsList();

        return view;
    }

    private void fetchFormsList() {
        // Skjul feilmelding før ny henting
        if (errorText != null) errorText.setVisibility(View.GONE);

        RetrofitClient.getApiService().getFormsList().enqueue(new Callback<List<GravityForm>>() {
            @Override
            public void onResponse(Call<List<GravityForm>> call, Response<List<GravityForm>> response) {
                if (!isAdded()) return;

                // Stopp lasting-indikatorer
                progressBar.setVisibility(View.GONE);
                swipeRefreshLayout.setRefreshing(false);

                if (response.isSuccessful() && response.body() != null) {
                    List<GravityForm> allForms = response.body();
                    List<GravityForm> activeForms = new ArrayList<>();

                    for (GravityForm form : allForms) {
                        if (form.getIsActive()) {
                            // NYTT: Hardkodet filtrering av skjemaer som ikke skal vises i listen
                            // ID 10 = HMS-bekreftelse (Skal ligge i Håndbok/separat flyt)
                            // ID 18 = Refusjon-vedlegg (Er et underskjema som brukes av ID 16)
                            if (form.id == 10 || form.id == 18) {
                                continue;
                            }

                            activeForms.add(form);
                        }
                    }

                    Collections.sort(activeForms, new Comparator<GravityForm>() {
                        @Override
                        public int compare(GravityForm f1, GravityForm f2) {
                            int num1 = extractNumber(f1.title);
                            int num2 = extractNumber(f2.title);
                            return Integer.compare(num1, num2);
                        }
                    });
                    populateList(activeForms);

                } else {
                    String msg = "Kunne ikke hente skjemaer. Kode: " + response.code();
                    if (response.code() == 401 || response.code() == 403) {
                        msg += "\n(Mangler tilgang. Prøv å logge ut og inn igjen.)";
                    }
                    showError(msg);
                }
            }

            @Override
            public void onFailure(Call<List<GravityForm>> call, Throwable t) {
                if (!isAdded()) return;

                // Stopp lasting-indikatorer
                progressBar.setVisibility(View.GONE);
                swipeRefreshLayout.setRefreshing(false);

                showError("Nettverksfeil: " + t.getMessage());
            }
        });
    }

    private void populateList(List<GravityForm> forms) {
        formsContainer.removeAllViews();

        if (forms.isEmpty()) {
            showError("Ingen aktive skjemaer funnet.");
            return;
        }

        for (GravityForm form : forms) {
            int formId = form.id;
            String cleanTitle = cleanTitle(form.title);
            addFormButton(formsContainer, cleanTitle, formId);
        }
    }

    private void addFormButton(LinearLayout container, String title, int formId) {
        Button btn = new Button(getContext());
        btn.setText(title);
        btn.setBackgroundColor(Color.parseColor("#0069B3"));
        btn.setTextColor(Color.WHITE);
        btn.setPadding(30, 30, 30, 30);

        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        params.setMargins(0, 0, 0, 20);
        btn.setLayoutParams(params);

        btn.setOnClickListener(v -> {
            Bundle bundle = new Bundle();
            bundle.putInt("formId", formId);
            Navigation.findNavController(v).navigate(R.id.action_formsListFragment_to_formsDetailFragment, bundle);
        });
        container.addView(btn);
    }

    private void showError(String message) {
        if (formsContainer == null) return;
        formsContainer.removeAllViews();

        TextView tv = new TextView(getContext());
        tv.setText(message);
        tv.setTextColor(Color.RED);
        tv.setTextSize(16);
        formsContainer.addView(tv);
    }

    private int extractNumber(String title) {
        if (title == null) return 9999;
        Matcher m = TITLE_NUMBER_PATTERN.matcher(title.trim());
        if (m.find()) {
            try {
                return Integer.parseInt(m.group(1));
            } catch (NumberFormatException e) {
                return 9999;
            }
        }
        return 9999;
    }

    private String cleanTitle(String title) {
        if (title == null) return "";
        Matcher m = TITLE_NUMBER_PATTERN.matcher(title.trim());
        if (m.find()) {
            return m.group(2);
        }
        return title;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\FormSubmission.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.Map;

public class FormSubmission {
    // Gravity Forms krever at dataene ligger inni "input_values"
    @SerializedName("input_values")
    public Map<String, String> inputValues;

    public FormSubmission(Map<String, String> inputValues) {
        this.inputValues = inputValues;
    }
}

============================================================
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
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.Map;

public class GravityEntryResponse {
    @SerializedName("total_count")
    public int totalCount;

    @SerializedName("entries")
    public List<Map<String, String>> entries;
    // Vi bruker Map<String, String> fordi Gravity Forms returnerer alle feltverdier som nøkkel/verdi par i roten av objektet.
    // F.eks: { "id": "100", "form_id": "1", "1.3": "Ola", "1.6": "Nordmann" }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\GravityField.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import java.util.List;

public class GravityField {
    @SerializedName("id")
    public String id;

    @SerializedName("type")
    public String type;

    @SerializedName("inputType")
    public String inputType;

    @SerializedName("label")
    public String label;

    @SerializedName("adminLabel")
    public String adminLabel;

    @SerializedName("description")
    public String description;

    @SerializedName("defaultValue")
    public String defaultValue;

    @SerializedName("isRequired")
    public boolean isRequired;

    @SerializedName("checkboxLabel")
    public String checkboxLabel;

    @SerializedName("visibility")
    public String visibility;

    @JsonAdapter(ChoicesAdapter.class)
    @SerializedName("choices")
    public List<Choice> choices;

    @SerializedName("content")
    public String content;

    // --- BRUKER ADAPTEREN HER ---
    @JsonAdapter(InputsAdapter.class)
    @SerializedName("inputs")
    public List<GravityField> inputs;
    // ---------------------------

    @SerializedName("isHidden")
    public boolean isHidden;

    @SerializedName("gwreadonly_enable")
    public boolean readOnly;

    @JsonAdapter(ConditionalLogicAdapter.class)
    @SerializedName("conditionalLogic")
    public ConditionalLogic conditionalLogic;

    @SerializedName("gppa-values-templates")
    public java.util.Map<String, String> gppaTemplates;

    @SerializedName("gpnfForm")
    public String gpnfForm;

    public static class Choice {
        @SerializedName("text") public String text;
        @SerializedName("value") public String value;
    }

    public static class ConditionalLogic {
        @SerializedName("actionType") public String actionType;
        @SerializedName("logicType") public String logicType;
        @SerializedName("rules") public List<Rule> rules;
    }

    public static class Rule {
        @SerializedName("fieldId") public String fieldId;
        @SerializedName("operator") public String operator;
        @SerializedName("value") public String value;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\GravityForm.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;

public class GravityForm {
    @SerializedName("id")
    public int id;

    @SerializedName("title")
    public String title;

    @SerializedName("description")
    public String description;

    // Endret til Object for å være robust mot både "1" (String) og 1 (Int) fra API
    @SerializedName("is_active")
    public Object isActive;

    @SerializedName("fields")
    public List<GravityField> fields;

    // Hjelpemetode for å sjekke om skjemaet er aktivt
    public boolean getIsActive() {
        if (isActive == null) return false;
        String s = isActive.toString();
        return "1".equals(s) || "true".equalsIgnoreCase(s);
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookAdapter.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookAdapter.java
package com.kbs.kbsintranett;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class HandbookAdapter extends RecyclerView.Adapter<HandbookAdapter.ViewHolder> {

    private List<HandbookItem> fullList;
    private List<HandbookItem> filteredList;
    private OnItemClickListener listener;

    public interface OnItemClickListener {
        void onItemClick(HandbookItem item);
    }

    public HandbookAdapter(List<HandbookItem> items, OnItemClickListener listener) {
        this.fullList = items;
        this.filteredList = new ArrayList<>(items);
        this.listener = listener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_handbook, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        HandbookItem item = filteredList.get(position);
        holder.title.setText(item.getTitle());

        // Håndter ingress: Kutt etter 80 tegn hvis den er veldig lang
        String desc = item.getDescription();
        if (desc == null) desc = "";
        if (desc.length() > 100) {
            desc = desc.substring(0, 100) + "...";
        }
        holder.desc.setText(desc);

        // Mapp ikon-type til ressurs (Utvidet liste fra PHP v3.0)
        int iconRes = R.drawable.ic_handbook_general; // Fallback
        String type = item.getIconType() != null ? item.getIconType() : "";

        switch (type) {
            case "car":         iconRes = R.drawable.ic_handbook_car; break;
            case "health":      iconRes = R.drawable.ic_handbook_health; break;
            case "people":      iconRes = R.drawable.ic_handbook_people; break;
            case "warning":     iconRes = R.drawable.ic_handbook_warning; break;
            case "doc":         iconRes = R.drawable.ic_handbook_doc; break;
            case "card":        iconRes = R.drawable.ic_handbook_doc; break; // Bruker doc inntil videre
            case "computer":    iconRes = R.drawable.ic_handbook_general; break;
            case "calendar":    iconRes = R.drawable.ic_handbook_general; break;
            case "money":       iconRes = R.drawable.ic_handbook_doc; break;
            case "helmet":      iconRes = R.drawable.ic_handbook_warning; break;
            case "trash":       iconRes = R.drawable.ic_handbook_general; break;
            case "book":        iconRes = R.drawable.ic_book; break; // Gjenbruk eksisterende ic_book
            case "chat":        iconRes = R.drawable.ic_handbook_people; break;
            default:            iconRes = R.drawable.ic_handbook_general; break;
        }
        holder.icon.setImageResource(iconRes);

        holder.itemView.setOnClickListener(v -> listener.onItemClick(item));
    }

    @Override
    public int getItemCount() {
        return filteredList.size();
    }

    public void filter(String query) {
        filteredList.clear();
        if (query.isEmpty()) {
            filteredList.addAll(fullList);
        } else {
            String q = query.toLowerCase();
            for (HandbookItem item : fullList) {
                if (item.getTitle().toLowerCase().contains(q) ||
                        (item.getDescription() != null && item.getDescription().toLowerCase().contains(q))) {
                    filteredList.add(item);
                }
            }
        }
        notifyDataSetChanged();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView title, desc;
        ImageView icon;

        public ViewHolder(View view) {
            super(view);
            title = view.findViewById(R.id.title);
            desc = view.findViewById(R.id.desc);
            icon = view.findViewById(R.id.icon);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookDetailFragment.java
============================================================
package com.kbs.kbsintranett;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;

import com.google.gson.JsonObject;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class HandbookDetailFragment extends Fragment {

    private int pageId;
    private String pageTitle;
    private WebView webView;
    private ProgressBar progressBar;

    // --- CSS: DESIGN MED MER LUFT ---
    private static final String CSS_STYLE =
            "<style>" +
                    "body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #333333; line-height: 1.6; padding: 16px; margin: 0; }" +

                    "h1 { color: #0069B3; font-size: 24px; border-bottom: 2px solid #0069B3; padding-bottom: 10px; margin-top: 0; }" +
                    "p, ul, li { margin-bottom: 12px; }" +
                    "a { color: #0069B3; font-weight: bold; text-decoration: none; }" +

                    // --- BOKSEN ---
                    ".trekkspill { " +
                    "   background-color: #fff; " +
                    "   border: 1px solid #ddd; " +
                    "   border-radius: 4px; " +
                    "   max-height: 58px; " + // LUKKET
                    "   overflow: hidden; " +
                    "   transition: max-height 0.4s ease; " +

                    // LUFT OG AVSTAND
                    "   margin-top: 32px; " +
                    "   margin-bottom: 16px; " +
                    "}" +

                    // --- KNAPPEN ---
                    ".trekkspill > a[id^='fl-accordion--label-'] { " +
                    "   display: flex; " +
                    "   justify-content: space-between; " +
                    "   align-items: center; " +
                    "   background-color: #f2f2f2; " +
                    "   color: #0069B3; " +
                    "   padding: 15px; " +
                    "   font-weight: bold; " +
                    "   cursor: pointer; " +
                    "   width: 100%; " +
                    "   height: 58px; " +
                    "   box-sizing: border-box; " +
                    "   pointer-events: auto; " +
                    "}" +

                    // --- PLUSS-TEGN ---
                    ".trekkspill > a[id^='fl-accordion--label-']::after { " +
                    "   content: '+'; " +
                    "   font-size: 24px; " +
                    "   font-weight: bold; " +
                    "   color: #0069B3; " +
                    "}" +

                    // --- ÅPEN TILSTAND ---
                    ".trekkspill.open { " +
                    "   max-height: 4000px; " + // ÅPEN
                    "   overflow: visible; " +
                    "   border-color: #0069B3; " +
                    "}" +

                    ".trekkspill.open > a[id^='fl-accordion--label-'] { " +
                    "   background-color: #0069B3; " +
                    "   color: #ffffff; " +
                    "}" +

                    ".trekkspill.open > a[id^='fl-accordion--label-']::after { " +
                    "   content: '-'; " +
                    "   color: #ffffff; " +
                    "}" +

                    // --- RYDDEJOBB ---
                    ".trekkspill > a[id^='fl-accordion--icon-'] { display: none !important; }" +
                    ".trekkspill > h2 { display: none; }" +
                    ".trekkspill > p, .trekkspill > ul, .trekkspill > div { padding: 10px 15px; }" +

                    "</style>";

    // --- JAVASCRIPT: AUTOSCROLL ---
    private static final String JS_SCRIPT =
            "<script>" +
                    "document.onclick = function(e) {\n" +
                    "    var target = e.target.closest('a[id^=\"fl-accordion--label-\"]');\n" +
                    "    \n" +
                    "    if (target) {\n" +
                    "        e.preventDefault();\n" +
                    "        \n" +
                    "        var currentBox = target.closest('.trekkspill');\n" +
                    "        \n" +
                    "        if (currentBox) {\n" +
                    "            var wasOpen = currentBox.classList.contains('open');\n" +
                    "            \n" +
                    "            // LUKK ALLE ANDRE\n" +
                    "            var allOpenBoxes = document.querySelectorAll('.trekkspill.open');\n" +
                    "            allOpenBoxes.forEach(function(box) {\n" +
                    "                box.classList.remove('open');\n" +
                    "            });\n" +
                    "            \n" +
                    "            // ÅPNE DEN VALGTE\n" +
                    "            if (!wasOpen) {\n" +
                    "                currentBox.classList.add('open');\n" +
                    "                \n" +
                    "                // AUTOSCROLL: Økt til 300ms for å vente på CSS-animasjonen\n" +
                    "                setTimeout(function() {\n" +
                    "                    currentBox.scrollIntoView({behavior: 'smooth', block: 'start'});\n" +
                    "                }, 300);\n" +
                    "            }\n" +
                    "        }\n" +
                    "        return false;\n" +
                    "    }\n" +
                    "};" +
                    "</script>";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_handbook_detail, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (getArguments() != null) {
            pageId = getArguments().getInt("page_id");
            pageTitle = getArguments().getString("page_title");
        }

        Toolbar toolbar = view.findViewById(R.id.detail_toolbar);
        toolbar.setTitle(pageTitle != null ? pageTitle : "Håndbok");
        toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).navigateUp());

        webView = view.findViewById(R.id.detail_webview);
        progressBar = view.findViewById(R.id.detail_loading);

        setupWebView();
        fetchContent();
    }

    private void setupWebView() {
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                return handleLinkClick(request.getUrl().toString());
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                return handleLinkClick(url);
            }
        });
    }

    private boolean handleLinkClick(String url) {
        // Ignorer klikk på accordion-lenker
        if (url.endsWith("#")) return true;

        String lowerUrl = url.toLowerCase();
        int formIdToOpen = 0;

        // --- SPESIALHÅNDTERING: Link til Skjemaer basert på URL-nøkkelord ---

        // ID 1: Ansatteopplysninger
        if (lowerUrl.contains("ansatteopplysninger")) {
            formIdToOpen = 1;
        }
        // ID 2: Vernerunde
        else if (lowerUrl.contains("vernerunde")) {
            formIdToOpen = 2;
        }
        // ID 4: RUH (Rapport om uønsket hendelse)
        else if (lowerUrl.contains("uonsket-hendelse") || lowerUrl.contains("/ruh")) {
            formIdToOpen = 4;
        }
        // ID 5: Lån av verktøy/henger
        else if (lowerUrl.contains("lan-av") || lowerUrl.contains("verktoy")) {
            formIdToOpen = 5;
        }
        // ID 6: Avviksmelding
        else if (lowerUrl.contains("avviksmelding") || lowerUrl.contains("/avvik")) {
            formIdToOpen = 6;
        }
        // ID 9: Sikkerhetskurs / Kompetansebevis
        else if (lowerUrl.contains("sikkerhetskurs") || lowerUrl.contains("kompetansebevis")) {
            formIdToOpen = 9;
        }
        // ID 10: HMS Bekreftelse
        else if (lowerUrl.contains("hms-bekreftelse") || lowerUrl.contains("hms-policy")) {
            formIdToOpen = 10;
        }
        // ID 11: Egenmelding
        else if (lowerUrl.contains("egenmelding")) {
            formIdToOpen = 11;
        }
        // ID 12: Sjekkliste firmabil
        else if (lowerUrl.contains("sjekkliste") && (lowerUrl.contains("bil") || lowerUrl.contains("kjoretoy"))) {
            formIdToOpen = 12;
        }
        // ID 14: SJA (Sikker Jobbanalyse)
        else if (lowerUrl.contains("sja") || lowerUrl.contains("jobbanalyse")) {
            formIdToOpen = 14;
        }
        // ID 15: Fraværsvarsel
        else if (lowerUrl.contains("fravaersvarsel") || lowerUrl.contains("fravarsvarsel")) {
            formIdToOpen = 15;
        }
        // ID 16: Refusjon utlegg
        else if (lowerUrl.contains("refusjon") || lowerUrl.contains("utlegg")) {
            formIdToOpen = 16;
        }
        // ID 21: Medarbeidersamtale
        else if (lowerUrl.contains("medarbeidersamtale")) {
            formIdToOpen = 21;
        }
        // ID 22: Medarbeiderundersøkelse
        else if (lowerUrl.contains("medarbeiderundersokelse")) {
            formIdToOpen = 22;
        }

        // Hvis vi fant et skjema, naviger dit internt
        if (formIdToOpen > 0) {
            Bundle bundle = new Bundle();
            bundle.putInt("formId", formIdToOpen);
            Navigation.findNavController(getView()).navigate(R.id.action_handbook_to_form, bundle);
            return true;
        }

        // --- STANDARD INTERN NAVIGASJON ---
        if (url.contains("intranet.kbs.no") || url.startsWith("/")) {
            int targetId = extractIdFromUrl(url);
            if (targetId > 0) {
                navigateToPage(targetId, "Laster...");
                return true;
            }

            progressBar.setVisibility(View.VISIBLE);
            RetrofitClient.getApiService().lookupPageId(url).enqueue(new Callback<JsonObject>() {
                @Override
                public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                    if (!isAdded()) return;
                    progressBar.setVisibility(View.GONE);

                    if (response.isSuccessful() && response.body() != null) {
                        int id = response.body().get("id").getAsInt();
                        if (id > 0) {
                            navigateToPage(id, "Laster...");
                        } else {
                            openExternal(url);
                        }
                    } else {
                        openExternal(url);
                    }
                }

                @Override
                public void onFailure(Call<JsonObject> call, Throwable t) {
                    if (!isAdded()) return;
                    progressBar.setVisibility(View.GONE);
                    openExternal(url);
                }
            });
            return true;
        } else {
            // Ekstern lenke
            Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            startActivity(browserIntent);
            return true;
        }
    }

    private void navigateToPage(int id, String title) {
        Bundle bundle = new Bundle();
        bundle.putInt("page_id", id);
        bundle.putString("page_title", title);
        Navigation.findNavController(getView()).navigate(R.id.action_handbook_to_detail, bundle);
    }

    private void openExternal(String url) {
        Intent intent = new Intent(getContext(), WebViewActivity.class);
        intent.putExtra(WebViewActivity.EXTRA_URL, url);
        intent.putExtra(WebViewActivity.EXTRA_TITLE, "KBS Intranett");
        startActivity(intent);
    }

    private int extractIdFromUrl(String url) {
        Pattern p = Pattern.compile("[?&](p|page_id|post)=([0-9]+)");
        Matcher m = p.matcher(url);
        if (m.find()) {
            try {
                return Integer.parseInt(m.group(2));
            } catch (NumberFormatException e) {
                return 0;
            }
        }
        return 0;
    }

    private void fetchContent() {
        progressBar.setVisibility(View.VISIBLE);
        RetrofitClient.getApiService().getHandbookPage(pageId).enqueue(new Callback<HandbookPage>() {
            @Override
            public void onResponse(Call<HandbookPage> call, Response<HandbookPage> response) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                if (response.isSuccessful() && response.body() != null) {
                    HandbookPage page = response.body();

                    if (getView() != null) {
                        Toolbar toolbar = getView().findViewById(R.id.detail_toolbar);
                        if (toolbar != null) toolbar.setTitle(page.title);
                    }

                    String htmlContent = "<!DOCTYPE html><html><head>" +
                            "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" +
                            CSS_STYLE +
                            JS_SCRIPT +
                            "</head><body>";

                    htmlContent += "<h1>" + page.title + "</h1>";

                    if (page.content != null) {
                        htmlContent += page.content;
                    } else {
                        htmlContent += "<p>Ingen innhold funnet.</p>";
                    }
                    htmlContent += "</body></html>";

                    webView.loadDataWithBaseURL("https://intranet.kbs.no", htmlContent, "text/html", "UTF-8", null);

                } else {
                    Toast.makeText(getContext(), "Kunne ikke laste innhold.", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<HandbookPage> call, Throwable t) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);
                Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookFragment.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookFragment.java
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation; // Viktig
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class HandbookFragment extends Fragment {

    private RecyclerView recyclerView;
    private ProgressBar progressBar;
    private EditText searchField;
    private HandbookAdapter adapter;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_handbook, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        recyclerView = view.findViewById(R.id.recycler_handbook);
        progressBar = view.findViewById(R.id.progressBar);
        searchField = view.findViewById(R.id.search_field);

        recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));

        searchField.addTextChangedListener(new TextWatcher() {
            @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
            @Override public void onTextChanged(CharSequence s, int start, int before, int count) {}
            @Override public void afterTextChanged(Editable s) {
                if (adapter != null) adapter.filter(s.toString());
            }
        });

        fetchHandbook();
    }

    private void fetchHandbook() {
        RetrofitClient.getApiService().getHandbookItems().enqueue(new Callback<List<HandbookItem>>() {
            @Override
            public void onResponse(Call<List<HandbookItem>> call, Response<List<HandbookItem>> response) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                if (response.isSuccessful() && response.body() != null) {
                    adapter = new HandbookAdapter(response.body(), item -> {
                        // NYTT: Naviger til Native Detail Fragment
                        Bundle bundle = new Bundle();
                        bundle.putInt("page_id", item.getId());
                        bundle.putString("page_title", item.getTitle());

                        Navigation.findNavController(getView())
                                .navigate(R.id.action_handbook_to_detail, bundle);
                    });
                    recyclerView.setAdapter(adapter);
                } else {
                    String msg = "Kunne ikke laste håndboken. Kode: " + response.code();
                    if (response.code() == 404) msg += "\n(Fant ikke foreldresiden 'interninstruks-hms')";
                    Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onFailure(Call<List<HandbookItem>> call, Throwable t) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);
                Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookItem.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookItem.java
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;

public class HandbookItem implements Serializable {
    @SerializedName("id")
    private int id; // NYTT

    @SerializedName("title")
    private String title;

    @SerializedName("desc")
    private String description;

    @SerializedName("icon_type")
    private String iconType;

    @SerializedName("url")
    private String url;

    public int getId() { return id; }
    public String getTitle() { return title; }
    public String getDescription() { return description; }
    public String getIconType() { return iconType; }
    public String getUrl() { return url; }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookPage.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\HandbookPage.java
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;

public class HandbookPage {
    @SerializedName("id")
    public int id;

    @SerializedName("title")
    public String title;

    @SerializedName("content")
    public String content; // Dette er HTML-strengen
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\HomeFragment.java
============================================================
package com.kbs.kbsintranett;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class HomeFragment extends Fragment {
    private ActivityResultLauncher<String> requestPermissionLauncher;
    private RecyclerView calendarRecycler;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestPermissionLauncher = registerForActivityResult(
                new ActivityResultContracts.RequestPermission(),
                isGranted -> {
                    if (calendarRecycler != null) {
                        fetchCalendarEvents(calendarRecycler);
                    }
                }
        );
        startNotificationWorker();
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_home, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        View profileBtn = view.findViewById(R.id.btn_profile);
        if (profileBtn != null) {
            profileBtn.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.navigation_profile));
        }

        // NY LOGIKK: Vis knapp hvis brukeren har tilgang til minst én kalender
        Button btnCreateEvent = view.findViewById(R.id.btn_create_event);
        List<String> writeable = UserManager.getInstance().getWriteableCalendars();

        if (writeable != null && !writeable.isEmpty()) {
            btnCreateEvent.setVisibility(View.VISIBLE);
            btnCreateEvent.setOnClickListener(v -> {
                Navigation.findNavController(view).navigate(R.id.action_home_to_create_event);
            });
        } else {
            btnCreateEvent.setVisibility(View.GONE);
        }

        calendarRecycler = view.findViewById(R.id.recycler_calendar);
        calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
        calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {}));

        TextView viewAllCalendar = view.findViewById(R.id.btn_view_all_calendar);
        if (viewAllCalendar != null) {
            viewAllCalendar.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.action_home_to_calendarFull));
        }

        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED) {
            fetchCalendarEvents(calendarRecycler);
        } else {
            requestPermissionLauncher.launch(Manifest.permission.READ_CALENDAR);
        }

        if (android.os.Build.VERSION.SDK_INT >= 33) {
            if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {}).launch(Manifest.permission.POST_NOTIFICATIONS);
            }
        }

        RecyclerView newsRecycler = view.findViewById(R.id.recycler_news);
        newsRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
        newsRecycler.setNestedScrollingEnabled(false);
        newsRecycler.setAdapter(new NewsAdapter(new ArrayList<>(), item -> {}));

        TextView viewAllNews = view.findViewById(R.id.btn_view_all_news);
        if (viewAllNews != null) {
            viewAllNews.setOnClickListener(v -> {
                Navigation.findNavController(view).navigate(R.id.action_home_to_newsFull);
            });
        }

        fetchNewsFromWordpress(newsRecycler);
    }

    private void fetchCalendarEvents(RecyclerView recyclerView) {
        List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext());

        RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
            @Override
            public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
                if (!isAdded()) return;

                List<CalendarEvent> apiEvents = new ArrayList<>();
                if (response.isSuccessful() && response.body() != null) {
                    apiEvents = response.body();
                    for (CalendarEvent e : apiEvents) {
                        CalendarManager.formatEventForUI(e);
                    }
                }

                List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
                List<CalendarEvent> upcomingEvents = new ArrayList<>();
                String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());

                for (CalendarEvent e : merged) {
                    if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) {
                        upcomingEvents.add(e);
                    }
                }

                List<CalendarEvent> top5 = new ArrayList<>();
                for(int i=0; i<Math.min(upcomingEvents.size(), 5); i++) {
                    top5.add(upcomingEvents.get(i));
                }

                recyclerView.setAdapter(new CalendarAdapter(top5, event -> {
                    CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
                    sheet.show(getParentFragmentManager(), "CalendarDetails");
                }));
            }

            @Override
            public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
                if (!isAdded()) return;
                if (!deviceEvents.isEmpty()) {
                    List<CalendarEvent> top5 = new ArrayList<>();
                    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");
                    }));
                } else {
                    List<CalendarEvent> errorList = new ArrayList<>();
                    errorList.add(new CalendarEvent("Kunne ikke laste kalender", "Sjekk nettverk", "!", "OBS", ""));
                    recyclerView.setAdapter(new CalendarAdapter(errorList, null));
                }
            }
        });
    }

    private void fetchNewsFromWordpress(RecyclerView recyclerView) {
        WordPressApiService apiService = RetrofitClient.getApiService();
        apiService.getPosts().enqueue(new Callback<List<WpPost>>() {
            @Override
            public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
                if (getContext() == null) return;

                if (response.isSuccessful() && response.body() != null) {
                    List<WpPost> wpPosts = response.body();
                    SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
                    rawFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
                    SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
                    targetFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));

                    for (WpPost post : wpPosts) {
                        try {
                            Date date = rawFormat.parse(post.date);
                            post.date = targetFormat.format(date);
                        } catch (Exception e) {}
                    }

                    NewsAdapter adapter = new NewsAdapter(wpPosts, post -> {
                        Bundle bundle = new Bundle();
                        bundle.putSerializable("post_data", post);
                        Navigation.findNavController(getView()).navigate(R.id.action_home_to_newsDetail, bundle);
                    });
                    recyclerView.setAdapter(adapter);
                }
            }

            @Override
            public void onFailure(Call<List<WpPost>> call, Throwable t) {
                if (getContext() == null) return;
                recyclerView.setAdapter(new NewsAdapter(new ArrayList<>(), null));
            }
        });
    }

    private void startNotificationWorker() {
        PeriodicWorkRequest notifRequest =
                new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
                        .build();
        WorkManager.getInstance(requireContext()).enqueue(notifRequest);
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\InputsAdapter.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class InputsAdapter implements JsonDeserializer<List<GravityField>> {
    @Override
    public List<GravityField> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonNull()) {
            return new ArrayList<>();
        }
        // Fikser krasjen: Hvis Gravity Forms sender "" i stedet for [], returner tom liste
        if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) {
            return new ArrayList<>();
        }
        if (json.isJsonArray()) {
            List<GravityField> list = new ArrayList<>();
            for (JsonElement e : json.getAsJsonArray()) {
                list.add(context.deserialize(e, GravityField.class));
            }
            return list;
        }
        return new ArrayList<>();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\InternalLinkMovementMethod.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\InternalLinkMovementMethod.java
package com.kbs.kbsintranett;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; // <-- Sjekk at denne er med!
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.style.URLSpan;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import androidx.navigation.Navigation;

import com.google.gson.JsonObject;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class InternalLinkMovementMethod extends LinkMovementMethod {
    private static InternalLinkMovementMethod instance;
    private static final String TAG = "InternalLinkMethod";

    public static InternalLinkMovementMethod getInstance() {
        if (instance == null) instance = new InternalLinkMovementMethod();
        return instance;
    }

    @Override
    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            int line = widget.getLayout().getLineForVertical(y);
            int off = widget.getLayout().getOffsetForHorizontal(line, x);

            URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
            if (link.length != 0) {
                String url = link[0].getURL();
                handleLink(widget.getContext(), url, widget);
                return true;
            }
        }
        return super.onTouchEvent(widget, buffer, event);
    }

    private void handleLink(Context context, String url, View view) {
        Log.d(TAG, "Link clicked: " + url);

        // 1. Sjekk om det er en intern lenke
        if (url.contains("intranet.kbs.no") || url.startsWith("/")) {

            // a) Prøv å finne ID hvis den finnes i URLen (?p=123)
            int pageId = extractIdFromUrl(url);

            if (pageId > 0) {
                navigateToInternalPage(view, pageId, "Laster...");
            } else {
                // b) Det er en "pen" URL. Vi må spørre APIet hva IDen er.
                // Vi bruker Toast for å gi feedback om at noe skjer
                Toast.makeText(context, "Åpner side...", Toast.LENGTH_SHORT).show();

                RetrofitClient.getApiService().lookupPageId(url).enqueue(new Callback<JsonObject>() {
                    @Override
                    public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                        if (response.isSuccessful() && response.body() != null) {
                            int id = response.body().get("id").getAsInt();
                            if (id > 0) {
                                // Suksess! Naviger internt
                                navigateToInternalPage(view, id, "Laster...");
                            } else {
                                // Fant ikke ID, åpne i WebView som fallback
                                openInWebView(context, url);
                            }
                        } else {
                            openInWebView(context, url);
                        }
                    }

                    @Override
                    public void onFailure(Call<JsonObject> call, Throwable t) {
                        openInWebView(context, url);
                    }
                });
            }
        } else {
            // Ekstern lenke - åpne i nettleser
            Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            context.startActivity(browserIntent);
        }
    }

    private int extractIdFromUrl(String url) {
        Pattern p = Pattern.compile("[?&](p|page_id|post)=([0-9]+)");
        Matcher m = p.matcher(url);
        if (m.find()) {
            try {
                return Integer.parseInt(m.group(2));
            } catch (NumberFormatException e) {
                return 0;
            }
        }
        return 0;
    }

    private void navigateToInternalPage(View view, int pageId, String title) {
        try {
            Bundle bundle = new Bundle();
            bundle.putInt("page_id", pageId);
            bundle.putString("page_title", title);
            Navigation.findNavController(view).navigate(R.id.action_handbook_to_detail, bundle);
        } catch (Exception e) {
            Log.e(TAG, "Kunne ikke navigere", e);
        }
    }

    private void openInWebView(Context context, String url) {
        Intent intent = new Intent(context, WebViewActivity.class);
        intent.putExtra(WebViewActivity.EXTRA_URL, url);
        intent.putExtra(WebViewActivity.EXTRA_TITLE, "KBS Intranett");
        context.startActivity(intent);
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\KbsApplication.java
============================================================
package com.kbs.kbsintranett;

import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;

public class KbsApplication extends Application {

    public static final String CHANNEL_ID = "kbs_calendar_channel";

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
    }

    private void createNotificationChannel() {
        // Vi oppretter kanalen her ved oppstart, så den er klar uansett om
        // det er MainActivity eller en bakgrunnsjobb som trenger den.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "KBS Kalendervarsler",
                    NotificationManager.IMPORTANCE_HIGH
            );
            channel.setDescription("Varsler for kalenderhendelser");

            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\LoginFragment.java
============================================================
package com.kbs.kbsintranett;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;

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.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.tasks.Task;

public class LoginFragment extends Fragment {

    private static final String TAG = "LoginFragment";
    private GoogleSignInClient mGoogleSignInClient;
    private TextView statusText;
    private SignInButton signInButton;

    // Håndterer resultatet fra Google-vinduet
    private final ActivityResultLauncher<Intent> signInLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(result.getData());
                handleGoogleResult(task);
            }
    );

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_login, container, false);
        statusText = view.findViewById(R.id.status_text);
        signInButton = view.findViewById(R.id.sign_in_button);
        signInButton.setSize(SignInButton.SIZE_WIDE);

        // Hent ID fra MainActivity
        String clientId = MainActivity.GOOGLE_WEB_CLIENT_ID;
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(clientId)
                .requestEmail()
                .build();
        mGoogleSignInClient = GoogleSignIn.getClient(requireActivity(), gso);

        signInButton.setOnClickListener(v -> {
            statusText.setText("Starter Google innlogging...");
            Intent signInIntent = mGoogleSignInClient.getSignInIntent();
            signInLauncher.launch(signInIntent);
        });
        return view;
    }

    private void handleGoogleResult(Task<GoogleSignInAccount> completedTask) {
        try {
            GoogleSignInAccount account = completedTask.getResult(ApiException.class);
            // 1. Google er OK. Nå logger vi inn på WordPress.
            statusText.setText("Google OK. Kobler til KBS Intranett...");
            signInButton.setEnabled(false); // Hindre dobbeltklikk

            String photoUrl = (account.getPhotoUrl() != null) ? account.getPhotoUrl().toString() : null;

            AuthRepository.loginToWordPress(
                    account.getIdToken(),
                    account.getDisplayName(),
                    account.getEmail(),
                    photoUrl,
                    new AuthRepository.AuthCallback() {
                        @Override
                        public void onSuccess(String role) {
                            // 2. Alt er OK! Naviger til Hjem.
                            if (isAdded()) {
                                statusText.setText("Innlogging OK!");
                                NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment);
                                navController.navigate(R.id.action_login_to_home);
                            }
                        }

                        @Override
                        public void onError(String message) {
                            if (isAdded()) {
                                statusText.setText(message);
                                signInButton.setEnabled(true);
                            }
                        }
                    }
            );
        } catch (ApiException e) {
            // --- KORRIGERT FEILMELDING ---
            Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
            String message;
            if (e.getStatusCode() == 12500) {
                message = "Konto ikke funnet, eller konto uten rettigheter.";
            } else {
                message = "Google-feil: " + e.getStatusCode();
            }
            statusText.setText(message);
            // --- SLUTT PÅ KORRIGERING ---
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\LoginRequest.java
============================================================
package com.kbs.kbsintranett;

public class LoginRequest {
    public String token;

    public LoginRequest(String token) {
        this.token = token;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\LoginResponse.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\LoginResponse.java
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.util.List;

public class LoginResponse {
    public boolean success;
    @SerializedName("full_cookie")
    public String fullCookie;

    public String role;

    @SerializedName("user_id")
    public int userId;

    @SerializedName("first_name")
    public String firstName;

    @SerializedName("last_name")
    public String lastName;

    @SerializedName("stilling")
    public String stilling;

    @SerializedName("mobiltelefon")
    public String mobiltelefon;

    // NYTT FELT: Liste over kalendere brukeren kan skrive til
    @SerializedName("writeable_calendars")
    public List<String> writeableCalendars;

    public String message;
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\MainActivity.java
============================================================
package com.kbs.kbsintranett;

import android.Manifest;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
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.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.material.bottomnavigation.BottomNavigationView;

import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {

    // VIKTIG: Sørg for at denne matcher den du har i Google Cloud Console
    public static final String GOOGLE_WEB_CLIENT_ID = "SECRET.apps.googleusercontent.com";

    private static final String TAG = "MainActivity";
    private NavController navController;
    private BottomNavigationView bottomNav;

    // Launcher for å spørre om varslingstillatelse (Android 13+)
    private ActivityResultLauncher<String> requestPermissionLauncher;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // --- 1. SETUP UI & NAVIGASJON ---
        // Sjekket activity_main.xml: ID er "bottom_nav_view"
        bottomNav = findViewById(R.id.bottom_nav_view);

        NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
                .findFragmentById(R.id.nav_host_fragment);

        if (navHostFragment != null) {
            navController = navHostFragment.getNavController();
            if (bottomNav != null) {
                NavigationUI.setupWithNavController(bottomNav, navController);
            }

            // Skjul meny på login-skjerm
            navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
                if (bottomNav == null) return;

                if (destination.getId() == R.id.navigation_login) {
                    bottomNav.setVisibility(View.GONE);
                } else {
                    bottomNav.setVisibility(View.VISIBLE);
                }
            });
        }

        // --- 2. VARSLINGSOPPSETT (NYTT) ---
        createNotificationChannel();

        // Initialiser permission launcher for varsler
        requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
            if (isGranted) {
                Log.d(TAG, "Varslingstillatelse gitt!");
            } else {
                Log.e(TAG, "Varslingstillatelse avslått. Bruker får ikke kalendervarsler.");
            }
        });

        // Sjekk tillatelser (både Varsler og Alarmer)
        checkNotificationPermission();
        checkExactAlarmPermission();

        // Start bakgrunnsjobben for kalenderen
        scheduleCalendarWork();

        // --- 3. AUTENTISERING (GAMMELT) ---
        checkLoginState();
    }

    /**
     * Sjekker om brukeren er logget inn med Google, og gjør en silent refresh mot WP.
     */
    private void checkLoginState() {
        GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
        if (account == null) {
            navigateToLogin();
        } else {
            refreshGoogleToken();
        }
    }

    private void refreshGoogleToken() {
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(GOOGLE_WEB_CLIENT_ID)
                .requestEmail()
                .build();
        GoogleSignInClient client = GoogleSignIn.getClient(this, gso);

        client.silentSignIn()
                .addOnSuccessListener(account -> {
                    String photoUrl = (account.getPhotoUrl() != null) ? account.getPhotoUrl().toString() : null;

                    AuthRepository.loginToWordPress(
                            account.getIdToken(),
                            account.getDisplayName(),
                            account.getEmail(),
                            photoUrl,
                            new AuthRepository.AuthCallback() {
                                @Override
                                public void onSuccess(String role) {
                                    Log.d(TAG, "Silent login fullført. Rolle: " + role);
                                    if (navController != null && navController.getCurrentDestination() != null &&
                                            navController.getCurrentDestination().getId() == R.id.navigation_login) {
                                        navController.navigate(R.id.action_login_to_home);
                                    }
                                }

                                @Override
                                public void onError(String message) {
                                    Log.e(TAG, "Silent login feilet mot WP: " + message);
                                    navigateToLogin();
                                }
                            }
                    );
                })
                .addOnFailureListener(e -> {
                    Log.e(TAG, "Silent Sign-In feilet mot Google", e);
                    navigateToLogin();
                });
    }

    private void navigateToLogin() {
        if (navController != null) {
            if (navController.getCurrentDestination() != null &&
                    navController.getCurrentDestination().getId() != R.id.navigation_login) {
                navController.navigate(R.id.navigation_login);
            }
        }
    }

    // --- NYE HJELPEMETODER FOR VARSLING ---

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = "KBS Kalendervarsler";
            String description = "Varsler for kalenderhendelser";
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel("kbs_calendar_channel", name, importance);
            channel.setDescription(description);
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(channel);
            }
        }
    }

    private void checkNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
            }
        }
    }

    /**
     * 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()) {
                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();
            }
        }
    }

    /**
     * Starter WorkManager som sjekker kalenderen hvert 15. minutt.
     */
    private void scheduleCalendarWork() {
        PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
                .build();

        WorkManager.getInstance(this).enqueueUniquePeriodicWork(
                "KbsCalendarWork",
                ExistingPeriodicWorkPolicy.UPDATE,
                workRequest
        );
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsAdapter.java
============================================================
package com.kbs.kbsintranett;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import java.util.List;

public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {

    private List<WpPost> posts;
    private OnItemClickListener listener; // NYTT

    // Interface for klikk
    public interface OnItemClickListener {
        void onItemClick(WpPost post);
    }

    // Oppdatert konstruktør
    public NewsAdapter(List<WpPost> posts, OnItemClickListener listener) {
        this.posts = posts;
        this.listener = listener;
    }

    // Overload for bakoverkompatibilitet (hvis du ikke sender listener)
    public NewsAdapter(List<WpPost> posts) {
        this.posts = posts;
        this.listener = null;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        WpPost post = posts.get(position);

        holder.title.setText(post.getTitleStr());
        holder.excerpt.setText(post.getExcerptStr());
        holder.date.setText(post.date);

        String cat = post.getCategoryName();
        if (!cat.isEmpty()) {
            holder.category.setText(cat);
            holder.category.setVisibility(View.VISIBLE);
        } else {
            holder.category.setVisibility(View.GONE);
        }

        String imageUrl = post.getFeaturedImageUrl();
        if (imageUrl != null && !imageUrl.isEmpty()) {
            holder.image.setVisibility(View.VISIBLE);
            Glide.with(holder.itemView.getContext())
                    .load(imageUrl)
                    .transition(DrawableTransitionOptions.withCrossFade())
                    .centerCrop()
                    .into(holder.image);
        } else {
            holder.image.setVisibility(View.GONE);
        }

        // NYTT: Håndter klikk
        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onItemClick(post);
            }
        });
    }

    @Override
    public int getItemCount() {
        return posts.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView title, excerpt, date, category;
        ImageView image;

        public ViewHolder(View view) {
            super(view);
            title = view.findViewById(R.id.news_title);
            excerpt = view.findViewById(R.id.news_excerpt);
            date = view.findViewById(R.id.news_date);
            category = view.findViewById(R.id.news_category);
            image = view.findViewById(R.id.news_image);
        }
    }

    // NYTT: Metode for å oppdatere listen etter filtrering
    public void updateList(List<WpPost> newPosts) {
        this.posts = newPosts;
        notifyDataSetChanged();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsDetailFragment.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import com.bumptech.glide.Glide;

public class NewsDetailFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_news_detail, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // Hent data fra argumentene (sendt fra HomeFragment/NewsFullFragment)
        if (getArguments() != null) {
            WpPost post = (WpPost) getArguments().getSerializable("post_data");
            if (post != null) {
                setupViews(view, post);
            }
        }
    }

    private void setupViews(View view, WpPost post) {
        Toolbar toolbar = view.findViewById(R.id.detail_toolbar);
        toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).navigateUp());

        ImageView image = view.findViewById(R.id.detail_image);
        TextView title = view.findViewById(R.id.detail_title);
        TextView category = view.findViewById(R.id.detail_category);
        TextView date = view.findViewById(R.id.detail_date);
        TextView author = view.findViewById(R.id.detail_author); // NY
        TextView content = view.findViewById(R.id.detail_content);

        String imgUrl = post.getFeaturedImageUrl();
        if (imgUrl != null) {
            Glide.with(this).load(imgUrl).centerCrop().into(image);
        } else {
            image.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
        }

        title.setText(post.getTitleStr());
        category.setText(post.getCategoryName());
        date.setText("Publisert: " + post.date);

        // NYTT: Sett forfatter
        author.setText("Av: " + post.getAuthorName());

        content.setText(Html.fromHtml(post.getContentStr(), Html.FROM_HTML_MODE_COMPACT));
        content.setMovementMethod(LinkMovementMethod.getInstance());
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsFullFragment.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class NewsFullFragment extends Fragment {

    private RecyclerView recyclerViewNews;
    private RecyclerView recyclerViewCategories;
    private ProgressBar progressBar;
    private NewsAdapter newsAdapter;
    private List<WpPost> allPosts = new ArrayList<>(); // Holder på ALLE postene

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_news_full, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        recyclerViewNews = view.findViewById(R.id.recycler_news_full);
        recyclerViewCategories = view.findViewById(R.id.recycler_categories);
        progressBar = view.findViewById(R.id.loading_news_full);
        ImageView backBtn = view.findViewById(R.id.btn_back_news);

        // Setup Nyhetsliste
        recyclerViewNews.setLayoutManager(new LinearLayoutManager(getContext()));

        // Setup Kategorier (Horisontal)
        recyclerViewCategories.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
        setupCategories();

        backBtn.setOnClickListener(v -> Navigation.findNavController(view).navigateUp());

        fetchAllNews();
    }

    private void setupCategories() {
        // Listen over kategorier du ønsket
        List<String> categories = Arrays.asList(
                "Alle", // Standard vis alt
                "Avtaler og invitasjoner", "BHT", "Bilhold", "Cordel",
                "Ferieavvikling", "Fest og moro", "Generell drift",
                "HMS", "IT og sikkerhet", "Miljøfyrtårn", "Møtereferat", "SMX"
        );

        CategoryAdapter catAdapter = new CategoryAdapter(categories, selectedCategory -> {
            filterNews(selectedCategory);
        });
        recyclerViewCategories.setAdapter(catAdapter);
    }

    private void fetchAllNews() {
        progressBar.setVisibility(View.VISIBLE);
        // Hent 50 siste (bør holde for en "Siste nytt" liste, ellers må vi paginere)
        RetrofitClient.getApiService().getAllPosts().enqueue(new Callback<List<WpPost>>() {
            @Override
            public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);

                if (response.isSuccessful() && response.body() != null) {
                    allPosts = response.body();
                    formatDates(allPosts);

                    // Vis alle i starten
                    newsAdapter = new NewsAdapter(new ArrayList<>(allPosts), post -> {
                        Bundle bundle = new Bundle();
                        bundle.putSerializable("post_data", post);
                        Navigation.findNavController(getView()).navigate(R.id.action_newsFull_to_newsDetail, bundle);
                    });
                    recyclerViewNews.setAdapter(newsAdapter);
                } else {
                    Toast.makeText(getContext(), "Klarte ikke laste nyheter", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<List<WpPost>> call, Throwable t) {
                if (!isAdded()) return;
                progressBar.setVisibility(View.GONE);
                Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void filterNews(String category) {
        if (newsAdapter == null) return;

        List<WpPost> filteredList = new ArrayList<>();

        if (category.equals("Alle")) {
            filteredList.addAll(allPosts);
        } else {
            for (WpPost post : allPosts) {
                // Vi sjekker om kategorinavnet matcher
                if (post.getCategoryName().equals(category)) {
                    filteredList.add(post);
                }
            }
        }

        // Oppdater adapteren med den filtrerte listen
        newsAdapter.updateList(filteredList);
    }

    private void formatDates(List<WpPost> posts) {
        SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
        rawFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
        SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
        targetFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));

        for (WpPost post : posts) {
            try {
                Date date = rawFormat.parse(post.date);
                post.date = targetFormat.format(date);
            } catch (Exception e) {}
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NewsItem.java
============================================================
package com.kbs.kbsintranett;

public class NewsItem {
    private String title;
    private String excerpt; // Kort tekst/ingress
    private String author;

    public NewsItem(String title, String excerpt, String author) {
        this.title = title;
        this.excerpt = excerpt;
        this.author = author;
    }

    public String getTitle() { return title; }
    public String getExcerpt() { return excerpt; }
    public String getAuthor() { return author; }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\NotificationWorker.java
============================================================
package com.kbs.kbsintranett;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import retrofit2.Response;

public class NotificationWorker extends Worker {
    private static final String TAG = "NotificationWorker";
    private static final String PREFS_NAME = "kbs_alarm_history";

    public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @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);
        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();
        SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());

        long catchUpWindow = now - (30 * 60 * 1000L);
        long futureWindow = now + (30 * 24 * 60 * 60 * 1000L); // 30 dager frem

        for (CalendarEvent event : events) {
            try {
                if (event.getRawDate() == null || event.getRawDate().length() == 10) continue;

                Date eventDate = null;
                if (event.getRawDate().contains("T")) {
                    String raw = event.getRawDate();
                    if (raw.length() > 19) raw = raw.substring(0, 19);
                    eventDate = isoFormat.parse(raw);
                }
                if (eventDate == null) continue;

                // Loop gjennom alle varsler for denne hendelsen
                for (int minutesBefore : event.getReminders()) {
                    if (minutesBefore < 0) continue; // 0 betyr nå "ved start", negative ignoreres

                    long triggerTime = eventDate.getTime() - (minutesBefore * 60 * 1000L);

                    // Unik nøkkel for denne alarmen: EventID + Tidspunkt
                    String alarmKey = "alarm_" + event.getId() + "_" + triggerTime;

                    // Sjekk om vi allerede har fyrt denne alarmen
                    if (prefs.getBoolean(alarmKey, false)) {
                        continue; // Allerede håndtert
                    }

                    if (triggerTime > catchUpWindow && triggerTime < futureWindow) {
                        if (triggerTime < now) {
                            triggerTime = now + 1000; // Catch-up
                        }

                        int alarmId = alarmKey.hashCode();
                        Intent intent = new Intent(context, AlarmReceiver.class);
                        intent.putExtra("TITLE", event.getTitle());
                        String timeStr = new SimpleDateFormat("HH:mm", Locale.getDefault()).format(eventDate);
                        intent.putExtra("MESSAGE", "Starter kl " + timeStr);
                        intent.putExtra("ID", alarmId);

                        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                                context, alarmId, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
                        );

                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
                        } else {
                            alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
                        }

                        // Marker som satt
                        prefs.edit().putBoolean(alarmKey, true).apply();
                        Log.i(TAG, "Satt alarm: " + event.getTitle() + " (" + minutesBefore + "m før)");
                    }
                }
            } catch (Exception e) {
                Log.e(TAG, "Feil", e);
            }
        }

        // Rensk opp gamle nøkler (valgfritt, for å spare plass over tid)
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\ProfileFragment.java
============================================================
package com.kbs.kbsintranett;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;

public class ProfileFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_profile, container, false);

        // 1. Finn Views
        ImageView closeBtn = view.findViewById(R.id.btn_close_profile);
        ImageView profileImage = view.findViewById(R.id.profile_image);
        TextView nameText = view.findViewById(R.id.profile_name);
        TextView emailText = view.findViewById(R.id.profile_email);
        TextView roleText = view.findViewById(R.id.profile_role);
        Button logoutBtn = view.findViewById(R.id.btn_logout);
        Button updateInfoBtn = view.findViewById(R.id.btn_update_info); // NY

        // 2. Hent data fra UserManager
        UserManager user = UserManager.getInstance();
        nameText.setText(user.getUserDisplayName());
        emailText.setText(user.getUserEmail());
        roleText.setText("Rolle: " + user.getUserRole());

        // 3. Last bilde med Glide
        if (user.getPhotoUrl() != null) {
            Glide.with(this)
                    .load(user.getPhotoUrl())
                    .apply(RequestOptions.circleCropTransform())
                    .into(profileImage);
        }

        // 4. Håndter "Lukk" (X) knapp - Gå tilbake til forrige skjerm
        closeBtn.setOnClickListener(v -> {
            Navigation.findNavController(view).navigateUp();
        });

        // 5. Håndter "Oppdater opplysninger" (Skjema ID 1)
        updateInfoBtn.setOnClickListener(v -> {
            Bundle bundle = new Bundle();
            bundle.putInt("formId", 1); // ID 1 er Ansatteopplysninger
            Navigation.findNavController(view).navigate(R.id.action_profile_to_form, bundle);
        });

        // 6. Håndter utlogging
        logoutBtn.setOnClickListener(v -> performLogout());

        return view;
    }

    private void performLogout() {
        // A. Konfigurer Google Client
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(MainActivity.GOOGLE_WEB_CLIENT_ID)
                .requestEmail()
                .build();
        GoogleSignInClient client = GoogleSignIn.getClient(requireActivity(), gso);

        // B. Logg ut fra Google
        client.signOut().addOnCompleteListener(task -> {

            // C. Tøm interne data
            UserManager.getInstance().logout();
            RetrofitClient.clearClient();

            // D. Naviger tilbake til Login-skjermen
            NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment);
            // Denne aksjonen finnes i mobile_navigation.xml
            navController.navigate(R.id.action_profile_to_login);

            Toast.makeText(getContext(), "Du er nå logget ut", Toast.LENGTH_SHORT).show();
        });
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\RetrofitClient.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import java.io.IOException;
import java.util.List;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {
    private static final String BASE_URL = "https://intranet.kbs.no/";
    private static Retrofit retrofit = null;
    public static WordPressApiService getApiService() {
        if (retrofit == null) {

            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            if (BuildConfig.DEBUG) {
                // I debug-modus logger vi det mest nødvendige
                logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
            } else {
                // I release er vi stille for ytelse og sikkerhet
                logging.setLevel(HttpLoggingInterceptor.Level.NONE);
            }

            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .addInterceptor(new Interceptor() {
                        @Override
                        public Response intercept(Chain chain) throws IOException {
                            Request originalRequest = chain.request();
                            Request.Builder builder = originalRequest.newBuilder();

                            String dynamicCookie = UserManager.getInstance().getCookie();
                            if (dynamicCookie != null && !dynamicCookie.isEmpty()) {
                                builder.header("Cookie", dynamicCookie);
                            }

                            return chain.proceed(builder.build());
                        }
                    })
                    .build();

            Gson gson = new GsonBuilder()
                    .registerTypeAdapter(new TypeToken<List<GravityField.Choice>>(){}.getType(), new ChoicesAdapter())
                    .setLenient()
                    .create();

            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .client(client)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        }
        return retrofit.create(WordPressApiService.class);
    }

    public static void clearClient() {
        retrofit = null;
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\UserManager.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\UserManager.java
package com.kbs.kbsintranett;

import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

/**
 * UserManager fungerer som en global sesjon for appen.
 * Den holder på informasjon om innlogget bruker, rettigheter og autentiserings-cookie.
 */
public class UserManager {
    private static UserManager instance;

    // Google Data
    private String userDisplayName;
    private String userEmail;
    private String googleIdToken;
    private String photoUrl;

    // WordPress Data
    private int userId;
    private String userRole;
    private String currentCookie;

    // Extended Info
    private String firstName;
    private String lastName;
    private String stilling;
    private String mobiltelefon;

    // NYTT:
    private List<String> writeableCalendars = new ArrayList<>();

    private UserManager() {}

    public static synchronized UserManager getInstance() {
        if (instance == null) {
            instance = new UserManager();
        }
        return instance;
    }

    public void setUserData(String name, String email, String token, @Nullable String photoUrl) {
        this.userDisplayName = name;
        this.userEmail = email;
        this.googleIdToken = token;
        this.photoUrl = photoUrl;
    }

    public void setExtendedUserInfo(String firstName, String lastName, String stilling, String mobiltelefon) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.stilling = stilling;
        this.mobiltelefon = mobiltelefon;
    }

    public void setWriteableCalendars(List<String> calendars) {
        this.writeableCalendars = calendars != null ? calendars : new ArrayList<>();
    }

    public List<String> getWriteableCalendars() {
        return writeableCalendars;
    }

    public void setCookie(String cookie) { this.currentCookie = cookie; }
    public void setUserRole(String role) { this.userRole = role; }
    public void setUserId(int id) { this.userId = id; }

    public String getUserDisplayName() { return userDisplayName != null ? userDisplayName : ""; }
    public String getUserEmail() { return userEmail != null ? userEmail : ""; }
    public String getGoogleIdToken() { return googleIdToken; }
    public String getPhotoUrl() { return photoUrl; }
    public String getCookie() { return currentCookie; }
    public String getUserRole() { return userRole != null ? userRole : "subscriber"; }
    public int getUserId() { return userId; }
    public String getFirstName() { return firstName != null ? firstName : ""; }
    public String getLastName() { return lastName != null ? lastName : ""; }
    public String getStilling() { return stilling != null ? stilling : ""; }
    public String getMobiltelefon() { return mobiltelefon != null ? mobiltelefon : ""; }

    public boolean isLoggedIn() { return userEmail != null && !userEmail.isEmpty(); }
    public boolean isAdmin() { return "administrator".equalsIgnoreCase(userRole); }
    public boolean isEditorOrAbove() {
        return userRole != null && (userRole.equalsIgnoreCase("administrator") || userRole.equalsIgnoreCase("editor"));
    }

    public void logout() {
        userDisplayName = null;
        userEmail = null;
        googleIdToken = null;
        photoUrl = null;
        userRole = null;
        currentCookie = null;
        userId = 0;
        firstName = null;
        lastName = null;
        stilling = null;
        mobiltelefon = null;
        writeableCalendars.clear();
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\WebViewActivity.java
============================================================
package com.kbs.kbsintranett;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.webkit.CookieManager;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

public class WebViewActivity extends AppCompatActivity {

    public static final String EXTRA_URL = "extra_url";
    public static final String EXTRA_TITLE = "extra_title";

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);

        String url = getIntent().getStringExtra(EXTRA_URL);
        String title = getIntent().getStringExtra(EXTRA_TITLE);

        Toolbar toolbar = findViewById(R.id.toolbar);
        toolbar.setTitle(title != null ? title : "Håndbok");
        toolbar.setNavigationIcon(android.R.drawable.ic_menu_revert);
        toolbar.setNavigationOnClickListener(v -> finish());

        WebView webView = findViewById(R.id.webview);
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setBuiltInZoomControls(true);
        settings.setDisplayZoomControls(false);

        // --- MAGIEN SKJER HER: INJISER COOKIE ---
        String cookie = UserManager.getInstance().getCookie();
        if (cookie != null && !cookie.isEmpty()) {
            CookieManager cookieManager = CookieManager.getInstance();
            cookieManager.setAcceptCookie(true);
            // Vi antar at domenet er intranet.kbs.no basert på APIet
            cookieManager.setCookie("https://intranet.kbs.no", cookie);
        }
        // ----------------------------------------

        webView.setWebViewClient(new WebViewClient()); // Åpne linker i samme WebView

        if (url != null) {
            webView.loadUrl(url);
        }
    }
}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\WordPressApiService.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.List;
import java.util.Map;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Multipart;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.Query;

public interface WordPressApiService {
    @GET("wp-json/wp/v2/posts?per_page=10&_embed")
    Call<List<WpPost>> getPosts();
    @GET("wp-json/kbs/v1/forms/{id}")
    Call<GravityForm> getForm(@Path("id") int formId);

    @POST("wp-json/gf/v2/forms/{id}/submissions")
    Call<JsonElement> submitForm(@Path("id") int formId, @Body FormSubmission submission);

    @POST("wp-json/kbs/v1/login")
    Call<LoginResponse> googleLogin(@Body LoginRequest request);

    @GET("wp-json/kbs/v1/forms")
    Call<List<GravityForm>> getFormsList();

    @Multipart
    @POST("wp-json/gf/v2/forms/{id}/submissions")
    Call<JsonElement> submitMultipartForm(
            @Path("id") int formId,
            @PartMap Map<String, RequestBody> textFields,
            @Part List<MultipartBody.Part> files
    );

    @GET("wp-json/kbs/v1/calendar/events")
    Call<List<CalendarEvent>> getCalendarEvents();

    // DETTE ER METODEN SOM MANGLER:
    @POST("wp-json/kbs/v1/calendar/create")
    Call<JsonElement> createCalendarEvent(@Body CreateEventRequest request);

    @GET("wp-json/gf/v2/entries")
    Call<GravityEntryResponse> getEntries(
            @Query("form_ids") int formId,
            @Query("search") String searchJson,
            @Query("paging[page_size]") int pageSize
    );

    @GET("wp-json/gf/v2/entries/{entry_id}")
    Call<JsonElement> getSingleEntry(@Path("entry_id") String entryId);

    @GET("wp-json/wp/v2/posts?per_page=50&_embed")
    Call<List<WpPost>> getAllPosts();

    @GET("wp-json/kbs/v1/handbook")
    Call<List<HandbookItem>> getHandbookItems();

    @GET("wp-json/kbs/v1/handbook/{id}")
    Call<HandbookPage> getHandbookPage(@Path("id") int id);

    @GET("wp-json/kbs/v1/lookup-id")
    Call<JsonObject> lookupPageId(@Query("url") String url);

    @POST("wp-json/kbs/v1/calendar/update")
    Call<JsonElement> updateCalendarEvent(@Body CreateEventRequest request);

    @POST("wp-json/kbs/v1/calendar/delete")
    Call<JsonElement> deleteCalendarEvent(@Body CreateEventRequest request); // Sender kun ID og cal_type


}

============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\WpPost.java
============================================================
package com.kbs.kbsintranett;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

public class WpPost implements Serializable {
    @SerializedName("title")
    public Rendered title;

    @SerializedName("excerpt")
    public Rendered excerpt;

    @SerializedName("content")
    public Rendered content;

    @SerializedName("date")
    public String date;

    @SerializedName("_embedded")
    public Embedded embedded;

    public static class Rendered implements Serializable {
        @SerializedName("rendered")
        public String renderedString;
    }

    public static class Embedded implements Serializable {
        @SerializedName("wp:featuredmedia")
        public List<Media> mediaList;

        @SerializedName("wp:term")
        public List<List<Term>> termList;

        // NYTT: Forfatter-liste
        @SerializedName("author")
        public List<Author> authorList;
    }

    public static class Media implements Serializable {
        @SerializedName("source_url")
        public String sourceUrl;
    }

    public static class Term implements Serializable {
        @SerializedName("name")
        public String name;
    }

    // NYTT: Forfatter-klasse
    public static class Author implements Serializable {
        @SerializedName("name")
        public String name;
    }

    public String getTitleStr() {
        return title != null ? title.renderedString : "Uten tittel";
    }

    public String getExcerptStr() {
        return excerpt != null ?
                android.text.Html.fromHtml(excerpt.renderedString, android.text.Html.FROM_HTML_MODE_COMPACT).toString().trim() : "";
    }

    public String getContentStr() {
        return content != null ? content.renderedString : "";
    }

    public String getFeaturedImageUrl() {
        if (embedded != null && embedded.mediaList != null && !embedded.mediaList.isEmpty()) {
            return embedded.mediaList.get(0).sourceUrl;
        }
        return null;
    }

    // NYTT: Hent forfatternavn
    public String getAuthorName() {
        if (embedded != null && embedded.authorList != null && !embedded.authorList.isEmpty()) {
            return embedded.authorList.get(0).name;
        }
        return "Ukjent"; // Fallback
    }

    public String getCategoryName() {
        if (embedded != null && embedded.termList != null && !embedded.termList.isEmpty()) {
            List<Term> categories = embedded.termList.get(0);
            if (categories == null || categories.isEmpty()) return "";

            List<String> priorityCategories = Arrays.asList(
                    "Avtaler og invitasjoner", "BHT", "Bilhold", "Cordel",
                    "Ferieavvikling", "Fest og moro", "Generell drift",
                    "HMS", "IT og sikkerhet", "Miljøfyrtårn", "Møtereferat", "SMX"
            );

            for (Term term : categories) {
                if (priorityCategories.contains(term.name)) return term.name;
            }
            for (Term term : categories) {
                if (term.name.contains("Alle ansatte")) return "Til info";
            }
            return categories.get(0).name;
        }
        return "";
    }
}

============================================================
FILSTI: app\src\main\res\color\selector_day_text.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:color="#FFFFFF"/>
    <item android:color="#333333"/>
</selector>

============================================================
FILSTI: app\src\main\res\drawable\bg_category_selected.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/kbs_logo_blue" />
    <corners android:radius="20dp" />
</shape>

============================================================
FILSTI: app\src\main\res\drawable\bg_category_unselected.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF" />
    <stroke android:width="1dp" android:color="#DDDDDD" />
    <corners android:radius="20dp" />
</shape>

============================================================
FILSTI: app\src\main\res\drawable\ic_book.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
      
    <path android:fillColor="@android:color/white" android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L720,80Q753,80 776.5,103.5Q800,127 800,160L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM240,800L720,800Q720,800 720,800Q720,800 720,800L720,160Q720,160 720,160Q720,160 720,160L640,160L640,440L540,380L440,440L440,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800ZM240,800Q240,800 240,800Q240,800 240,800L240,160Q240,160 240,160Q240,160 240,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800ZM440,440L540,380L640,440L640,440L540,380L440,440Z"/>
    
</vector>


============================================================
FILSTI: app\src\main\res\drawable\ic_form.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
      
    <path android:fillColor="@android:color/white" android:pathData="M22,7h-9v2h9V7zM22,15h-9v2h9V15zM5.54,11L2,7.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,11zM5.54,19L2,15.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,19z"/>
    
</vector>


============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_car.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zM17.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11H5z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_doc.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6zm2,16H8v-2h8v2zm0,-4H8v-2h8v2zm-3,-5V3.5L18.5,9H13z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_general.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2V7h-2v2z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_health.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM17,13h-4v4h-2v-4H7v-2h4V7h2v4h4V13z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_people.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zm-8,0c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zm0,2c-2.33,0 -7,1.17 -7,3.5V19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zm8,0c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45V19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_handbook_warning.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="#0069B3">
    <path android:fillColor="@android:color/white" android:pathData="M1,21h22L12,2 1,21zm12,-3h-2v-2h2v2zm0,-4h-2v-4h2v4z"/>
</vector>

============================================================
FILSTI: app\src\main\res\drawable\ic_home.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
      
    <path android:fillColor="@android:color/white" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
    
</vector>


============================================================
FILSTI: app\src\main\res\drawable\ic_launcher_background.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<vector
    android:height="108dp"
    android:width="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#3DDC84"
          android:pathData="M0,0h108v108h-108z"/>
    <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>


============================================================
FILSTI: app\src\main\res\drawable\ic_launcher_foreground.xml
============================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="85.84757"
                android:endY="92.4963"
                android:startX="42.9492"
                android:startY="49.59793"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
        android:strokeWidth="1"
        android:strokeColor="#00000000" />
</vector>

============================================================
FILSTI: app\src\main\res\drawable\selector_day_toggle.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true">
        <shape android:shape="oval">
            <solid android:color="#0069B3"/> <!-- KBS Blå -->
        </shape>
    </item>
    <item>
        <shape android:shape="oval">
            <solid android:color="#EEEEEE"/> <!-- Lys grå -->
        </shape>
    </item>

</selector>

============================================================
FILSTI: app\src\main\res\layout\activity_main.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:navGraph="@navigation/mobile_navigation"
        app:layout_constraintBottom_toTopOf="@id/bottom_nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="?android:attr/windowBackground"
        app:menu="@menu/bottom_nav_menu"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

============================================================
FILSTI: app\src\main\res\layout\activity_webview.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/white"
        android:elevation="4dp"
        android:theme="@style/ThemeOverlay.AppCompat.Light" />

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\bottom_sheet_calendar_details.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="24dp"
    android:background="@android:color/white">
    <TextView
        android:id="@+id/sheet_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Tittel"
        android:textSize="22sp"
        android:textStyle="bold"
        android:textColor="@color/black"
        android:layout_marginBottom="8dp"/>

    <TextView
        android:id="@+id/sheet_time"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Tidspunkt"
        android:textSize="16sp"
        android:textColor="@color/kbs_muted_blue_gray"
        android:layout_marginBottom="16dp"
        android:drawablePadding="8dp"
        app:drawableStartCompat="@android:drawable/ic_menu_recent_history"/>

    <TextView
        android:id="@+id/sheet_location"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Sted"
        android:textSize="16sp"
        android:textColor="#333"
        android:layout_marginBottom="16dp"
        android:visibility="gone"/>

    <TextView
        android:id="@+id/sheet_desc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Beskrivelse..."
        android:textSize="14sp"
        android:textColor="#555"
        android:layout_marginBottom="24dp"/>

    <!-- ADMIN KNAPPER (Vises kun for admin) -->
    <LinearLayout
        android:id="@+id/layout_admin_buttons"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:visibility="gone"
        android:layout_marginTop="16dp">

        <Button
            android:id="@+id/btn_delete"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Slett"
            android:backgroundTint="#D32F2F"
            android:textColor="#FFF"
            android:layout_marginEnd="8dp"/>

        <Button
            android:id="@+id/btn_edit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Endre"
            android:backgroundTint="@color/kbs_logo_blue"
            android:textColor="#FFF"
            android:layout_marginStart="8dp"/>
    </LinearLayout>

    <!-- Gammel knapp (skjult) -->
    <Button
        android:id="@+id/btn_add_to_calendar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="gone"/>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\dialog_custom_recurrence.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="24dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Egendefinert gjentakelse"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="#333"
            android:layout_marginBottom="24dp"/>

        <!-- FREKVENS -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:layout_marginBottom="24dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Gjenta hvert: "
                android:textSize="16sp"/>

            <EditText
                android:id="@+id/et_interval"
                android:layout_width="60dp"
                android:layout_height="wrap_content"
                android:inputType="number"
                android:text="1"
                android:gravity="center"
                android:layout_marginHorizontal="8dp"/>

            <Spinner
                android:id="@+id/spinner_freq"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:entries="@array/recurrence_freq_array"/>
        </LinearLayout>

        <!-- UKEDAGER (Vises kun hvis Uke er valgt) -->
        <LinearLayout
            android:id="@+id/layout_weekdays"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginBottom="24dp"
            android:visibility="visible">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Gjenta på"
                android:layout_marginBottom="8dp"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:weightSum="7">

                <ToggleButton android:id="@+id/tg_mon" android:textOn="M" android:textOff="M" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_tue" android:textOn="T" android:textOff="T" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_wed" android:textOn="O" android:textOff="O" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_thu" android:textOn="T" android:textOff="T" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_fri" android:textOn="F" android:textOff="F" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_sat" android:textOn="L" android:textOff="L" style="@style/DayToggle"/>
                <ToggleButton android:id="@+id/tg_sun" android:textOn="S" android:textOff="S" style="@style/DayToggle"/>
            </LinearLayout>
        </LinearLayout>

        <!-- SLUTTER -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Slutter"
            android:layout_marginBottom="8dp"/>

        <RadioGroup
            android:id="@+id/rg_end"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <RadioButton
                android:id="@+id/rb_never"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Aldri"
                android:checked="true"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:gravity="center_vertical">

                <RadioButton
                    android:id="@+id/rb_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Den"/>

                <Button
                    android:id="@+id/btn_end_date_picker"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Velg dato"
                    android:layout_marginStart="16dp"
                    style="@style/Widget.MaterialComponents.Button.TextButton"/>
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:gravity="center_vertical">

                <RadioButton
                    android:id="@+id/rb_count"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Etter"/>

                <EditText
                    android:id="@+id/et_count"
                    android:layout_width="60dp"
                    android:layout_height="wrap_content"
                    android:inputType="number"
                    android:text="13"
                    android:gravity="center"
                    android:layout_marginHorizontal="16dp"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="ganger"/>
            </LinearLayout>
        </RadioGroup>

        <!-- KNAPPER -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="end"
            android:layout_marginTop="32dp">

            <Button
                android:id="@+id/btn_cancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Avbryt"
                style="@style/Widget.MaterialComponents.Button.TextButton"
                android:textColor="#666"/>

            <Button
                android:id="@+id/btn_done"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Ferdig"
                android:backgroundTint="@color/kbs_logo_blue"
                android:textColor="#FFF"
                android:layout_marginStart="16dp"/>
        </LinearLayout>

    </LinearLayout>
</ScrollView>

============================================================
FILSTI: app\src\main\res\layout\fragment_calendar_full.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/kbs_very_light_blue">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:background="@color/white"
        android:elevation="4dp">

        <ImageView
            android:id="@+id/btn_back_calendar"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:src="@android:drawable/ic_menu_revert"
            android:layout_centerVertical="true"
            app:tint="@color/kbs_logo_blue"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="KBS Kalender"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="@color/black"
            android:layout_centerInParent="true"/>
    </RelativeLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_full_calendar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="8dp"
            android:clipToPadding="false"/>

        <ProgressBar
            android:id="@+id/loading_full_calendar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"/>

        <TextView
            android:id="@+id/empty_view_calendar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ingen hendelser funnet"
            android:layout_gravity="center"
            android:visibility="gone"/>
    </FrameLayout>
</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_create_event.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Opprett ny hendelse"
            android:textSize="24sp"
            android:textStyle="bold"
            android:layout_marginBottom="24dp"
            android:textColor="#333"/>

        <!-- TITTEL -->
        <EditText
            android:id="@+id/et_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Tittel"
            android:inputType="textCapSentences"
            android:padding="12dp"
            android:background="@android:drawable/edit_text"
            android:layout_marginBottom="16dp"/>

        <!-- BESKRIVELSE -->
        <EditText
            android:id="@+id/et_desc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Beskrivelse / Notater"
            android:inputType="textMultiLine"
            android:minLines="3"
            android:padding="12dp"
            android:gravity="top"
            android:background="@android:drawable/edit_text"
            android:layout_marginBottom="16dp"/>

        <!-- STED -->
        <EditText
            android:id="@+id/et_location"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Hvor (Sted)"
            android:inputType="textCapWords"
            android:padding="12dp"
            android:background="@android:drawable/edit_text"
            android:layout_marginBottom="16dp"/>

        <!-- KALENDER VALG -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Velg Kalender:"
            android:textSize="14sp"
            android:textColor="#666"/>

        <Spinner
            android:id="@+id/spinner_calendar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:padding="12dp"/>

        <!-- HELE DAGEN -->
        <Switch
            android:id="@+id/switch_all_day"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Hele dagen"
            android:layout_marginBottom="16dp"/>

        <!-- START DATO/TID -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="8dp">

            <Button
                android:id="@+id/btn_start_date"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Startdato"
                android:layout_marginEnd="4dp"/>

            <Button
                android:id="@+id/btn_start_time"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Starttid"
                android:layout_marginStart="4dp"/>
        </LinearLayout>

        <!-- SLUTT DATO/TID -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="16dp">

            <Button
                android:id="@+id/btn_end_date"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Sluttdato"
                android:layout_marginEnd="4dp"/>

            <Button
                android:id="@+id/btn_end_time"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Slutttid"
                android:layout_marginStart="4dp"/>
        </LinearLayout>

        <TextView
            android:id="@+id/txt_time_preview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Valgt: -"
            android:gravity="center"
            android:textStyle="bold"
            android:layout_marginBottom="24dp"/>

        <!-- GJENTAKELSE -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Gjentakelse:"
            android:textSize="14sp"
            android:textColor="#666"/>

        <Spinner
            android:id="@+id/spinner_recurrence"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:padding="12dp"/>

        <!-- NYTT: VARSLING MED CHIPS (MULTIVALG) -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Varsling (Velg en eller flere):"
            android:textSize="14sp"
            android:textColor="#666"
            android:layout_marginBottom="8dp"/>

        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="none"
            android:layout_marginBottom="32dp">

            <com.google.android.material.chip.ChipGroup
                android:id="@+id/chip_group_reminders"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:singleLine="true"
                app:selectionRequired="false">
                <!-- Chips legges til programmatisk i Java -->
            </com.google.android.material.chip.ChipGroup>

        </HorizontalScrollView>

        <Button
            android:id="@+id/btn_save_event"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Lagre i Kalender"
            android:backgroundTint="@color/kbs_logo_blue"
            android:textColor="#FFFFFF"/>

    </LinearLayout>

</ScrollView>

============================================================
FILSTI: app\src\main\res\layout\fragment_forms.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#F5F5F5"
    tools:context=".FormsFragment">

    <LinearLayout
        android:id="@+id/history_wrapper"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:orientation="vertical"
        android:background="#FFFFFF"
        android:elevation="4dp"
        android:layout_marginBottom="8dp"
        android:padding="16dp">

        <TextView
            android:id="@+id/lbl_history"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Tidligere innsendinger"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="#333333"
            android:layout_marginBottom="10dp"/>

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/historyContainer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" />
        </ScrollView>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="7"
        android:orientation="vertical"
        android:background="#FFFFFF"
        android:elevation="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:padding="8dp"
            android:background="#FAFAFA">

            <ProgressBar
                android:id="@+id/loading_spinner"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:visibility="gone"
                android:layout_marginEnd="8dp"/>

            <TextView
                android:id="@+id/txt_status"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:textColor="#666666"
                android:textSize="12sp"
                android:text="" />

            <ImageView
                android:id="@+id/btn_toggle_history"
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:src="@android:drawable/arrow_up_float"
                android:background="?attr/selectableItemBackgroundBorderless"
                android:padding="4dp"
                android:contentDescription="Vis/Skjul historikk"
                app:tint="#666666" />
        </LinearLayout>

        <ScrollView
            android:id="@+id/form_scroll_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="16dp"
            android:fillViewport="true">

            <LinearLayout
                android:id="@+id/form_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:paddingBottom="40dp">
            </LinearLayout>
        </ScrollView>
    </LinearLayout>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_forms_list.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:background="#F5F5F5">

    <TextView
        android:id="@+id/header_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Skjemaer"
        android:textSize="24sp"
        android:textStyle="bold"
        android:layout_marginBottom="20dp"
        android:textColor="#333333"/>

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/forms_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" />
        </ScrollView>

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_handbook.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#F5F5F5">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp"
        android:background="@color/white"
        android:elevation="4dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Håndbok"
            android:textSize="24sp"
            android:textStyle="bold"
            android:textColor="#333333"
            android:layout_marginBottom="12dp"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="KBS Klima og Byggservice ønsker å ta vare på miljø og helse, og har derfor utarbeidet en håndbok som omhandler disse temaene. For enkelthets skyld er denne publisert i KBS-appen og på Intranettet.\n\nDet forventes at alle ansatte skal ha lest hele håndboken minst én gang, og at de har lest og forstått bedriftens HMS-målsetting senest 1. april hvert år."
            android:textSize="14sp"
            android:textColor="#666666"
            android:lineSpacingExtra="4dp"
            android:layout_marginBottom="16dp"/>

        <EditText
            android:id="@+id/search_field"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:background="@drawable/bg_category_unselected"
            android:hint="Søk i håndboken..."
            android:paddingHorizontal="16dp"
            android:drawableStart="@android:drawable/ic_menu_search"
            android:drawablePadding="8dp"
            android:textColor="#333"
            android:textSize="14sp"
            android:inputType="text"/>
    </LinearLayout>

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="32dp"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_handbook"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp"
        android:clipToPadding="false"/>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_handbook_detail.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/white">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/detail_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/kbs_very_light_blue"
        android:elevation="4dp"
        app:navigationIcon="@android:drawable/ic_menu_revert"
        app:titleTextColor="@color/black" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <WebView
            android:id="@+id/detail_webview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />

        <ProgressBar
            android:id="@+id/detail_loading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:visibility="gone"/>

    </RelativeLayout>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_home.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="8dp"
    android:background="@color/kbs_very_light_blue">

    <!-- OVERSKRIFT OG PROFIL -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:paddingHorizontal="8dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="KBS Intranett"
            android:textSize="24sp"
            android:textStyle="bold"
            android:textColor="@color/kbs_muted_blue_gray"
            android:layout_centerVertical="true"/>

        <ImageView
            android:id="@+id/btn_profile"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@android:drawable/ic_menu_my_calendar"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:layout_alignParentEnd="true"
            android:layout_centerVertical="true"
            app:tint="@color/kbs_logo_blue"/>
    </RelativeLayout>

    <!-- NYTT: KNAPP FOR Å OPPRETTE HENDELSE -->
    <!-- Denne var borte i din fil. Den settes til visibility="gone" som standard,
         og skrus på av Java-koden hvis brukeren er admin. -->
    <Button
        android:id="@+id/btn_create_event"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="+ Ny Kalenderhendelse"
        android:backgroundTint="@color/kbs_logo_blue"
        android:textColor="#FFFFFF"
        android:layout_marginHorizontal="8dp"
        android:layout_marginBottom="12dp"
        android:visibility="gone"/>

    <!-- KALENDER-SEKSJON -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_marginBottom="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Kommende hendelser"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="@color/black"/>

        <TextView
            android:id="@+id/btn_view_all_calendar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Se alle >"
            android:textColor="@color/kbs_logo_blue"
            android:textStyle="bold"
            android:padding="8dp"
            android:background="?attr/selectableItemBackground"/>
    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_calendar"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:scrollbars="vertical"
        android:layout_marginBottom="16dp"/>

    <!-- NYHETER-SEKSJON -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_marginBottom="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Siste nytt"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="@color/black"/>

        <TextView
            android:id="@+id/btn_view_all_news"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Se alle >"
            android:textColor="@color/kbs_logo_blue"
            android:textStyle="bold"
            android:padding="8dp"
            android:background="?attr/selectableItemBackground"/>
    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_news"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:scrollbars="vertical"/>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_login.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:padding="32dp"
    android:background="#FFFFFF">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="KBS Intranett"
        android:textSize="28sp"
        android:textStyle="bold"
        android:textColor="#333333"
        android:layout_marginBottom="8dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Vennligst logg inn"
        android:textSize="16sp"
        android:textColor="#666666"
        android:layout_marginBottom="48dp"/>

    <com.google.android.gms.common.SignInButton
        android:id="@+id/sign_in_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/status_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:gravity="center"
        android:text=""
        android:textColor="#D32F2F"/>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_news_detail.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="250dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:contentScrim="@color/kbs_logo_blue">

            <ImageView
                android:id="@+id/detail_image"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax" />

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/detail_toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:navigationIcon="@android:drawable/ic_menu_revert" />

        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <TextView
                android:id="@+id/detail_category"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@color/kbs_logo_accent_red"
                android:textStyle="bold"
                android:textAllCaps="true"
                android:textSize="12sp"
                android:layout_marginBottom="8dp"/>

            <TextView
                android:id="@+id/detail_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="24sp"
                android:textStyle="bold"
                android:textColor="@android:color/black"
                android:layout_marginBottom="8dp"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:layout_marginBottom="24dp">

                <TextView
                    android:id="@+id/detail_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#888888"
                    android:textSize="12sp"
                    android:layout_marginEnd="16dp"/>

                <TextView
                    android:id="@+id/detail_author"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#888888"
                    android:textStyle="italic"
                    android:textSize="12sp"
                    android:text="Av: Forfatter"/>
            </LinearLayout>

            <TextView
                android:id="@+id/detail_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="#333333"
                android:textSize="16sp"
                android:lineSpacingExtra="4dp"/>

        </LinearLayout>
    </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_news_full.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/kbs_very_light_blue">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:background="@android:color/white"
        android:elevation="4dp">

        <ImageView
            android:id="@+id/btn_back_news"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:src="@android:drawable/ic_menu_revert"
            android:layout_centerVertical="true"
            android:contentDescription="Tilbake"
            android:background="?attr/selectableItemBackgroundBorderless"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Siste nytt"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="@android:color/black"
            android:layout_centerInParent="true"/>
    </RelativeLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_categories"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="8dp"
        android:clipToPadding="false"
        android:scrollbars="none"/>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_news_full"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingTop="8dp"
            android:paddingHorizontal="4dp"
            android:clipToPadding="false"/>

        <ProgressBar
            android:id="@+id/loading_news_full"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"/>
    </FrameLayout>
</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\fragment_profile.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:background="@color/kbs_very_light_blue">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:paddingHorizontal="10dp"
        android:background="@color/kbs_logo_blue" >

        <ImageView
            android:id="@+id/btn_close_profile"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@android:drawable/ic_menu_close_clear_cancel"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:padding="4dp"
            android:layout_alignParentStart="true"
            android:layout_centerVertical="true"
            app:tint="@color/white"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Min Profil"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="@color/white"
            android:layout_centerInParent="true"/>
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="center_horizontal"
        android:paddingTop="32dp">

        <ImageView
            android:id="@+id/profile_image"
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:src="@android:drawable/sym_def_app_icon"
            android:background="@android:color/transparent"
            android:layout_marginBottom="24dp"/>

        <TextView
            android:id="@+id/profile_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Laster navn..."
            android:textSize="24sp"
            android:textStyle="bold"
            android:textColor="@color/kbs_muted_blue_gray"/>

        <TextView
            android:id="@+id/profile_email"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="..."
            android:textSize="16sp"
            android:textColor="@color/kbs_muted_blue_gray"
            android:layout_marginBottom="8dp"/>

        <TextView
            android:id="@+id/profile_role"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Rolle: ..."
            android:textSize="14sp"
            android:textStyle="italic"
            android:textColor="@color/kbs_logo_blue"
            android:background="@color/kbs_very_light_blue"
            android:paddingHorizontal="12dp"
            android:paddingVertical="4dp"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="32dp"/>

        <Button
            android:id="@+id/btn_update_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Oppdater mine opplysninger"
            android:backgroundTint="@color/white"
            android:textColor="@color/kbs_logo_blue"
            android:layout_marginBottom="16dp"/>

        <Button
            android:id="@+id/btn_logout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Logg ut"
            android:backgroundTint="@color/kbs_logo_accent_red"
            android:textColor="@color/white"/>

    </LinearLayout>

</LinearLayout>

============================================================
FILSTI: app\src\main\res\layout\item_calendar.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    android:layout_marginHorizontal="8dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="12dp"
        android:gravity="center_vertical">

        <LinearLayout
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:orientation="vertical"
            android:gravity="center"
            android:background="@color/kbs_logo_light_blue"  android:layout_marginEnd="16dp">

            <TextView
                android:id="@+id/cal_day"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="09"
                android:textSize="20sp"
                android:textStyle="bold"
                android:textColor="@color/white"/>

            <TextView
                android:id="@+id/cal_month"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="DES"
                android:textSize="10sp"
                android:textAllCaps="true"
                android:textColor="@color/white"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">

            <TextView
                android:id="@+id/cal_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Møtetittel"
                android:textSize="16sp"
                android:textStyle="bold"
                android:textColor="@color/kbs_muted_blue_gray"/>

            <TextView
                android:id="@+id/cal_time"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Kl. 10:00 - 11:00"
                android:textSize="12sp"
                android:textColor="@android:color/darker_gray"/>
        </LinearLayout>

    </LinearLayout>
</androidx.cardview.widget.CardView>

============================================================
FILSTI: app\src\main\res\layout\item_category.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/category_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Kategori"
    android:paddingHorizontal="16dp"
    android:paddingVertical="8dp"
    android:layout_marginEnd="8dp"
    android:background="@drawable/bg_category_unselected"
    android:textColor="#333333"
    android:textSize="14sp"
    android:textStyle="bold" />

============================================================
FILSTI: app\src\main\res\layout\item_handbook.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="6dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp"
    app:cardBackgroundColor="@color/white">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp"
        android:gravity="center_horizontal">

        <ImageView
            android:id="@+id/icon"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@drawable/ic_handbook_general"
            app:tint="@color/kbs_logo_blue"
            android:layout_marginBottom="12dp"/>

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Tittel"
            android:textStyle="bold"
            android:textColor="#333333"
            android:textSize="14sp"
            android:gravity="center"
            android:layout_marginBottom="4dp"/>

        <TextView
            android:id="@+id/desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Beskrivelse..."
            android:textColor="#666666"
            android:textSize="12sp"
            android:gravity="center"
            android:maxLines="2"
            android:ellipsize="end"/>

    </LinearLayout>
</androidx.cardview.widget.CardView>

============================================================
FILSTI: app\src\main\res\layout\item_news.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="24dp"
    android:layout_marginHorizontal="8dp"
    app:cardCornerRadius="12dp"
    app:cardElevation="4dp"
    app:cardBackgroundColor="@android:color/white">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/news_image"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:scaleType="centerCrop"
            android:src="@android:drawable/ic_menu_gallery"
            android:background="#EEEEEE" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <TextView
                android:id="@+id/news_category"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="KATEGORI"
                android:textSize="12sp"
                android:textStyle="bold"
                android:textColor="@color/kbs_logo_accent_red"
                android:textAllCaps="true"
                android:letterSpacing="0.05"
                android:layout_marginBottom="4dp"/>

            <TextView
                android:id="@+id/news_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Overskrift på nyhetssaken kommer her"
                android:textSize="20sp"
                android:textStyle="bold"
                android:textColor="@android:color/black"
                android:layout_marginBottom="8dp"
                android:lineSpacingExtra="2dp"/>

            <TextView
                android:id="@+id/news_excerpt"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Her kommer en kort ingress som beskriver saken litt nærmere før man klikker seg inn..."
                android:textColor="#555555"
                android:textSize="14sp"
                android:maxLines="3"
                android:ellipsize="end"
                android:layout_marginBottom="12dp"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:gravity="center_vertical">

                <ImageView
                    android:layout_width="14dp"
                    android:layout_height="14dp"
                    android:src="@android:drawable/ic_menu_my_calendar"
                    app:tint="#999999"
                    android:layout_marginEnd="6dp"/>

                <TextView
                    android:id="@+id/news_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="23. Nov 2025"
                    android:textSize="12sp"
                    android:textColor="#999999"/>
            </LinearLayout>

        </LinearLayout>
    </LinearLayout>
</androidx.cardview.widget.CardView>

============================================================
FILSTI: app\src\main\res\menu\bottom_nav_menu.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_home"
        android:icon="@android:drawable/ic_menu_today"
        android:title="Hjem" />

    <item
        android:id="@+id/navigation_forms"
        android:icon="@android:drawable/ic_menu_edit"
        android:title="Skjema" />

    <item
        android:id="@+id/navigation_handbook"
        android:icon="@android:drawable/ic_menu_info_details"
        android:title="Håndbok" />
</menu>

============================================================
FILSTI: app\src\main\res\mipmap-anydpi-v26\ic_launcher.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@color/ic_launcher_background"/>
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

============================================================
FILSTI: app\src\main\res\mipmap-anydpi-v26\ic_launcher_round.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@color/ic_launcher_background"/>
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

============================================================
FILSTI: app\src\main\res\navigation\mobile_navigation.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <fragment
        android:id="@+id/navigation_login"
        android:name="com.kbs.kbsintranett.LoginFragment"
        android:label="Logg inn"
        tools:layout="@layout/fragment_login">
        <action
            android:id="@+id/action_login_to_home"
            app:destination="@id/navigation_home"
            app:popUpTo="@id/navigation_home"
            app:popUpToInclusive="true" />
    </fragment>

    <fragment
        android:id="@+id/navigation_home"
        android:name="com.kbs.kbsintranett.HomeFragment"
        android:label="Hjem"
        tools:layout="@layout/fragment_home">
        <action
            android:id="@+id/action_home_to_calendarFull"
            app:destination="@id/navigation_calendar_full" />
        <action
            android:id="@+id/action_home_to_newsFull"
            app:destination="@id/navigation_news_full" />
        <action
            android:id="@+id/action_home_to_newsDetail"
            app:destination="@id/navigation_news_detail" />
        <!-- HER ER DEN MANGLENDE LINKEN: -->
        <action
            android:id="@+id/action_home_to_create_event"
            app:destination="@id/navigation_create_event" />
    </fragment>

    <!-- NYTT FRAGMENT: Opprett hendelse -->
    <fragment
        android:id="@+id/navigation_create_event"
        android:name="com.kbs.kbsintranett.CreateEventFragment"
        android:label="Ny Hendelse"
        tools:layout="@layout/fragment_create_event" />

    <fragment
        android:id="@+id/navigation_calendar_full"
        android:name="com.kbs.kbsintranett.CalendarFullFragment"
        android:label="Kalender"
        tools:layout="@layout/fragment_calendar_full" />

    <fragment
        android:id="@+id/navigation_news_full"
        android:name="com.kbs.kbsintranett.NewsFullFragment"
        android:label="Nyheter"
        tools:layout="@layout/fragment_news_full">
        <action
            android:id="@+id/action_newsFull_to_newsDetail"
            app:destination="@id/navigation_news_detail" />
    </fragment>

    <fragment
        android:id="@+id/navigation_news_detail"
        android:name="com.kbs.kbsintranett.NewsDetailFragment"
        android:label="Nyhet"
        tools:layout="@layout/fragment_news_detail" />

    <fragment
        android:id="@+id/navigation_forms"
        android:name="com.kbs.kbsintranett.FormsListFragment"
        android:label="Skjemaer"
        tools:layout="@layout/fragment_forms_list">
        <action
            android:id="@+id/action_formsListFragment_to_formsDetailFragment"
            app:destination="@id/navigation_forms_detail" />
    </fragment>

    <fragment
        android:id="@+id/navigation_forms_detail"
        android:name="com.kbs.kbsintranett.FormsFragment"
        android:label="Fyll ut skjema"
        tools:layout="@layout/fragment_forms">
        <argument
            android:name="formId"
            app:argType="integer"
            android:defaultValue="0" />
    </fragment>

    <fragment
        android:id="@+id/navigation_handbook"
        android:name="com.kbs.kbsintranett.HandbookFragment"
        android:label="Håndbok"
        tools:layout="@layout/fragment_handbook">
        <action
            android:id="@+id/action_handbook_to_detail"
            app:destination="@id/navigation_handbook_detail" />
    </fragment>

    <fragment
        android:id="@+id/navigation_handbook_detail"
        android:name="com.kbs.kbsintranett.HandbookDetailFragment"
        android:label="Håndbok Detaljer"
        tools:layout="@layout/fragment_handbook_detail">
        <argument
            android:name="page_id"
            app:argType="integer" />
        <argument
            android:name="page_title"
            app:argType="string" />

        <action
            android:id="@+id/action_handbook_to_detail"
            app:destination="@id/navigation_handbook_detail" />

        <action
            android:id="@+id/action_handbook_to_form"
            app:destination="@id/navigation_forms_detail" />
    </fragment>

    <fragment
        android:id="@+id/navigation_profile"
        android:name="com.kbs.kbsintranett.ProfileFragment"
        android:label="Min Profil"
        tools:layout="@layout/fragment_profile">
        <action
            android:id="@+id/action_profile_to_login"
            app:destination="@id/navigation_login"
            app:popUpTo="@id/navigation_home"
            app:popUpToInclusive="true" />
        <action
            android:id="@+id/action_profile_to_form"
            app:destination="@id/navigation_forms_detail" />
    </fragment>

</navigation>

============================================================
FILSTI: app\src\main\res\values\colors.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>

    <color name="kbs_logo_blue">#0069B3</color>
    <color name="kbs_logo_light_blue">#53AFE9</color>
    <color name="kbs_logo_accent_red">#C40426</color>

    <color name="kbs_very_light_blue">#F5F7FA</color>
    <color name="kbs_muted_blue_gray">#4F5B66</color>
    <color name="kbs_soft_light_pink_beige">#F8E5E8</color>
</resources>

============================================================
FILSTI: app\src\main\res\values\ic_launcher_background.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="ic_launcher_background">#FFFFFF</color>
</resources>

============================================================
FILSTI: app\src\main\res\values\strings.xml
============================================================
<resources>
    <string name="app_name">KBS Intranett</string>
    <!-- NYTT: Array for gjentakelse-spinneren -->
    <string-array name="recurrence_freq_array">
        <item>dag</item>
        <item>uke</item>
        <item>måned</item>
        <item>år</item>
    </string-array>
</resources>

============================================================
FILSTI: app\src\main\res\values\themes.xml
============================================================
<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Base.Theme.KBSIntranett" parent="Theme.Material3.Light.NoActionBar">
        <item name="colorPrimary">#0056b3</item> <item name="colorPrimaryVariant">#004494</item>
        <item name="colorOnPrimary">#FFFFFF</item>

        <item name="android:windowLightStatusBar">true</item>
    </style>

    <style name="Theme.KBSIntranett" parent="Base.Theme.KBSIntranett" />

    <!-- NYTT: Stil for runde ukedag-knapper (M T O T F L S) -->
    <style name="DayToggle">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">40dp</item>
        <item name="android:layout_weight">1</item>
        <item name="android:background">@drawable/selector_day_toggle</item>
        <item name="android:textColor">@color/selector_day_text</item>
        <item name="android:textOff">M</item>
        <item name="android:textOn">M</item>
        <item name="android:textSize">12sp</item>
        <item name="android:layout_margin">2dp</item>
    </style>
</resources>

============================================================
FILSTI: app\src\main\res\values-night\themes.xml
============================================================
<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Base.Theme.KBSIntranett" parent="Theme.Material3.Light.NoActionBar">
        <item name="colorPrimary">#0056b3</item> <item name="colorPrimaryVariant">#004494</item>
        <item name="colorOnPrimary">#FFFFFF</item>

        <item name="android:windowLightStatusBar">true</item>
    </style>

    <style name="Theme.KBSIntranett" parent="Base.Theme.KBSIntranett" />
</resources>

============================================================
FILSTI: app\src\main\res\xml\backup_rules.xml
============================================================
<?xml version="1.0" encoding="utf-8"?><!--
   Sample backup rules file; uncomment and customize as necessary.
   See https://developer.android.com/guide/topics/data/autobackup
   for details.
   Note: This file is ignored for devices older than API 31
   See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
    <!--
   <include domain="sharedpref" path="."/>
   <exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

============================================================
FILSTI: app\src\main\res\xml\data_extraction_rules.xml
============================================================
<?xml version="1.0" encoding="utf-8"?><!--
   Sample data extraction rules file; uncomment and customize as necessary.
   See https://developer.android.com/about/versions/12/backup-restore#xml-changes
   for details.
-->
<data-extraction-rules>
    <cloud-backup>
        <!-- TODO: Use <include> and <exclude> to control what is backed up.
        <include .../>
        <exclude .../>
        -->
    </cloud-backup>
    <!--
    <device-transfer>
        <include .../>
        <exclude .../>
    </device-transfer>
    -->
</data-extraction-rules>

============================================================
FILSTI: app\src\main\res\xml\file_paths.xml
============================================================
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-files-path name="my_images" path="Pictures" />
</paths>

============================================================
FILSTI: app\src\test\java\com\kbs\kbsintranett\ExampleUnitTest.java
============================================================
package com.kbs.kbsintranett;

import org.junit.Test;

import static org.junit.Assert.*;

/**
 * Example local unit test, which will execute on the development machine (host).
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() {
        assertEquals(4, 2 + 2);
    }
}
