Før ny chat
This commit is contained in:
parent
a66dafb6c3
commit
b74c8715ef
21 changed files with 2854 additions and 412 deletions
|
|
@ -15,6 +15,38 @@ import java.util.TimeZone;
|
||||||
|
|
||||||
public class CalendarManager {
|
public class CalendarManager {
|
||||||
|
|
||||||
|
// NY HJELPEMETODE: Finner ID-ene til kalendere som tilhører @kbs.no
|
||||||
|
private static List<String> getKbsCalendarIds(Context context) {
|
||||||
|
List<String> ids = new ArrayList<>();
|
||||||
|
|
||||||
|
String[] projection = new String[] {
|
||||||
|
CalendarContract.Calendars._ID,
|
||||||
|
CalendarContract.Calendars.ACCOUNT_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
// Vi ser etter kontoer som slutter på @kbs.no
|
||||||
|
// (SQL: account_name LIKE '%@kbs.no')
|
||||||
|
String selection = CalendarContract.Calendars.ACCOUNT_NAME + " LIKE ?";
|
||||||
|
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)); // Legg til kalender-ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
public static List<CalendarEvent> getDeviceEvents(Context context) {
|
public static List<CalendarEvent> getDeviceEvents(Context context) {
|
||||||
List<CalendarEvent> deviceEvents = new ArrayList<>();
|
List<CalendarEvent> deviceEvents = new ArrayList<>();
|
||||||
|
|
||||||
|
|
@ -23,10 +55,18 @@ public class CalendarManager {
|
||||||
return deviceEvents;
|
return deviceEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ENDRET: Hent events fra 1 år tilbake og 1 år frem
|
// 1. Finn først ID-ene til KBS-kalenderne
|
||||||
|
List<String> kbsCalendarIds = getKbsCalendarIds(context);
|
||||||
|
|
||||||
|
// Hvis ingen kbs-kalendere finnes på telefonen, returner tom liste
|
||||||
|
if (kbsCalendarIds.isEmpty()) {
|
||||||
|
return deviceEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hent events fra 1 år tilbake og 1 år frem
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long startMillis = now - (365L * 24 * 60 * 60 * 1000); // 1 år tilbake
|
long startMillis = now - (365L * 24 * 60 * 60 * 1000);
|
||||||
long endMillis = now + (365L * 24 * 60 * 60 * 1000); // 1 år frem
|
long endMillis = now + (365L * 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
String[] projection = new String[]{
|
String[] projection = new String[]{
|
||||||
CalendarContract.Events.TITLE,
|
CalendarContract.Events.TITLE,
|
||||||
|
|
@ -34,21 +74,39 @@ public class CalendarManager {
|
||||||
CalendarContract.Events.DTEND,
|
CalendarContract.Events.DTEND,
|
||||||
CalendarContract.Events.DESCRIPTION,
|
CalendarContract.Events.DESCRIPTION,
|
||||||
CalendarContract.Events.EVENT_LOCATION,
|
CalendarContract.Events.EVENT_LOCATION,
|
||||||
CalendarContract.Events.ALL_DAY // Nyttig for å vite om det er heldags
|
CalendarContract.Events.ALL_DAY
|
||||||
};
|
};
|
||||||
|
|
||||||
String selection = CalendarContract.Events.DTSTART + " >= ? AND " + CalendarContract.Events.DTSTART + " <= ?";
|
// 2. Bygg opp spørringen for å filtrere på disse ID-ene
|
||||||
String[] selectionArgs = new String[]{String.valueOf(startMillis), String.valueOf(endMillis)};
|
// Resultatet blir noe sånt som: "((calendar_id = ?) OR (calendar_id = ?)) AND dtstart >= ? AND dtstart <= ?"
|
||||||
|
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(
|
try (Cursor cursor = context.getContentResolver().query(
|
||||||
CalendarContract.Events.CONTENT_URI,
|
CalendarContract.Events.CONTENT_URI,
|
||||||
projection,
|
projection,
|
||||||
selection,
|
selection.toString(),
|
||||||
selectionArgs,
|
selectionArgs,
|
||||||
CalendarContract.Events.DTSTART + " ASC"
|
CalendarContract.Events.DTSTART + " ASC"
|
||||||
)) {
|
)) {
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
// Vi bruker ISO format internt for sortering
|
|
||||||
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());
|
||||||
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
|
|
@ -63,12 +121,10 @@ public class CalendarManager {
|
||||||
String rawEnd;
|
String rawEnd;
|
||||||
|
|
||||||
if (allDay == 1) {
|
if (allDay == 1) {
|
||||||
// For heldags lagrer vi bare datoen: yyyy-MM-dd
|
|
||||||
SimpleDateFormat shortFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
SimpleDateFormat shortFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||||
rawStart = shortFormat.format(new Date(dtStart));
|
rawStart = shortFormat.format(new Date(dtStart));
|
||||||
rawEnd = shortFormat.format(new Date(dtEnd));
|
rawEnd = shortFormat.format(new Date(dtEnd));
|
||||||
} else {
|
} else {
|
||||||
// For vanlige events bruker vi full tid
|
|
||||||
rawStart = isoFormat.format(new Date(dtStart));
|
rawStart = isoFormat.format(new Date(dtStart));
|
||||||
rawEnd = isoFormat.format(new Date(dtEnd));
|
rawEnd = isoFormat.format(new Date(dtEnd));
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +140,7 @@ public class CalendarManager {
|
||||||
return deviceEvents;
|
return deviceEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- ROBUST DATO-PARSING (Løser "null" problemet) ---
|
// --- (formatEventForUI og mergeAndSort er uendret fra forrige versjon) ---
|
||||||
public static void formatEventForUI(CalendarEvent event) {
|
public static void formatEventForUI(CalendarEvent event) {
|
||||||
if (event.getRawDate() == null) return;
|
if (event.getRawDate() == null) return;
|
||||||
|
|
||||||
|
|
@ -102,32 +158,26 @@ public class CalendarManager {
|
||||||
|
|
||||||
String raw = event.getRawDate();
|
String raw = event.getRawDate();
|
||||||
|
|
||||||
// SJEKK 1: Er det heldagsdato? (Lengde 10, f.eks "2025-12-31")
|
|
||||||
if (raw.length() == 10 && !raw.contains("T") && !raw.contains(" ")) {
|
if (raw.length() == 10 && !raw.contains("T") && !raw.contains(" ")) {
|
||||||
SimpleDateFormat shortFmt = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
SimpleDateFormat shortFmt = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||||
date = shortFmt.parse(raw);
|
date = shortFmt.parse(raw);
|
||||||
isAllDay = true;
|
isAllDay = true;
|
||||||
|
|
||||||
if (event.getRawEndDate() != null && event.getRawEndDate().length() == 10) {
|
if (event.getRawEndDate() != null && event.getRawEndDate().length() == 10) {
|
||||||
endDate = shortFmt.parse(event.getRawEndDate());
|
endDate = shortFmt.parse(event.getRawEndDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SJEKK 2: Er det ISO format? (Har 'T')
|
|
||||||
else if (raw.contains("T")) {
|
else if (raw.contains("T")) {
|
||||||
SimpleDateFormat isoFmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
SimpleDateFormat isoFmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
||||||
isoFmt.setTimeZone(TimeZone.getTimeZone("Europe/Oslo")); // Viktig for Google events
|
isoFmt.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
||||||
date = isoFmt.parse(raw);
|
date = isoFmt.parse(raw);
|
||||||
|
|
||||||
if (event.getRawEndDate() != null && event.getRawEndDate().contains("T")) {
|
if (event.getRawEndDate() != null && event.getRawEndDate().contains("T")) {
|
||||||
endDate = isoFmt.parse(event.getRawEndDate());
|
endDate = isoFmt.parse(event.getRawEndDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SJEKK 3: Er det SQL format? (Mellomrom)
|
|
||||||
else {
|
else {
|
||||||
SimpleDateFormat sqlFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
SimpleDateFormat sqlFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||||
sqlFmt.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
sqlFmt.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
||||||
date = sqlFmt.parse(raw);
|
date = sqlFmt.parse(raw);
|
||||||
|
|
||||||
if (event.getRawEndDate() != null) {
|
if (event.getRawEndDate() != null) {
|
||||||
endDate = sqlFmt.parse(event.getRawEndDate());
|
endDate = sqlFmt.parse(event.getRawEndDate());
|
||||||
}
|
}
|
||||||
|
|
@ -142,9 +192,6 @@ public class CalendarManager {
|
||||||
} else {
|
} else {
|
||||||
String timeStr = outputTime.format(date);
|
String timeStr = outputTime.format(date);
|
||||||
if (endDate != null) {
|
if (endDate != null) {
|
||||||
// Hvis sluttdato er samme dag, vis bare klokkeslett
|
|
||||||
// (Enkelt sjekk: hvis datoene er like)
|
|
||||||
// Her forenkler vi og viser alltid sluttid hvis den finnes
|
|
||||||
timeStr += " - " + outputTime.format(endDate);
|
timeStr += " - " + outputTime.format(endDate);
|
||||||
}
|
}
|
||||||
event.setTime("Kl. " + timeStr);
|
event.setTime("Kl. " + timeStr);
|
||||||
|
|
|
||||||
69
app/src/main/java/com/kbs/kbsintranett/CategoryAdapter.java
Normal file
69
app/src/main/java/com/kbs/kbsintranett/CategoryAdapter.java
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,60 +2,177 @@ package com.kbs.kbsintranett;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.Gravity;
|
||||||
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.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class FormsListFragment extends Fragment {
|
public class FormsListFragment extends Fragment {
|
||||||
|
|
||||||
|
private LinearLayout formsContainer;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private TextView errorText;
|
||||||
|
|
||||||
|
private static final Pattern TITLE_NUMBER_PATTERN = Pattern.compile("^(\\d+)[.\\s-]+\\s*(.*)");
|
||||||
|
|
||||||
@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) {
|
||||||
View view = inflater.inflate(R.layout.fragment_forms_list, container, false);
|
View view = inflater.inflate(R.layout.fragment_forms_list, container, false);
|
||||||
LinearLayout formsContainer = view.findViewById(R.id.forms_container);
|
formsContainer = view.findViewById(R.id.forms_container);
|
||||||
|
|
||||||
// Legger til knappene for de ulike skjemaene
|
progressBar = new ProgressBar(getContext());
|
||||||
addFormButton(formsContainer, "1. Ansatteopplysninger", 1);
|
LinearLayout.LayoutParams progressParams = new LinearLayout.LayoutParams(
|
||||||
addFormButton(formsContainer, "4. RUH (Rapport om uønsket hendelse)", 4);
|
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
addFormButton(formsContainer, "9. Sikkerhetskurs / Kompetansebevis", 9);
|
progressParams.gravity = Gravity.CENTER;
|
||||||
addFormButton(formsContainer, "10. HMS-bekreftelse", 10);
|
progressBar.setLayoutParams(progressParams);
|
||||||
addFormButton(formsContainer, "11. Egenmelding", 11);
|
formsContainer.addView(progressBar);
|
||||||
addFormButton(formsContainer, "12. Sjekkliste for firmabil", 12);
|
|
||||||
addFormButton(formsContainer, "14. SJA (Sikker Jobbanalyse)", 14);
|
errorText = new TextView(getContext());
|
||||||
addFormButton(formsContainer, "15. Fraværsvarsel", 15);
|
errorText.setTextColor(Color.RED);
|
||||||
addFormButton(formsContainer, "16. Refusjon utlegg", 16);
|
errorText.setVisibility(View.GONE);
|
||||||
addFormButton(formsContainer, "21. Forberedelse til medarbeidersamtale", 21);
|
errorText.setPadding(20, 20, 20, 20);
|
||||||
addFormButton(formsContainer, "22. Medarbeiderundersøkelse", 22);
|
formsContainer.addView(errorText);
|
||||||
|
|
||||||
|
fetchFormsList();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fetchFormsList() {
|
||||||
|
RetrofitClient.getApiService().getFormsListMap().enqueue(new Callback<Map<String, GravityForm>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<Map<String, GravityForm>> call, Response<Map<String, GravityForm>> response) {
|
||||||
|
if (!isAdded()) return;
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
List<GravityForm> activeForms = new ArrayList<>();
|
||||||
|
|
||||||
|
for (GravityForm form : response.body().values()) {
|
||||||
|
// 1. Sjekk om skjemaet er aktivt (Bruker nå metoden i GravityForm)
|
||||||
|
if (form.getIsActive()) {
|
||||||
|
activeForms.add(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Sorter basert på tallet i tittelen
|
||||||
|
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. Er du admin?)";
|
||||||
|
}
|
||||||
|
showError(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<Map<String, GravityForm>> call, Throwable t) {
|
||||||
|
if (!isAdded()) return;
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
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) {
|
||||||
|
// FIX: form.id er allerede en int i GravityForm.java, så vi trenger ikke parse
|
||||||
|
int formId = form.id;
|
||||||
|
String cleanTitle = cleanTitle(form.title);
|
||||||
|
addFormButton(formsContainer, cleanTitle, formId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addFormButton(LinearLayout container, String title, int formId) {
|
private void addFormButton(LinearLayout container, String title, int formId) {
|
||||||
Button btn = new Button(getContext());
|
Button btn = new Button(getContext());
|
||||||
btn.setText(title);
|
btn.setText(title);
|
||||||
btn.setBackgroundColor(Color.parseColor("#0069B3")); // KBS Blå
|
btn.setBackgroundColor(Color.parseColor("#0069B3"));
|
||||||
btn.setTextColor(Color.WHITE);
|
btn.setTextColor(Color.WHITE);
|
||||||
btn.setPadding(30, 30, 30, 30);
|
btn.setPadding(30, 30, 30, 30);
|
||||||
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
params.setMargins(0, 0, 0, 20); // Litt avstand mellom knappene
|
params.setMargins(0, 0, 0, 20);
|
||||||
btn.setLayoutParams(params);
|
btn.setLayoutParams(params);
|
||||||
|
|
||||||
btn.setOnClickListener(v -> {
|
btn.setOnClickListener(v -> {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putInt("formId", formId);
|
bundle.putInt("formId", formId);
|
||||||
// HER VAR FEILEN: Endret R.id.nav_forms til riktig action ID
|
|
||||||
Navigation.findNavController(v).navigate(R.id.action_formsListFragment_to_formsDetailFragment, bundle);
|
Navigation.findNavController(v).navigate(R.id.action_formsListFragment_to_formsDetailFragment, bundle);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.addView(btn);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,13 +42,15 @@ public class GravityField {
|
||||||
@SerializedName("content")
|
@SerializedName("content")
|
||||||
public String content;
|
public String content;
|
||||||
|
|
||||||
|
// --- BRUKER ADAPTEREN HER ---
|
||||||
|
@JsonAdapter(InputsAdapter.class)
|
||||||
@SerializedName("inputs")
|
@SerializedName("inputs")
|
||||||
public List<GravityField> inputs;
|
public List<GravityField> inputs;
|
||||||
|
// ---------------------------
|
||||||
|
|
||||||
@SerializedName("isHidden")
|
@SerializedName("isHidden")
|
||||||
public boolean isHidden;
|
public boolean isHidden;
|
||||||
|
|
||||||
// NYTT: For å sjekke om feltet er Read Only (f.eks dato i refusjon)
|
|
||||||
@SerializedName("gwreadonly_enable")
|
@SerializedName("gwreadonly_enable")
|
||||||
public boolean readOnly;
|
public boolean readOnly;
|
||||||
|
|
||||||
|
|
@ -63,32 +65,19 @@ public class GravityField {
|
||||||
public String gpnfForm;
|
public String gpnfForm;
|
||||||
|
|
||||||
public static class Choice {
|
public static class Choice {
|
||||||
@SerializedName("text")
|
@SerializedName("text") public String text;
|
||||||
public String text;
|
@SerializedName("value") public String value;
|
||||||
|
|
||||||
@SerializedName("value")
|
|
||||||
public String value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ConditionalLogic {
|
public static class ConditionalLogic {
|
||||||
@SerializedName("actionType")
|
@SerializedName("actionType") public String actionType;
|
||||||
public String actionType;
|
@SerializedName("logicType") public String logicType;
|
||||||
|
@SerializedName("rules") public List<Rule> rules;
|
||||||
@SerializedName("logicType")
|
|
||||||
public String logicType;
|
|
||||||
|
|
||||||
@SerializedName("rules")
|
|
||||||
public List<Rule> rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Rule {
|
public static class Rule {
|
||||||
@SerializedName("fieldId")
|
@SerializedName("fieldId") public String fieldId;
|
||||||
public String fieldId;
|
@SerializedName("operator") public String operator;
|
||||||
|
@SerializedName("value") public String value;
|
||||||
@SerializedName("operator")
|
|
||||||
public String operator;
|
|
||||||
|
|
||||||
@SerializedName("value")
|
|
||||||
public String value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,9 +13,17 @@ public class GravityForm {
|
||||||
@SerializedName("description")
|
@SerializedName("description")
|
||||||
public String description;
|
public String description;
|
||||||
|
|
||||||
|
// Endret til Object for å være robust mot både "1" (String) og 1 (Int) fra API
|
||||||
@SerializedName("is_active")
|
@SerializedName("is_active")
|
||||||
public String isActive; // "1" = Aktiv, "0" = Inaktiv
|
public Object isActive;
|
||||||
|
|
||||||
@SerializedName("fields")
|
@SerializedName("fields")
|
||||||
public List<GravityField> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,8 +18,12 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.work.PeriodicWorkRequest;
|
import androidx.work.PeriodicWorkRequest;
|
||||||
import androidx.work.WorkManager;
|
import androidx.work.WorkManager;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
|
|
@ -33,16 +37,19 @@ public class HomeFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Håndter svar på kalendertillatelse
|
// Håndter svar på kalendertillatelse
|
||||||
requestPermissionLauncher = registerForActivityResult(
|
requestPermissionLauncher = registerForActivityResult(
|
||||||
new ActivityResultContracts.RequestPermission(),
|
new ActivityResultContracts.RequestPermission(),
|
||||||
isGranted -> {
|
isGranted -> {
|
||||||
// Last kalender på nytt (nå med eller uten personlig kalender)
|
// Prøv å laste kalender på nytt (nå potensielt med personlig kalender)
|
||||||
fetchCalendarEvents(calendarRecycler);
|
if (calendarRecycler != null) {
|
||||||
|
fetchCalendarEvents(calendarRecycler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start bakgrunnsjobb for varsling (Hvert 15. minutt)
|
// Start bakgrunnsjobb for varsling (kjører hver 15. minutt)
|
||||||
startNotificationWorker();
|
startNotificationWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,18 +63,19 @@ 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);
|
||||||
|
|
||||||
// Profil-knapp
|
// 0. Profil-knapp
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kalender oppsett
|
// 1. Kalender oppsett
|
||||||
calendarRecycler = view.findViewById(R.id.recycler_calendar);
|
calendarRecycler = view.findViewById(R.id.recycler_calendar);
|
||||||
calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
|
calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
// Sett tom adapter midlertidig
|
||||||
calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {}));
|
calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {}));
|
||||||
|
|
||||||
// "Se alle" knapp
|
// "Se alle" knapp for kalender
|
||||||
TextView viewAllCalendar = view.findViewById(R.id.btn_view_all_calendar);
|
TextView viewAllCalendar = view.findViewById(R.id.btn_view_all_calendar);
|
||||||
if (viewAllCalendar != null) {
|
if (viewAllCalendar != null) {
|
||||||
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));
|
||||||
|
|
@ -88,18 +96,29 @@ public class HomeFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nyheter
|
// 2. Nyheter oppsett
|
||||||
RecyclerView newsRecycler = view.findViewById(R.id.recycler_news);
|
RecyclerView 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<>()));
|
// Sett tom adapter midlertidig
|
||||||
|
newsRecycler.setAdapter(new NewsAdapter(new ArrayList<>(), item -> {}));
|
||||||
|
|
||||||
|
// "Se alle" knapp for nyheter
|
||||||
|
TextView viewAllNews = view.findViewById(R.id.btn_view_all_news);
|
||||||
|
if (viewAllNews != null) {
|
||||||
|
viewAllNews.setOnClickListener(v -> {
|
||||||
|
Navigation.findNavController(view).navigate(R.id.action_home_to_newsFull);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fetchNewsFromWordpress(newsRecycler);
|
fetchNewsFromWordpress(newsRecycler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchCalendarEvents(RecyclerView recyclerView) {
|
private void fetchCalendarEvents(RecyclerView recyclerView) {
|
||||||
// 1. Hent personlige hendelser først
|
// 1. Hent personlige hendelser først (fra CalendarManager)
|
||||||
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext());
|
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext());
|
||||||
|
|
||||||
|
// 2. Hent API-hendelser fra WordPress
|
||||||
WordPressApiService apiService = RetrofitClient.getApiService();
|
WordPressApiService apiService = RetrofitClient.getApiService();
|
||||||
apiService.getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
apiService.getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -114,13 +133,24 @@ public class HomeFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flett lister
|
// 3. Flett listene (API + Personlig) og sorter
|
||||||
List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
|
List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
|
||||||
|
|
||||||
// Vis kun topp 5
|
// 4. Filtrer ut hendelser som har passert (vis kun fremtidige + i dag)
|
||||||
|
// (CalendarManager henter 1 år bakover, så vi må filtrere for "Topp 5 kommende")
|
||||||
|
List<CalendarEvent> upcomingEvents = new ArrayList<>();
|
||||||
|
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
|
||||||
|
|
||||||
|
for (CalendarEvent e : merged) {
|
||||||
|
if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) {
|
||||||
|
upcomingEvents.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Vis kun de 5 første av de kommende
|
||||||
List<CalendarEvent> top5 = new ArrayList<>();
|
List<CalendarEvent> top5 = new ArrayList<>();
|
||||||
for(int i=0; i<Math.min(merged.size(), 5); i++) {
|
for(int i=0; i<Math.min(upcomingEvents.size(), 5); i++) {
|
||||||
top5.add(merged.get(i));
|
top5.add(upcomingEvents.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
recyclerView.setAdapter(new CalendarAdapter(top5, event -> {
|
recyclerView.setAdapter(new CalendarAdapter(top5, event -> {
|
||||||
|
|
@ -135,6 +165,7 @@ public class HomeFragment extends Fragment {
|
||||||
// Hvis API feiler, vis bare personlige events hvis vi har noen
|
// Hvis API feiler, vis bare personlige events hvis vi har noen
|
||||||
if (!deviceEvents.isEmpty()) {
|
if (!deviceEvents.isEmpty()) {
|
||||||
List<CalendarEvent> top5 = new ArrayList<>();
|
List<CalendarEvent> top5 = new ArrayList<>();
|
||||||
|
// Filtrer og plukk topp 5 fra lokale også
|
||||||
for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) top5.add(deviceEvents.get(i));
|
for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) top5.add(deviceEvents.get(i));
|
||||||
|
|
||||||
recyclerView.setAdapter(new CalendarAdapter(top5, event -> {
|
recyclerView.setAdapter(new CalendarAdapter(top5, event -> {
|
||||||
|
|
@ -142,7 +173,6 @@ public class HomeFragment extends Fragment {
|
||||||
sheet.show(getParentFragmentManager(), "CalendarDetails");
|
sheet.show(getParentFragmentManager(), "CalendarDetails");
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
// Vis feil hvis alt er tomt
|
|
||||||
List<CalendarEvent> errorList = new ArrayList<>();
|
List<CalendarEvent> errorList = new ArrayList<>();
|
||||||
errorList.add(new CalendarEvent("Kunne ikke laste kalender", "Sjekk nettverk", "!", "OBS"));
|
errorList.add(new CalendarEvent("Kunne ikke laste kalender", "Sjekk nettverk", "!", "OBS"));
|
||||||
recyclerView.setAdapter(new CalendarAdapter(errorList, null));
|
recyclerView.setAdapter(new CalendarAdapter(errorList, null));
|
||||||
|
|
@ -151,48 +181,55 @@ public class HomeFragment extends Fragment {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fetchNewsFromWordpress(RecyclerView recyclerView) {
|
||||||
|
WordPressApiService apiService = RetrofitClient.getApiService();
|
||||||
|
// Bruker getPosts som henter 5-10 innlegg med ?_embed
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Datoformatering
|
||||||
|
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); // Setter pen dato
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sett adapter med Click Listener
|
||||||
|
NewsAdapter adapter = new NewsAdapter(wpPosts, post -> {
|
||||||
|
// Naviger til detaljvisning og send med post-objektet
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putSerializable("post_data", post); // WpPost er nå Serializable
|
||||||
|
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;
|
||||||
|
// Ved feil, sett tom liste (eller vis feilmelding)
|
||||||
|
recyclerView.setAdapter(new NewsAdapter(new ArrayList<>(), null));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void startNotificationWorker() {
|
private void startNotificationWorker() {
|
||||||
// Kjører en jobb hvert 15. minutt for å sjekke om det er nye møter
|
// Kjører en jobb hvert 15. minutt for å sjekke om det er nye møter i HMS-kalenderen
|
||||||
PeriodicWorkRequest notifRequest =
|
PeriodicWorkRequest notifRequest =
|
||||||
new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
|
new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
|
||||||
.build();
|
.build();
|
||||||
WorkManager.getInstance(requireContext()).enqueue(notifRequest);
|
WorkManager.getInstance(requireContext()).enqueue(notifRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... (fetchNewsFromWordpress beholdes som før) ...
|
|
||||||
private void fetchNewsFromWordpress(RecyclerView recyclerView) {
|
|
||||||
// [Lim inn koden for nyheter fra forrige svar her, den var OK]
|
|
||||||
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();
|
|
||||||
List<NewsItem> newsList = new ArrayList<>();
|
|
||||||
java.text.SimpleDateFormat rawFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", java.util.Locale.getDefault());
|
|
||||||
rawFormat.setTimeZone(java.util.TimeZone.getTimeZone("Europe/Oslo"));
|
|
||||||
java.text.SimpleDateFormat targetFormat = new java.text.SimpleDateFormat("dd. MMM yyyy", java.util.Locale.getDefault());
|
|
||||||
targetFormat.setTimeZone(java.util.TimeZone.getTimeZone("Europe/Oslo"));
|
|
||||||
|
|
||||||
for (WpPost post : wpPosts) {
|
|
||||||
String formattedDate = post.date;
|
|
||||||
try {
|
|
||||||
java.util.Date date = rawFormat.parse(post.date);
|
|
||||||
formattedDate = targetFormat.format(date);
|
|
||||||
} catch (java.text.ParseException e) {}
|
|
||||||
newsList.add(new NewsItem(post.getTitleStr(), post.getExcerptStr(), "Publisert: " + formattedDate));
|
|
||||||
}
|
|
||||||
recyclerView.setAdapter(new NewsAdapter(newsList));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<List<WpPost>> call, Throwable t) {
|
|
||||||
if (getContext() == null) return;
|
|
||||||
List<NewsItem> errorList = new ArrayList<>();
|
|
||||||
errorList.add(new NewsItem("Kunne ikke laste nyheter", "Sjekk nettverket ditt.", "System"));
|
|
||||||
recyclerView.setAdapter(new NewsAdapter(errorList));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
30
app/src/main/java/com/kbs/kbsintranett/InputsAdapter.java
Normal file
30
app/src/main/java/com/kbs/kbsintranett/InputsAdapter.java
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
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<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,17 +3,34 @@ package com.kbs.kbsintranett;
|
||||||
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.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
|
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
|
||||||
|
|
||||||
private List<NewsItem> newsList;
|
private List<WpPost> posts;
|
||||||
|
private OnItemClickListener listener; // NYTT
|
||||||
|
|
||||||
public NewsAdapter(List<NewsItem> newsList) {
|
// Interface for klikk
|
||||||
this.newsList = newsList;
|
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
|
@NonNull
|
||||||
|
|
@ -25,24 +42,62 @@ public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
NewsItem item = newsList.get(position);
|
WpPost post = posts.get(position);
|
||||||
holder.title.setText(item.getTitle());
|
|
||||||
holder.excerpt.setText(item.getExcerpt());
|
holder.title.setText(post.getTitleStr());
|
||||||
holder.author.setText("Av: " + item.getAuthor());
|
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
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return newsList.size();
|
return posts.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
TextView title, excerpt, author;
|
TextView title, excerpt, date, category;
|
||||||
|
ImageView image;
|
||||||
|
|
||||||
public ViewHolder(View view) {
|
public ViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
title = view.findViewById(R.id.news_title);
|
title = view.findViewById(R.id.news_title);
|
||||||
excerpt = view.findViewById(R.id.news_excerpt);
|
excerpt = view.findViewById(R.id.news_excerpt);
|
||||||
author = view.findViewById(R.id.news_author);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
144
app/src/main/java/com/kbs/kbsintranett/NewsFullFragment.java
Normal file
144
app/src/main/java/com/kbs/kbsintranett/NewsFullFragment.java
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
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) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,8 +16,8 @@ import retrofit2.http.PartMap;
|
||||||
import retrofit2.http.Query;
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
public interface WordPressApiService {
|
public interface WordPressApiService {
|
||||||
// 1. Hent nyheter
|
// 1. Hent nyheter - ENDRET: Lagt til &_embed for å få bilde og kategori
|
||||||
@GET("wp-json/wp/v2/posts?per_page=5")
|
@GET("wp-json/wp/v2/posts?per_page=10&_embed")
|
||||||
Call<List<WpPost>> getPosts();
|
Call<List<WpPost>> getPosts();
|
||||||
|
|
||||||
// 2. Hent et spesifikt skjema med ID
|
// 2. Hent et spesifikt skjema med ID
|
||||||
|
|
@ -57,7 +57,11 @@ public interface WordPressApiService {
|
||||||
@Query("paging[page_size]") int pageSize
|
@Query("paging[page_size]") int pageSize
|
||||||
);
|
);
|
||||||
|
|
||||||
// 9. HENT ÉN ENKELT INNSENDING (Ny! Brukes for å hente detaljer fra underskjema)
|
// 9. HENT ÉN ENKELT INNSENDING
|
||||||
@GET("wp-json/gf/v2/entries/{entry_id}")
|
@GET("wp-json/gf/v2/entries/{entry_id}")
|
||||||
Call<JsonElement> getSingleEntry(@Path("entry_id") String entryId);
|
Call<JsonElement> getSingleEntry(@Path("entry_id") String entryId);
|
||||||
|
|
||||||
|
// 10. HENT ALLE NYHETER (F.eks 50 stk) - Brukes av "Se alle" siden
|
||||||
|
@GET("wp-json/wp/v2/posts?per_page=50&_embed")
|
||||||
|
Call<List<WpPost>> getAllPosts();
|
||||||
}
|
}
|
||||||
|
|
@ -1,31 +1,106 @@
|
||||||
package com.kbs.kbsintranett;
|
package com.kbs.kbsintranett;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class WpPost {
|
public class WpPost implements Serializable {
|
||||||
// WordPress sender tittelen som et objekt: "title": { "rendered": "Overskrift" }
|
|
||||||
@SerializedName("title")
|
@SerializedName("title")
|
||||||
public Rendered title;
|
public Rendered title;
|
||||||
|
|
||||||
@SerializedName("excerpt")
|
@SerializedName("excerpt")
|
||||||
public Rendered excerpt;
|
public Rendered excerpt;
|
||||||
|
|
||||||
|
@SerializedName("content")
|
||||||
|
public Rendered content;
|
||||||
|
|
||||||
@SerializedName("date")
|
@SerializedName("date")
|
||||||
public String date;
|
public String date;
|
||||||
|
|
||||||
// Hjelpeklasse for å hente ut teksten inni "rendered"
|
@SerializedName("_embedded")
|
||||||
public static class Rendered {
|
public Embedded embedded;
|
||||||
|
|
||||||
|
public static class Rendered implements Serializable {
|
||||||
@SerializedName("rendered")
|
@SerializedName("rendered")
|
||||||
public String renderedString;
|
public String renderedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
// En hjelpemetode for å få ren tekst ut (fjerner HTML-koder hvis nødvendig)
|
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() {
|
public String getTitleStr() {
|
||||||
return title != null ? title.renderedString : "Uten tittel";
|
return title != null ? title.renderedString : "Uten tittel";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getExcerptStr() {
|
public String getExcerptStr() {
|
||||||
// En enkel rensing av HTML-tags (f.eks <p>)
|
return excerpt != null ?
|
||||||
return excerpt != null ? android.text.Html.fromHtml(excerpt.renderedString, android.text.Html.FROM_HTML_MODE_COMPACT).toString() : "";
|
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 "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
5
app/src/main/res/drawable/bg_category_selected.xml
Normal file
5
app/src/main/res/drawable/bg_category_selected.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?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>
|
||||||
6
app/src/main/res/drawable/bg_category_unselected.xml
Normal file
6
app/src/main/res/drawable/bg_category_unselected.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?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>
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:textColor="@color/kbs_muted_blue_gray"
|
android:textColor="@color/kbs_muted_blue_gray"
|
||||||
android:layout_centerVertical="true"/>
|
android:layout_centerVertical="true"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/btn_profile"
|
android:id="@+id/btn_profile"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
|
|
@ -69,15 +70,34 @@
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
android:layout_marginBottom="16dp"/>
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Siste nytt"
|
android:orientation="horizontal"
|
||||||
android:textSize="18sp"
|
android:gravity="center_vertical"
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="@color/black"
|
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:layout_marginStart="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
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recycler_news"
|
android:id="@+id/recycler_news"
|
||||||
|
|
|
||||||
101
app/src/main/res/layout/fragment_news_detail.xml
Normal file
101
app/src/main/res/layout/fragment_news_detail.xml
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?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>
|
||||||
61
app/src/main/res/layout/fragment_news_full.xml
Normal file
61
app/src/main/res/layout/fragment_news_full.xml
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?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>
|
||||||
13
app/src/main/res/layout/item_category.xml
Normal file
13
app/src/main/res/layout/item_category.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?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" />
|
||||||
|
|
@ -3,42 +3,87 @@
|
||||||
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="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="24dp"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginHorizontal="8dp"
|
||||||
android:layout_marginEnd="4dp"
|
app:cardCornerRadius="12dp"
|
||||||
app:cardCornerRadius="8dp"
|
app:cardElevation="4dp"
|
||||||
app:cardElevation="2dp">
|
app:cardBackgroundColor="@android:color/white">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical">
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/news_title"
|
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_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Nyhets overskrift"
|
android:orientation="vertical"
|
||||||
android:textSize="18sp"
|
android:padding="16dp">
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="@android:color/black"/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/news_excerpt"
|
android:id="@+id/news_category"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:text="KATEGORI"
|
||||||
android:text="Her kommer ingressen..."
|
android:textSize="12sp"
|
||||||
android:textColor="@android:color/darker_gray"/>
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/kbs_logo_accent_red"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:letterSpacing="0.05"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/news_author"
|
android:id="@+id/news_title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
android:text="Overskrift på nyhetssaken kommer her"
|
||||||
android:text="Skrevet av: Ole"
|
android:textSize="20sp"
|
||||||
android:textSize="12sp"
|
android:textStyle="bold"
|
||||||
android:textStyle="italic"/>
|
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>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
@ -22,9 +22,18 @@
|
||||||
android:name="com.kbs.kbsintranett.HomeFragment"
|
android:name="com.kbs.kbsintranett.HomeFragment"
|
||||||
android:label="Hjem"
|
android:label="Hjem"
|
||||||
tools:layout="@layout/fragment_home">
|
tools:layout="@layout/fragment_home">
|
||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_home_to_calendarFull"
|
android:id="@+id/action_home_to_calendarFull"
|
||||||
app:destination="@id/navigation_calendar_full" />
|
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" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
|
|
@ -33,14 +42,29 @@
|
||||||
android:label="Kalender"
|
android:label="Kalender"
|
||||||
tools:layout="@layout/fragment_calendar_full" />
|
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
|
<fragment
|
||||||
android:id="@+id/navigation_forms"
|
android:id="@+id/navigation_forms"
|
||||||
android:name="com.kbs.kbsintranett.FormsListFragment"
|
android:name="com.kbs.kbsintranett.FormsListFragment"
|
||||||
android:label="Skjemaer"
|
android:label="Skjemaer"
|
||||||
tools:layout="@layout/fragment_forms">
|
tools:layout="@layout/fragment_forms_list"> <action
|
||||||
<action
|
android:id="@+id/action_formsListFragment_to_formsDetailFragment"
|
||||||
android:id="@+id/action_formsListFragment_to_formsDetailFragment"
|
app:destination="@id/navigation_forms_detail" />
|
||||||
app:destination="@id/navigation_forms_detail" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
|
|
|
||||||
2005
hele_prosjektet.txt
2005
hele_prosjektet.txt
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue