Før refreshing på forsiden

This commit is contained in:
ErolHaagenrud 2025-12-18 10:21:43 +01:00
parent 530a5a3d16
commit d3b9504f9a
15 changed files with 530 additions and 202 deletions

View file

@ -1,5 +1,7 @@
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
// NY LINJE: Aktiver Google Services plugin her
id("com.google.gms.google-services")
} }
android { android {
@ -10,8 +12,8 @@ android {
applicationId = "com.kbs.kbsintranett" applicationId = "com.kbs.kbsintranett"
minSdk = 28 minSdk = 28
targetSdk = 34 targetSdk = 34
versionCode = 1 versionCode = 2
versionName = "1.0" versionName = "1.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@ -63,4 +65,10 @@ dependencies {
// Swipe Refresh Layout // Swipe Refresh Layout
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
}
// NY LINJE: Firebase BOM (Bill of Materials) styrer versjoner
implementation(platform("com.google.firebase:firebase-bom:33.1.2"))
// NY LINJE: (Valgfritt, men lurt for statistikk)
implementation("com.google.firebase:firebase-analytics")
}

View file

@ -15,23 +15,31 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.navigation.Navigation;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment { public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
private CalendarEvent event; private CalendarEvent event;
private OnEventChangeListener changeListener;
// Interface for å varsle foreldre-fragmentet om endringer
public interface OnEventChangeListener {
void onEventChanged();
}
public CalendarDetailsBottomSheet(CalendarEvent event) { public CalendarDetailsBottomSheet(CalendarEvent event) {
this.event = event; this.event = event;
} }
public void setOnEventChangeListener(OnEventChangeListener listener) {
this.changeListener = listener;
}
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@ -41,7 +49,7 @@ public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
TextView time = view.findViewById(R.id.sheet_time); TextView time = view.findViewById(R.id.sheet_time);
TextView desc = view.findViewById(R.id.sheet_desc); TextView desc = view.findViewById(R.id.sheet_desc);
TextView loc = view.findViewById(R.id.sheet_location); TextView loc = view.findViewById(R.id.sheet_location);
TextView calName = view.findViewById(R.id.sheet_calendar_name); // NYTT TextView calName = view.findViewById(R.id.sheet_calendar_name);
LinearLayout adminLayout = view.findViewById(R.id.layout_admin_buttons); LinearLayout adminLayout = view.findViewById(R.id.layout_admin_buttons);
Button btnDelete = view.findViewById(R.id.btn_delete); Button btnDelete = view.findViewById(R.id.btn_delete);
@ -50,7 +58,6 @@ public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
title.setText(event.getTitle()); title.setText(event.getTitle());
time.setText(event.getTime() + " (" + event.getDay() + ". " + event.getMonth() + ")"); time.setText(event.getTime() + " (" + event.getDay() + ". " + event.getMonth() + ")");
// NYTT: Sett navn og farge
calName.setText(event.getCalendarName().toUpperCase()); calName.setText(event.getCalendarName().toUpperCase());
try { try {
int color = Color.parseColor(event.getCalendarColor()); int color = Color.parseColor(event.getCalendarColor());
@ -75,13 +82,6 @@ public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
// Sjekk admin-rettigheter // Sjekk admin-rettigheter
if (UserManager.getInstance().isEditorOrAbove()) { if (UserManager.getInstance().isEditorOrAbove()) {
// MERK: Selv om man er admin, bør man kanskje bare kunne slette/endre events fra kalendere
// man faktisk har skrivetilgang til i konfigurasjonen.
// Men siden admin har tilgang til alt i PHP, viser vi knappene.
// Hvis brukeren er vanlig ansatt (men ikke i f.eks service), vil skrivetilgang sjekkes
// mot 'writeable_calendars' eller ved API-kall.
// Enklest her: Vi viser knappene hvis brukeren KAN skrive til denne kalenderen.
boolean canEdit = UserManager.getInstance().getWriteableCalendars().contains(event.getCalendarName()) boolean canEdit = UserManager.getInstance().getWriteableCalendars().contains(event.getCalendarName())
|| UserManager.getInstance().isAdmin(); || UserManager.getInstance().isAdmin();
@ -122,6 +122,12 @@ public class CalendarDetailsBottomSheet extends BottomSheetDialogFragment {
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) { public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
Toast.makeText(getContext(), "Slettet!", Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Slettet!", Toast.LENGTH_SHORT).show();
// VARSLE LISTEN OM AT NOE ER SLETTET
if (changeListener != null) {
changeListener.onEventChanged();
}
dismiss(); dismiss();
} else { } else {
Toast.makeText(getContext(), "Kunne ikke slette", Toast.LENGTH_LONG).show(); Toast.makeText(getContext(), "Kunne ikke slette", Toast.LENGTH_LONG).show();

View file

@ -1,6 +1,8 @@
package com.kbs.kbsintranett; package com.kbs.kbsintranett;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -46,16 +48,25 @@ public class CalendarFullFragment extends Fragment {
layoutManager = new LinearLayoutManager(getContext()); layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
backBtn.setOnClickListener(v -> Navigation.findNavController(view).navigateUp()); backBtn.setOnClickListener(v -> Navigation.findNavController(view).navigateUp());
}
@Override
public void onResume() {
super.onResume();
fetchAllEvents(); fetchAllEvents();
} }
private void fetchAllEvents() { private void fetchAllEvents() {
progressBar.setVisibility(View.VISIBLE); 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) new Thread(() -> {
// HER ER ENDRINGEN: isPreview = false (Hent alt)
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), false);
new Handler(Looper.getMainLooper()).post(() -> fetchApiEvents(deviceEvents));
}).start();
}
private void fetchApiEvents(List<CalendarEvent> deviceEvents) {
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() { RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
@Override @Override
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) { public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
@ -65,7 +76,6 @@ public class CalendarFullFragment extends Fragment {
List<CalendarEvent> apiEvents = new ArrayList<>(); List<CalendarEvent> apiEvents = new ArrayList<>();
if (response.isSuccessful() && response.body() != null) { if (response.isSuccessful() && response.body() != null) {
apiEvents = response.body(); apiEvents = response.body();
// Formater data for visning
for (CalendarEvent e : apiEvents) { for (CalendarEvent e : apiEvents) {
CalendarManager.formatEventForUI(e); CalendarManager.formatEventForUI(e);
} }
@ -82,6 +92,7 @@ public class CalendarFullFragment extends Fragment {
CalendarAdapter adapter = new CalendarAdapter(allEvents, event -> { CalendarAdapter adapter = new CalendarAdapter(allEvents, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event); CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(CalendarFullFragment.this::fetchAllEvents);
sheet.show(getParentFragmentManager(), "CalendarDetails"); sheet.show(getParentFragmentManager(), "CalendarDetails");
}); });
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
@ -126,5 +137,4 @@ public class CalendarFullFragment extends Fragment {
layoutManager.scrollToPositionWithOffset(scrollIndex, 0); layoutManager.scrollToPositionWithOffset(scrollIndex, 0);
} }
} }
} }

View file

@ -9,8 +9,10 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
public class CalendarManager { public class CalendarManager {
@ -44,7 +46,13 @@ public class CalendarManager {
return ids; return ids;
} }
public static List<CalendarEvent> getDeviceEvents(Context context) { /**
* Henter hendelser fra enheten.
* @param context App Context
* @param isPreview Hvis true: Henter kun kommende måned (Raskt). Hvis false: Henter -1 til +6 mnd (Tregere).
* @return Liste med events
*/
public static List<CalendarEvent> getDeviceEvents(Context context, boolean isPreview) {
List<CalendarEvent> deviceEvents = new ArrayList<>(); List<CalendarEvent> deviceEvents = new ArrayList<>();
if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_CALENDAR) if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_CALENDAR)
!= PackageManager.PERMISSION_GRANTED) { != PackageManager.PERMISSION_GRANTED) {
@ -57,8 +65,20 @@ public class CalendarManager {
} }
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
long startMillis = now - (365L * 24 * 60 * 60 * 1000); long startMillis;
long endMillis = now + (365L * 24 * 60 * 60 * 1000); long endMillis;
if (isPreview) {
// FORSIDEN: Optimalisert for hastighet.
// Henter fra og 30 dager frem i tid.
startMillis = now;
endMillis = now + (30L * 24 * 60 * 60 * 1000);
} else {
// FULL VISNING: Bredere tidsvindu.
// 1 mnd tilbake til 6 mnd frem.
startMillis = now - (30L * 24 * 60 * 60 * 1000);
endMillis = now + (180L * 24 * 60 * 60 * 1000);
}
String[] projection = new String[]{ String[] projection = new String[]{
CalendarContract.Events.TITLE, CalendarContract.Events.TITLE,
@ -120,8 +140,7 @@ public class CalendarManager {
CalendarEvent event = new CalendarEvent(title, rawStart, rawEnd, desc, loc); CalendarEvent event = new CalendarEvent(title, rawStart, rawEnd, desc, loc);
event.setReminderMinutes(0); event.setReminderMinutes(0);
// NYTT V12.2: Sett standardfarge for lokale kalendere event.setCalendarColor("#888888");
event.setCalendarColor("#888888"); // Grå for lokale events
event.setCalendarName("Min Kalender (Lokal)"); event.setCalendarName("Min Kalender (Lokal)");
formatEventForUI(event); formatEventForUI(event);
@ -200,9 +219,24 @@ public class CalendarManager {
} }
public static List<CalendarEvent> mergeAndSort(List<CalendarEvent> apiEvents, List<CalendarEvent> deviceEvents) { public static List<CalendarEvent> mergeAndSort(List<CalendarEvent> apiEvents, List<CalendarEvent> deviceEvents) {
List<CalendarEvent> all = new ArrayList<>(apiEvents); List<CalendarEvent> all = new ArrayList<>();
all.addAll(deviceEvents); Set<String> uniqueKeys = new HashSet<>();
// 1. Legg til alle API-events først (Prioritert)
for (CalendarEvent apiEvent : apiEvents) {
all.add(apiEvent);
uniqueKeys.add(generateKey(apiEvent));
}
// 2. Legg til Device-events KUN hvis nøkkelen ikke finnes
for (CalendarEvent deviceEvent : deviceEvents) {
String key = generateKey(deviceEvent);
if (!uniqueKeys.contains(key)) {
all.add(deviceEvent);
}
}
// 3. Sorter alt kronologisk
Collections.sort(all, (e1, e2) -> { Collections.sort(all, (e1, e2) -> {
String d1 = e1.getRawDate() != null ? e1.getRawDate() : ""; String d1 = e1.getRawDate() != null ? e1.getRawDate() : "";
String d2 = e2.getRawDate() != null ? e2.getRawDate() : ""; String d2 = e2.getRawDate() != null ? e2.getRawDate() : "";
@ -212,4 +246,20 @@ public class CalendarManager {
return all; return all;
} }
private static String generateKey(CalendarEvent event) {
String title = event.getTitle() != null ? event.getTitle().toLowerCase().trim() : "";
String datePart = "";
if (event.getRawDate() != null) {
String digits = event.getRawDate().replaceAll("[^0-9]", "");
if (digits.length() >= 12) {
datePart = digits.substring(0, 12);
} else if (digits.length() >= 8) {
datePart = digits.substring(0, 8);
} else {
datePart = digits;
}
}
return title + "_" + datePart;
}
} }

View file

@ -139,6 +139,20 @@ public class CreateEventFragment extends Fragment {
etDesc.setText(cleanDesc); etDesc.setText(cleanDesc);
etLocation.setText(event.getLocation()); etLocation.setText(event.getLocation());
// --- FIKS 404 FEIL VED OPPDATERING ---
// 1. Sett spinneren til riktig kalender
ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinnerCalendar.getAdapter();
if (adapter != null) {
int position = adapter.getPosition(event.getCalendarName());
if (position >= 0) {
spinnerCalendar.setSelection(position);
}
}
// 2. Deaktiver spinneren. Man kan ikke bytte kalender ved oppdatering (API-begrensning).
// Man slette og opprette nytt for å flytte.
spinnerCalendar.setEnabled(false);
// -------------------------------------
// Dato-parsing // Dato-parsing
try { try {
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()); SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());

View file

@ -3,10 +3,13 @@ package com.kbs.kbsintranett;
import android.Manifest; import android.Manifest;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts; import androidx.activity.result.contract.ActivityResultContracts;
@ -33,6 +36,8 @@ import retrofit2.Response;
public class HomeFragment extends Fragment { public class HomeFragment extends Fragment {
private ActivityResultLauncher<String> requestPermissionLauncher; private ActivityResultLauncher<String> requestPermissionLauncher;
private RecyclerView calendarRecycler; private RecyclerView calendarRecycler;
private RecyclerView newsRecycler;
private ProgressBar mainProgressBar; // Variabelen som være her
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
@ -58,12 +63,16 @@ public class HomeFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
// Her leter vi etter IDen som finnes i XML-filen over
mainProgressBar = view.findViewById(R.id.main_loading_spinner);
if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE);
View profileBtn = view.findViewById(R.id.btn_profile); View profileBtn = view.findViewById(R.id.btn_profile);
if (profileBtn != null) { if (profileBtn != null) {
profileBtn.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.navigation_profile)); 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); Button btnCreateEvent = view.findViewById(R.id.btn_create_event);
List<String> writeable = UserManager.getInstance().getWriteableCalendars(); List<String> writeable = UserManager.getInstance().getWriteableCalendars();
@ -85,19 +94,13 @@ public class HomeFragment extends Fragment {
viewAllCalendar.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.action_home_to_calendarFull)); 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 (android.os.Build.VERSION.SDK_INT >= 33) {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {}).launch(Manifest.permission.POST_NOTIFICATIONS); registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {}).launch(Manifest.permission.POST_NOTIFICATIONS);
} }
} }
RecyclerView newsRecycler = view.findViewById(R.id.recycler_news); newsRecycler = view.findViewById(R.id.recycler_news);
newsRecycler.setLayoutManager(new LinearLayoutManager(getContext())); newsRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
newsRecycler.setNestedScrollingEnabled(false); newsRecycler.setNestedScrollingEnabled(false);
newsRecycler.setAdapter(new NewsAdapter(new ArrayList<>(), item -> {})); newsRecycler.setAdapter(new NewsAdapter(new ArrayList<>(), item -> {}));
@ -112,14 +115,32 @@ public class HomeFragment extends Fragment {
fetchNewsFromWordpress(newsRecycler); fetchNewsFromWordpress(newsRecycler);
} }
private void fetchCalendarEvents(RecyclerView recyclerView) { @Override
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext()); public void onResume() {
super.onResume();
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED) {
fetchCalendarEvents(calendarRecycler);
} else {
requestPermissionLauncher.launch(Manifest.permission.READ_CALENDAR);
}
}
private void fetchCalendarEvents(RecyclerView recyclerView) {
new Thread(() -> {
// isPreview = true for raskere lasting
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
new Handler(Looper.getMainLooper()).post(() -> fetchApiEvents(recyclerView, deviceEvents));
}).start();
}
private void fetchApiEvents(RecyclerView recyclerView, List<CalendarEvent> deviceEvents) {
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() { RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
@Override @Override
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) { public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
if (!isAdded()) return; if (!isAdded()) return;
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
List<CalendarEvent> apiEvents = new ArrayList<>(); List<CalendarEvent> apiEvents = new ArrayList<>();
if (response.isSuccessful() && response.body() != null) { if (response.isSuccessful() && response.body() != null) {
apiEvents = response.body(); apiEvents = response.body();
@ -145,6 +166,7 @@ public class HomeFragment extends Fragment {
recyclerView.setAdapter(new CalendarAdapter(top5, event -> { recyclerView.setAdapter(new CalendarAdapter(top5, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event); CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(() -> fetchCalendarEvents(recyclerView));
sheet.show(getParentFragmentManager(), "CalendarDetails"); sheet.show(getParentFragmentManager(), "CalendarDetails");
})); }));
} }
@ -152,6 +174,9 @@ public class HomeFragment extends Fragment {
@Override @Override
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) { public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
if (!isAdded()) return; if (!isAdded()) return;
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
if (!deviceEvents.isEmpty()) { if (!deviceEvents.isEmpty()) {
List<CalendarEvent> top5 = new ArrayList<>(); List<CalendarEvent> top5 = new ArrayList<>();
for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) top5.add(deviceEvents.get(i)); for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) top5.add(deviceEvents.get(i));
@ -160,9 +185,7 @@ public class HomeFragment extends Fragment {
sheet.show(getParentFragmentManager(), "CalendarDetails"); sheet.show(getParentFragmentManager(), "CalendarDetails");
})); }));
} else { } else {
List<CalendarEvent> errorList = new ArrayList<>(); recyclerView.setAdapter(new CalendarAdapter(new ArrayList<>(), null));
errorList.add(new CalendarEvent("Kunne ikke laste kalender", "Sjekk nettverk", "!", "OBS", ""));
recyclerView.setAdapter(new CalendarAdapter(errorList, null));
} }
} }
}); });

View file

@ -0,0 +1,90 @@
package com.kbs.kbsintranett;
import android.app.Dialog;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
public class ImageDialogFragment extends DialogFragment {
private static final String ARG_URL = "image_url";
public static ImageDialogFragment newInstance(String imageUrl) {
ImageDialogFragment fragment = new ImageDialogFragment();
Bundle args = new Bundle();
args.putString(ARG_URL, imageUrl);
fragment.setArguments(args);
return fragment;
}
@Override
public void onStart() {
super.onStart();
// Gjør dialogen fullskjerm
Dialog dialog = getDialog();
if (dialog != null) {
int width = ViewGroup.LayoutParams.MATCH_PARENT;
int height = ViewGroup.LayoutParams.MATCH_PARENT;
dialog.getWindow().setLayout(width, height);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_image_dialog, container, false);
ImageView imageView = view.findViewById(R.id.full_screen_image);
ImageButton closeBtn = view.findViewById(R.id.btn_close_image);
ProgressBar progressBar = view.findViewById(R.id.loading_image);
String url = getArguments() != null ? getArguments().getString(ARG_URL) : null;
if (url != null) {
Glide.with(this)
.load(url)
.transition(DrawableTransitionOptions.withCrossFade())
.listener(new com.bumptech.glide.request.RequestListener<android.graphics.drawable.Drawable>() {
@Override
public boolean onLoadFailed(@Nullable com.bumptech.glide.load.engine.GlideException e, Object model, com.bumptech.glide.request.target.Target<android.graphics.drawable.Drawable> target, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(android.graphics.drawable.Drawable resource, Object model, com.bumptech.glide.request.target.Target<android.graphics.drawable.Drawable> target, com.bumptech.glide.load.DataSource dataSource, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
})
.into(imageView);
}
closeBtn.setOnClickListener(v -> dismiss());
// Lukk også hvis man trykker selve bildet
imageView.setOnClickListener(v -> dismiss());
return view;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
}

View file

@ -36,14 +36,12 @@ import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity { 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 = "738325360287-cidl3plnqv9ei74vm9vm5muustj6eenb.apps.googleusercontent.com"; // Bytt med din egen hvis denne er feil
public static final String GOOGLE_WEB_CLIENT_ID = "738325360287-cidl3plnqv9ei74vm9vm5muustj6eenb.apps.googleusercontent.com";
private static final String TAG = "MainActivity"; private static final String TAG = "MainActivity";
private NavController navController; private NavController navController;
private BottomNavigationView bottomNav; private BottomNavigationView bottomNav;
// Launcher for å spørre om varslingstillatelse (Android 13+)
private ActivityResultLauncher<String> requestPermissionLauncher; private ActivityResultLauncher<String> requestPermissionLauncher;
@Override @Override
@ -52,7 +50,6 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
// --- 1. SETUP UI & NAVIGASJON --- // --- 1. SETUP UI & NAVIGASJON ---
// Sjekket activity_main.xml: ID er "bottom_nav_view"
bottomNav = findViewById(R.id.bottom_nav_view); bottomNav = findViewById(R.id.bottom_nav_view);
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager() NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
@ -62,6 +59,13 @@ public class MainActivity extends AppCompatActivity {
navController = navHostFragment.getNavController(); navController = navHostFragment.getNavController();
if (bottomNav != null) { if (bottomNav != null) {
NavigationUI.setupWithNavController(bottomNav, navController); NavigationUI.setupWithNavController(bottomNav, navController);
// --- NYTT: Håndter "Reselection" (Klikk fanen man allerede er i) ---
bottomNav.setOnItemReselectedListener(item -> {
// Dette fjerner alt som ligger "oppå" hovedsiden i stabelen.
// F.eks: Hjem -> Kalender Full -> (Klikk Hjem) -> Hjem
navController.popBackStack(item.getItemId(), false);
});
} }
// Skjul meny login-skjerm // Skjul meny login-skjerm
@ -76,32 +80,26 @@ public class MainActivity extends AppCompatActivity {
}); });
} }
// --- 2. VARSLINGSOPPSETT (NYTT) --- // --- 2. VARSLINGSOPPSETT ---
createNotificationChannel(); createNotificationChannel();
// Initialiser permission launcher for varsler
requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
if (isGranted) { if (isGranted) {
Log.d(TAG, "Varslingstillatelse gitt!"); Log.d(TAG, "Varslingstillatelse gitt!");
} else { } else {
Log.e(TAG, "Varslingstillatelse avslått. Bruker får ikke kalendervarsler."); Log.e(TAG, "Varslingstillatelse avslått.");
} }
}); });
// Sjekk tillatelser (både Varsler og Alarmer)
checkNotificationPermission(); checkNotificationPermission();
checkExactAlarmPermission(); checkExactAlarmPermission();
// Start bakgrunnsjobben for kalenderen
scheduleCalendarWork(); scheduleCalendarWork();
// --- 3. AUTENTISERING (GAMMELT) --- // --- 3. AUTENTISERING ---
checkLoginState(); checkLoginState();
} }
/**
* Sjekker om brukeren er logget inn med Google, og gjør en silent refresh mot WP.
*/
private void checkLoginState() { private void checkLoginState() {
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this); GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if (account == null) { if (account == null) {
@ -160,8 +158,6 @@ public class MainActivity extends AppCompatActivity {
} }
} }
// --- NYE HJELPEMETODER FOR VARSLING ---
private void createNotificationChannel() { private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "KBS Kalendervarsler"; CharSequence name = "KBS Kalendervarsler";
@ -184,10 +180,6 @@ public class MainActivity extends AppCompatActivity {
} }
} }
/**
* Sjekker om appen har lov til å sette nøyaktige alarmer (SCHEDULE_EXACT_ALARM).
* Hvis ikke, spør brukeren om å til innstillinger.
*/
private void checkExactAlarmPermission() { private void checkExactAlarmPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
@ -206,9 +198,6 @@ public class MainActivity extends AppCompatActivity {
} }
} }
/**
* Starter WorkManager som sjekker kalenderen hvert 15. minutt.
*/
private void scheduleCalendarWork() { private void scheduleCalendarWork() {
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES) PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
.build(); .build();

View file

@ -1,11 +1,14 @@
package com.kbs.kbsintranett; package com.kbs.kbsintranett;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -17,6 +20,41 @@ import com.bumptech.glide.Glide;
public class NewsDetailFragment extends Fragment { public class NewsDetailFragment extends Fragment {
// CSS Styling (Samme stil som håndboken, pluss bildehåndtering)
private static final String CSS_STYLE =
"<style>" +
"body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #333333; line-height: 1.6; padding: 0; margin: 0; }" +
"p, ul, li { margin-bottom: 12px; font-size: 16px; }" +
"a { color: #0069B3; font-weight: bold; text-decoration: none; }" +
"h1, h2, h3 { color: #0069B3; margin-top: 20px; margin-bottom: 10px; }" +
// Bilde-styling
"img { " +
" max-width: 100% !important; " +
" height: auto !important; " +
" border-radius: 4px; " +
" margin: 16px 0; " +
" box-shadow: 0 2px 5px rgba(0,0,0,0.1); " +
" display: block;" +
"}" +
// Fjerner unødvendig whitespace fra WP-galleri
".gallery-item { margin: 0; padding: 0; }" +
"</style>";
// JavaScript for å fange opp bildeklikk
private static final String JS_SCRIPT =
"<script>" +
"document.addEventListener('DOMContentLoaded', function() {" +
" var images = document.getElementsByTagName('img');" +
" for (var i = 0; i < images.length; i++) {" +
" images[i].onclick = function() {" +
" Android.showImage(this.src);" +
" }" +
" }" +
"});" +
"</script>";
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@ -27,7 +65,6 @@ public class NewsDetailFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
// Hent data fra argumentene (sendt fra HomeFragment/NewsFullFragment)
if (getArguments() != null) { if (getArguments() != null) {
WpPost post = (WpPost) getArguments().getSerializable("post_data"); WpPost post = (WpPost) getArguments().getSerializable("post_data");
if (post != null) { if (post != null) {
@ -44,9 +81,10 @@ public class NewsDetailFragment extends Fragment {
TextView title = view.findViewById(R.id.detail_title); TextView title = view.findViewById(R.id.detail_title);
TextView category = view.findViewById(R.id.detail_category); TextView category = view.findViewById(R.id.detail_category);
TextView date = view.findViewById(R.id.detail_date); TextView date = view.findViewById(R.id.detail_date);
TextView author = view.findViewById(R.id.detail_author); // NY TextView author = view.findViewById(R.id.detail_author);
TextView content = view.findViewById(R.id.detail_content); WebView webView = view.findViewById(R.id.detail_webview);
// Header bilde
String imgUrl = post.getFeaturedImageUrl(); String imgUrl = post.getFeaturedImageUrl();
if (imgUrl != null) { if (imgUrl != null) {
Glide.with(this).load(imgUrl).centerCrop().into(image); Glide.with(this).load(imgUrl).centerCrop().into(image);
@ -57,11 +95,59 @@ public class NewsDetailFragment extends Fragment {
title.setText(post.getTitleStr()); title.setText(post.getTitleStr());
category.setText(post.getCategoryName()); category.setText(post.getCategoryName());
date.setText("Publisert: " + post.date); date.setText("Publisert: " + post.date);
// NYTT: Sett forfatter
author.setText("Av: " + post.getAuthorName()); author.setText("Av: " + post.getAuthorName());
content.setText(Html.fromHtml(post.getContentStr(), Html.FROM_HTML_MODE_COMPACT)); // Konfigurer WebView
content.setMovementMethod(LinkMovementMethod.getInstance()); WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
// Legg til Interface for å snakke med Java
webView.addJavascriptInterface(new WebAppInterface(getContext()), "Android");
// Håndter linker internt (som i Håndboken)
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Bruk samme link-logikk som i Håndboken hvis nødvendig,
// men her lar vi linker åpnes i nettleser for enkelhets skyld foreløpig
return false;
}
});
// Bygg HTML
String rawContent = post.getContentStr();
// Vask innholdet litt hvis nødvendig (f.eks fjerne inline styles som ødelegger)
// Her legger vi bare til vår CSS og JS
String htmlData = "<!DOCTYPE html><html><head>" +
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" +
CSS_STYLE +
JS_SCRIPT +
"</head><body>" +
rawContent +
"</body></html>";
webView.loadDataWithBaseURL("https://intranet.kbs.no", htmlData, "text/html", "UTF-8", null);
}
// Bridge-klasse for å ta imot klikk fra JavaScript
public class WebAppInterface {
Context mContext;
WebAppInterface(Context c) {
mContext = c;
}
@JavascriptInterface
public void showImage(String url) {
// kjøres UI-tråden
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
ImageDialogFragment dialog = ImageDialogFragment.newInstance(url);
dialog.show(getParentFragmentManager(), "image_lightbox");
});
}
}
} }
} }

View file

@ -27,22 +27,24 @@ public class ProfileFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_profile, container, false); View view = inflater.inflate(R.layout.fragment_profile, container, false);
// 1. Finn Views
ImageView closeBtn = view.findViewById(R.id.btn_close_profile); ImageView closeBtn = view.findViewById(R.id.btn_close_profile);
ImageView profileImage = view.findViewById(R.id.profile_image); ImageView profileImage = view.findViewById(R.id.profile_image);
TextView nameText = view.findViewById(R.id.profile_name); TextView nameText = view.findViewById(R.id.profile_name);
TextView emailText = view.findViewById(R.id.profile_email); TextView emailText = view.findViewById(R.id.profile_email);
TextView roleText = view.findViewById(R.id.profile_role); TextView roleText = view.findViewById(R.id.profile_role);
Button logoutBtn = view.findViewById(R.id.btn_logout); Button logoutBtn = view.findViewById(R.id.btn_logout);
Button updateInfoBtn = view.findViewById(R.id.btn_update_info); // NY Button updateInfoBtn = view.findViewById(R.id.btn_update_info);
TextView versionText = view.findViewById(R.id.tv_version_info); // NYTT
// 2. Hent data fra UserManager
UserManager user = UserManager.getInstance(); UserManager user = UserManager.getInstance();
nameText.setText(user.getUserDisplayName()); nameText.setText(user.getUserDisplayName());
emailText.setText(user.getUserEmail()); emailText.setText(user.getUserEmail());
roleText.setText("Rolle: " + user.getUserRole()); roleText.setText("Rolle: " + user.getUserRole());
// 3. Last bilde med Glide // NYTT: Sett versjonstekst
String versionInfo = "Versjon " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")";
versionText.setText(versionInfo);
if (user.getPhotoUrl() != null) { if (user.getPhotoUrl() != null) {
Glide.with(this) Glide.with(this)
.load(user.getPhotoUrl()) .load(user.getPhotoUrl())
@ -50,44 +52,33 @@ public class ProfileFragment extends Fragment {
.into(profileImage); .into(profileImage);
} }
// 4. Håndter "Lukk" (X) knapp - tilbake til forrige skjerm
closeBtn.setOnClickListener(v -> { closeBtn.setOnClickListener(v -> {
Navigation.findNavController(view).navigateUp(); Navigation.findNavController(view).navigateUp();
}); });
// 5. Håndter "Oppdater opplysninger" (Skjema ID 1)
updateInfoBtn.setOnClickListener(v -> { updateInfoBtn.setOnClickListener(v -> {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putInt("formId", 1); // ID 1 er Ansatteopplysninger bundle.putInt("formId", 1);
Navigation.findNavController(view).navigate(R.id.action_profile_to_form, bundle); Navigation.findNavController(view).navigate(R.id.action_profile_to_form, bundle);
}); });
// 6. Håndter utlogging
logoutBtn.setOnClickListener(v -> performLogout()); logoutBtn.setOnClickListener(v -> performLogout());
return view; return view;
} }
private void performLogout() { private void performLogout() {
// A. Konfigurer Google Client
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(MainActivity.GOOGLE_WEB_CLIENT_ID) .requestIdToken(MainActivity.GOOGLE_WEB_CLIENT_ID)
.requestEmail() .requestEmail()
.build(); .build();
GoogleSignInClient client = GoogleSignIn.getClient(requireActivity(), gso); GoogleSignInClient client = GoogleSignIn.getClient(requireActivity(), gso);
// B. Logg ut fra Google
client.signOut().addOnCompleteListener(task -> { client.signOut().addOnCompleteListener(task -> {
// C. Tøm interne data
UserManager.getInstance().logout(); UserManager.getInstance().logout();
RetrofitClient.clearClient(); RetrofitClient.clearClient();
// D. Naviger tilbake til Login-skjermen
NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); 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); navController.navigate(R.id.action_profile_to_login);
Toast.makeText(getContext(), "Du er nå logget ut", Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Du er nå logget ut", Toast.LENGTH_SHORT).show();
}); });
} }

View file

@ -1,126 +1,137 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp"
android:background="@color/kbs_very_light_blue"> 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 <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:orientation="horizontal" android:orientation="vertical"
android:gravity="center_vertical" android:padding="8dp">
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp">
<TextView <!-- OVERSKRIFT OG PROFIL -->
android:layout_width="0dp" <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" 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>
<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:layout_weight="1"
android:text="Kommende hendelser" android:scrollbars="vertical"
android:textSize="18sp" android:layout_marginBottom="16dp"/>
android:textStyle="bold"
android:textColor="@color/black"/>
<TextView <!-- NYHETER-SEKSJON -->
android:id="@+id/btn_view_all_calendar" <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Se alle >" android:orientation="horizontal"
android:textColor="@color/kbs_logo_blue" android:gravity="center_vertical"
android:textStyle="bold" android:layout_marginBottom="8dp"
android:padding="8dp" android:layout_marginStart="8dp"
android:background="?attr/selectableItemBackground"/> 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> </LinearLayout>
<androidx.recyclerview.widget.RecyclerView <!-- DENNE MANGLER SANNZYNLIGVIS HOS DEG NÅ: -->
android:id="@+id/recycler_calendar" <ProgressBar
android:layout_width="match_parent" android:id="@+id/main_loading_spinner"
android:layout_height="0dp" android:layout_width="wrap_content"
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:layout_height="wrap_content"
android:orientation="horizontal" android:layout_gravity="center"
android:gravity="center_vertical" android:visibility="visible"/>
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp">
<TextView </FrameLayout>
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>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
<!-- Lukkeknapp -->
<ImageButton
android:id="@+id/btn_close_image"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@android:drawable/ic_menu_close_clear_cancel"
android:background="?attr/selectableItemBackgroundBorderless"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_margin="16dp"
android:tint="@android:color/white"
android:elevation="10dp"/>
<!-- Selve bildet -->
<ImageView
android:id="@+id/full_screen_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:scaleType="fitCenter"
android:adjustViewBounds="true"/>
<!-- Loading spinner -->
<ProgressBar
android:id="@+id/loading_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>

View file

@ -45,6 +45,7 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<!-- Kategori -->
<TextView <TextView
android:id="@+id/detail_category" android:id="@+id/detail_category"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -55,6 +56,7 @@
android:textSize="12sp" android:textSize="12sp"
android:layout_marginBottom="8dp"/> android:layout_marginBottom="8dp"/>
<!-- Tittel -->
<TextView <TextView
android:id="@+id/detail_title" android:id="@+id/detail_title"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -64,6 +66,7 @@
android:textColor="@android:color/black" android:textColor="@android:color/black"
android:layout_marginBottom="8dp"/> android:layout_marginBottom="8dp"/>
<!-- Dato og Forfatter -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -88,13 +91,12 @@
android:text="Av: Forfatter"/> android:text="Av: Forfatter"/>
</LinearLayout> </LinearLayout>
<TextView <!-- BYTTET FRA TextView TIL WebView -->
android:id="@+id/detail_content" <WebView
android:id="@+id/detail_webview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="#333333" android:scrollbars="none" />
android:textSize="16sp"
android:lineSpacingExtra="4dp"/>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View file

@ -97,7 +97,17 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Logg ut" android:text="Logg ut"
android:backgroundTint="@color/kbs_logo_accent_red" android:backgroundTint="@color/kbs_logo_accent_red"
android:textColor="@color/white"/> android:textColor="@color/white"
android:layout_marginBottom="32dp"/>
<!-- NYTT: VERSJONSINFO -->
<TextView
android:id="@+id/tv_version_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Versjon 1.0"
android:textColor="#999999"
android:textSize="12sp"/>
</LinearLayout> </LinearLayout>

View file

@ -1,4 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
} // NY LINJE: Legg til Google Services plugin her
id("com.google.gms.google-services") version "4.4.2" apply false
}