Før Oppgaver Steg 2, kobling mot WP

This commit is contained in:
ErolHaagenrud 2026-01-07 15:15:10 +01:00
parent c81310f982
commit 34ee55d362
21 changed files with 976 additions and 211 deletions

View file

@ -0,0 +1,187 @@
package com.kbs.kbsintranett;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class AddTaskBottomSheet extends BottomSheetDialogFragment {
private EditText etTitle, etDesc;
private Button btnDate, btnUsers, btnSave;
private TextView txtDatePreview, txtUsersPreview;
private Calendar dueDate = Calendar.getInstance();
private List<User> allUsersFromApi = new ArrayList<>();
private List<User> filteredUsers = new ArrayList<>();
private List<User> selectedUsers = new ArrayList<>();
public interface OnTaskAddedListener {
void onTaskAdded(TaskItem task);
}
private OnTaskAddedListener listener;
public void setOnTaskAddedListener(OnTaskAddedListener listener) {
this.listener = listener;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bottom_sheet_add_task, container, false);
etTitle = v.findViewById(R.id.et_task_title);
etDesc = v.findViewById(R.id.et_task_desc);
btnDate = v.findViewById(R.id.btn_task_date);
btnUsers = v.findViewById(R.id.btn_task_users);
btnSave = v.findViewById(R.id.btn_save_task);
txtDatePreview = v.findViewById(R.id.txt_date_preview);
txtUsersPreview = v.findViewById(R.id.txt_users_preview);
dueDate.add(Calendar.DAY_OF_MONTH, 1);
updateDatePreview();
btnDate.setOnClickListener(view -> {
new DatePickerDialog(getContext(), (d, y, m, day) -> {
dueDate.set(y, m, day);
updateDatePreview();
}, dueDate.get(Calendar.YEAR), dueDate.get(Calendar.MONTH), dueDate.get(Calendar.DAY_OF_MONTH)).show();
});
btnUsers.setOnClickListener(view -> showUserSelectionDialog());
btnSave.setOnClickListener(view -> saveTask());
fetchUsers();
return v;
}
private void fetchUsers() {
RetrofitClient.getApiService().getUsersList().enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.isSuccessful() && response.body() != null) {
allUsersFromApi = response.body();
filterUsersByHierarchy();
}
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {}
});
}
private void filterUsersByHierarchy() {
filteredUsers.clear();
UserManager me = UserManager.getInstance();
// 1. Hvis Admin/Editor, legg til alle
if (me.isEditorOrAbove()) {
filteredUsers.addAll(allUsersFromApi);
return;
}
// 2. Finn mine roller/avdelinger
List<String> myRoles = new ArrayList<>();
for (User u : allUsersFromApi) {
if (u.getEmail().equalsIgnoreCase(me.getUserEmail())) {
for (String r : u.getRoles()) myRoles.add(r.toLowerCase());
break;
}
}
// 3. Filtrer logikk (Identisk med CreateEventFragment)
for (User u : allUsersFromApi) {
// Alltid legg til seg selv
if (u.getEmail().equalsIgnoreCase(me.getUserEmail())) {
if (!filteredUsers.contains(u)) filteredUsers.add(u);
continue;
}
boolean hasAccess = false;
for (String role : u.getRoles()) {
String r = role.toLowerCase();
// Sjekk om vi deler en avdelingsrolle
if ((r.equals("serviceavdelingen") && myRoles.contains("serviceavdelingen")) ||
(r.equals("automasjonsavdelingen") && myRoles.contains("automasjonsavdelingen")) ||
(r.equals("prosjektavdelingen") && myRoles.contains("prosjektavdelingen")) ||
(r.equals("administrasjonen") && myRoles.contains("administrasjonen")) ||
(r.equals("kbs_alle") && myRoles.contains("kbs_alle"))) {
hasAccess = true;
break;
}
}
if (hasAccess && !filteredUsers.contains(u)) {
filteredUsers.add(u);
}
}
}
private void showUserSelectionDialog() {
if (filteredUsers.isEmpty()) {
Toast.makeText(getContext(), "Henter tilgjengelige personer...", Toast.LENGTH_SHORT).show();
return;
}
String[] names = new String[filteredUsers.size()];
boolean[] checked = new boolean[filteredUsers.size()];
for (int i = 0; i < filteredUsers.size(); i++) {
names[i] = filteredUsers.get(i).getName();
checked[i] = selectedUsers.contains(filteredUsers.get(i));
}
new AlertDialog.Builder(getContext())
.setTitle("Tildel til...")
.setMultiChoiceItems(names, checked, (dialog, which, isChecked) -> {
if (isChecked) selectedUsers.add(filteredUsers.get(which));
else selectedUsers.remove(filteredUsers.get(which));
})
.setPositiveButton("OK", (dialog, which) -> {
if (selectedUsers.isEmpty()) txtUsersPreview.setText("Kun meg");
else if (selectedUsers.size() == 1) txtUsersPreview.setText(selectedUsers.get(0).getName());
else txtUsersPreview.setText(selectedUsers.size() + " personer valgt");
})
.show();
}
private void updateDatePreview() {
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
txtDatePreview.setText("Frist: " + sdf.format(dueDate.getTime()));
}
private void saveTask() {
String title = etTitle.getText().toString().trim();
if (title.isEmpty()) {
etTitle.setError("Mangler tittel");
return;
}
TaskItem task = new TaskItem(title, etDesc.getText().toString(), dueDate.getTimeInMillis());
if (selectedUsers.isEmpty()) {
task.addAssignee(UserManager.getInstance().getUserEmail());
} else {
for (User u : selectedUsers) task.addAssignee(u.getEmail());
}
if (listener != null) listener.onTaskAdded(task);
dismiss();
}
}

View file

@ -92,4 +92,15 @@ public class CacheManager {
return null;
}
}
private static final String FILE_TASKS = "cache_tasks.json";
public static void saveTasks(Context context, List<TaskItem> tasks) {
saveList(context, FILE_TASKS, tasks);
}
public static List<TaskItem> getTasks(Context context) {
Type type = new TypeToken<List<TaskItem>>() {}.getType();
List<TaskItem> list = loadList(context, FILE_TASKS, type);
return list != null ? list : new ArrayList<>();
}
}

View file

@ -11,73 +11,92 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.ViewHolder> {
public class CalendarAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<CalendarEvent> events;
public static final int TYPE_EVENT = 0;
public static final int TYPE_YEAR_HEADER = 1;
private List<Object> items;
private final OnItemClickListener listener;
// Farge for individuelle/private hendelser (Deep Purple)
private static final String PRIVATE_EVENT_COLOR = "#673AB7";
public interface OnItemClickListener {
void onItemClick(CalendarEvent event);
}
public CalendarAdapter(List<CalendarEvent> events, OnItemClickListener listener) {
this.events = events;
public CalendarAdapter(List<Object> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
@Override
public int getItemViewType(int position) {
return (items.get(position) instanceof String) ? TYPE_YEAR_HEADER : TYPE_EVENT;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_calendar, parent, false);
return new ViewHolder(view);
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == TYPE_YEAR_HEADER) {
return new YearViewHolder(inflater.inflate(R.layout.item_calendar_year_header, parent, false));
} else {
return new EventViewHolder(inflater.inflate(R.layout.item_calendar, parent, false));
}
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
CalendarEvent event = events.get(position);
holder.day.setText(event.getDay());
holder.month.setText(event.getMonth());
holder.time.setText(event.getTime());
holder.title.setText(event.getTitle());
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Object item = items.get(position);
// NYTT: Sjekk om hendelsen er "privat" (har deltakere)
boolean isPrivate = event.getDescription() != null && event.getDescription().contains("#deltakere:");
try {
int color;
if (isPrivate) {
// Bruk privat farge
color = Color.parseColor(PRIVATE_EVENT_COLOR);
} else {
// Bruk kalenderens standardfarge
color = Color.parseColor(event.getCalendarColor());
}
holder.dateBox.setBackgroundTintList(ColorStateList.valueOf(color));
} catch (Exception e) {
// Fallback til standard blå hvis fargekoden er ugyldig
holder.dateBox.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#0069B3")));
if (holder instanceof YearViewHolder) {
((YearViewHolder) holder).yearText.setText((String) item);
}
else if (holder instanceof EventViewHolder) {
CalendarEvent event = (CalendarEvent) item;
EventViewHolder vh = (EventViewHolder) holder;
holder.itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onItemClick(event);
vh.day.setText(event.getDay());
vh.month.setText(event.getMonth());
vh.time.setText(event.getTime());
vh.title.setText(event.getTitle());
// --- ÅRSTALL LOGIKK: BAKGRUNNSFARGE ---
// Vi henter årstall fra datoen (yyyy-MM-dd)
String year = "2025";
if (event.getRawDate() != null && event.getRawDate().length() >= 4) {
year = event.getRawDate().substring(0, 4);
}
});
// Alternerende bakgrunn basert år (partall vs oddetall)
int yearInt = Integer.parseInt(year);
if (yearInt % 2 == 0) {
vh.itemView.setBackgroundColor(Color.parseColor("#F5F7FA")); // KBS Very Light Blue
} else {
vh.itemView.setBackgroundColor(Color.WHITE);
}
// Privat-markering og farge datoboks
boolean isPrivate = event.getDescription() != null && event.getDescription().contains("#deltakere:");
try {
int color = Color.parseColor(isPrivate ? "#673AB7" : event.getCalendarColor());
vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(color));
} catch (Exception e) {
vh.dateBox.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#0069B3")));
}
vh.itemView.setOnClickListener(v -> listener.onItemClick(event));
}
}
@Override
public int getItemCount() {
return events.size();
return items.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
static class EventViewHolder extends RecyclerView.ViewHolder {
TextView day, month, title, time;
LinearLayout dateBox;
public ViewHolder(View view) {
EventViewHolder(View view) {
super(view);
day = view.findViewById(R.id.cal_day);
month = view.findViewById(R.id.cal_month);
@ -86,4 +105,12 @@ public class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.ViewHo
dateBox = view.findViewById(R.id.date_box_background);
}
}
static class YearViewHolder extends RecyclerView.ViewHolder {
TextView yearText;
YearViewHolder(View view) {
super(view);
yearText = view.findViewById(R.id.txt_calendar_year);
}
}
}

View file

@ -6,7 +6,7 @@ import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
@ -15,11 +15,8 @@ import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@ -29,6 +26,7 @@ public class CalendarFullFragment extends Fragment {
private RecyclerView recyclerView;
private ProgressBar progressBar;
private TextView emptyView;
private Button btnAddEvent;
private LinearLayoutManager layoutManager;
@Nullable
@ -43,11 +41,17 @@ public class CalendarFullFragment extends Fragment {
recyclerView = view.findViewById(R.id.recycler_full_calendar);
progressBar = view.findViewById(R.id.loading_full_calendar);
emptyView = view.findViewById(R.id.empty_view_calendar);
ImageView backBtn = view.findViewById(R.id.btn_back_calendar);
btnAddEvent = view.findViewById(R.id.btn_add_calendar_event);
layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
backBtn.setOnClickListener(v -> Navigation.findNavController(view).navigateUp());
// VIKTIG: Ingen referanse til btn_back_calendar her lenger.
if (!UserManager.getInstance().getWriteableCalendars().isEmpty() || UserManager.getInstance().isEditorOrAbove()) {
btnAddEvent.setVisibility(View.VISIBLE);
btnAddEvent.setOnClickListener(v -> Navigation.findNavController(view).navigate(R.id.navigation_create_event));
}
}
@Override
@ -58,9 +62,7 @@ public class CalendarFullFragment extends Fragment {
private void fetchAllEvents() {
progressBar.setVisibility(View.VISIBLE);
new Thread(() -> {
// HER ER ENDRINGEN: isPreview = false (Hent alt)
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), false);
new Handler(Looper.getMainLooper()).post(() -> fetchApiEvents(deviceEvents));
}).start();
@ -76,65 +78,53 @@ public class CalendarFullFragment extends Fragment {
List<CalendarEvent> apiEvents = new ArrayList<>();
if (response.isSuccessful() && response.body() != null) {
apiEvents = response.body();
for (CalendarEvent e : apiEvents) {
CalendarManager.formatEventForUI(e);
}
for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
}
List<CalendarEvent> allEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
if (allEvents.isEmpty()) {
emptyView.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
CalendarAdapter adapter = new CalendarAdapter(allEvents, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(CalendarFullFragment.this::fetchAllEvents);
sheet.show(getParentFragmentManager(), "CalendarDetails");
});
recyclerView.setAdapter(adapter);
scrollToToday(allEvents);
}
displaySortedList(allEvents);
}
@Override
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
if (!isAdded()) return;
progressBar.setVisibility(View.GONE);
if (!deviceEvents.isEmpty()) {
CalendarAdapter adapter = new CalendarAdapter(deviceEvents, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.show(getParentFragmentManager(), "CalendarDetails");
});
recyclerView.setAdapter(adapter);
scrollToToday(deviceEvents);
} else {
emptyView.setText("Ingen hendelser funnet.");
emptyView.setVisibility(View.VISIBLE);
}
displaySortedList(deviceEvents);
}
});
}
private void scrollToToday(List<CalendarEvent> events) {
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
int scrollIndex = 0;
private void displaySortedList(List<CalendarEvent> events) {
if (events.isEmpty()) {
emptyView.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
return;
}
for (int i = 0; i < events.size(); i++) {
String raw = events.get(i).getRawDate();
if (raw != null && raw.compareTo(today) >= 0) {
scrollIndex = i;
break;
emptyView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
List<Object> itemsWithHeaders = new ArrayList<>();
String currentYear = "";
for (CalendarEvent e : events) {
String year = "Ukjent år";
if (e.getRawDate() != null && e.getRawDate().length() >= 4) {
year = e.getRawDate().substring(0, 4);
}
if (!year.equals(currentYear)) {
itemsWithHeaders.add(year);
currentYear = year;
}
itemsWithHeaders.add(e);
}
if (scrollIndex > 0) {
layoutManager.scrollToPositionWithOffset(scrollIndex, 0);
}
CalendarAdapter adapter = new CalendarAdapter(itemsWithHeaders, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(CalendarFullFragment.this::fetchAllEvents);
sheet.show(getParentFragmentManager(), "CalendarDetails");
});
recyclerView.setAdapter(adapter);
}
}

View file

@ -9,7 +9,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
@ -25,7 +24,6 @@ import java.util.ArrayList;
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;
@ -173,12 +171,7 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
private void buildAndDisplayList() {
List<Object> items = new ArrayList<>();
// 1. Create Event Knapp (hvis tilgang)
if (!UserManager.getInstance().getWriteableCalendars().isEmpty()) {
items.add(new HomeAdapter.CreateButtonItem());
}
// 2. Kalender Seksjon (Redusert til 3 hendelser som avtalt)
// 1. Kalender Seksjon (Knappen "Ny hendelse" er fjernet herfra)
items.add(new HomeAdapter.SectionTitleItem("Kommende hendelser", true));
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
int count = 0;
@ -190,7 +183,7 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
if (count >= 3) break;
}
// 3. Nyheter Seksjon
// 2. Nyheter Seksjon
items.add(new HomeAdapter.SectionTitleItem("Siste nytt", false));
items.addAll(currentNews);
@ -198,33 +191,24 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
recyclerView.setAdapter(adapter);
}
// --- KLIKKHÅNDTERING ---
@Override public void onProfileClick() {
Navigation.findNavController(getView()).navigate(R.id.navigation_profile);
}
@Override public void onProfileClick() {}
@Override public void onCreateEventClick() {
Navigation.findNavController(getView()).navigate(R.id.action_home_to_create_event);
Navigation.findNavController(getView()).navigate(R.id.navigation_create_event);
}
@Override public void onViewAllCalendarClick() {
Navigation.findNavController(getView()).navigate(R.id.action_home_to_calendarFull);
Navigation.findNavController(getView()).navigate(R.id.navigation_calendar_full);
}
@Override public void onViewAllNewsClick() {
Navigation.findNavController(getView()).navigate(R.id.action_home_to_newsFull);
Navigation.findNavController(getView()).navigate(R.id.navigation_news_full);
}
@Override public void onCalendarItemClick(CalendarEvent event) {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(this::refreshData);
sheet.show(getParentFragmentManager(), "CalendarDetails");
}
@Override public void onNewsItemClick(WpPost post) {
Bundle bundle = new Bundle();
bundle.putSerializable("post_data", post);
Navigation.findNavController(getView()).navigate(R.id.action_home_to_newsDetail, bundle);
Navigation.findNavController(getView()).navigate(R.id.navigation_news_detail, bundle);
}
}

View file

@ -26,6 +26,9 @@ import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
@ -33,15 +36,17 @@ import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.material.navigation.NavigationView;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
// Din oppdaterte linje:
public static final String GOOGLE_WEB_CLIENT_ID = BuildConfig.WEB_CLIENT_ID;
private static final String TAG = "MainActivity";
private static final String TAG = "MainActivity";
private NavController navController;
private DrawerLayout drawerLayout;
private AppBarConfiguration appBarConfiguration;
private ActivityResultLauncher<String> requestPermissionLauncher;
@Override
@ -77,13 +82,17 @@ public class MainActivity extends AppCompatActivity {
} else {
toolbar.setVisibility(View.VISIBLE);
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
// Oppdater navn hver gang vi bytter skjerm for å være sikker
updateNavHeader(navigationView);
}
});
}
updateNavHeader(navigationView);
setupTaskReminders();
createNotificationChannel();
requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {});
checkNotificationPermission();
checkExactAlarmPermission();
checkLoginState();
@ -94,13 +103,18 @@ public class MainActivity extends AppCompatActivity {
TextView name = headerView.findViewById(R.id.nav_header_name);
TextView email = headerView.findViewById(R.id.nav_header_email);
new android.os.Handler().postDelayed(() -> {
UserManager user = UserManager.getInstance();
if (user.isLoggedIn()) {
name.setText(user.getUserDisplayName());
email.setText(user.getUserEmail());
}
}, 2000);
UserManager user = UserManager.getInstance();
if (user.isLoggedIn()) {
name.setText(user.getUserDisplayName());
email.setText(user.getUserEmail());
}
}
private void setupTaskReminders() {
PeriodicWorkRequest taskCheck = new PeriodicWorkRequest.Builder(
TaskReminderWorker.class, 2, TimeUnit.DAYS).build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"TaskReminder", ExistingPeriodicWorkPolicy.KEEP, taskCheck);
}
@Override
@ -128,8 +142,8 @@ public class MainActivity extends AppCompatActivity {
.requestIdToken(GOOGLE_WEB_CLIENT_ID).requestEmail().build();
GoogleSignInClient client = GoogleSignIn.getClient(this, gso);
client.silentSignIn().addOnSuccessListener(account -> {
AuthRepository.loginToWordPress(account.getIdToken(), account.getDisplayName(), account.getEmail(),
(account.getPhotoUrl() != null ? account.getPhotoUrl().toString() : null),
String photoUrl = (account.getPhotoUrl() != null) ? account.getPhotoUrl().toString() : null;
AuthRepository.loginToWordPress(account.getIdToken(), account.getDisplayName(), account.getEmail(), photoUrl,
new AuthRepository.AuthCallback() {
@Override public void onSuccess(String role) {
if (navController.getCurrentDestination().getId() == R.id.navigation_login) {
@ -142,22 +156,16 @@ public class MainActivity extends AppCompatActivity {
}
private void navigateToLogin() {
if (navController.getCurrentDestination().getId() != R.id.navigation_login) {
if (navController != null && navController.getCurrentDestination() != null &&
navController.getCurrentDestination().getId() != R.id.navigation_login) {
navController.navigate(R.id.navigation_login);
}
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "KBS Kalendervarsler";
String description = "Varsler for kalenderhendelser";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel("kbs_calendar_channel", name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
NotificationChannel channel = new NotificationChannel("kbs_calendar_channel", "Varsler", NotificationManager.IMPORTANCE_HIGH);
getSystemService(NotificationManager.class).createNotificationChannel(channel);
}
}

View file

@ -0,0 +1,51 @@
package com.kbs.kbsintranett;
import android.Manifest;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
public class NotificationHelper {
private static final String CHANNEL_ID = "kbs_calendar_channel";
/**
* En enkel metode for å vise et standard KBS-varsel.
*/
public static void showNotification(Context context, String title, String message) {
// Sjekk tillatelse for Android 13+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
return;
}
}
// Intent for å åpne appen når man trykker varselet
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
(int) System.currentTimeMillis(),
intent,
PendingIntent.FLAG_IMMUTABLE
);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_kbs) // Bruker samme ikon som kalender
.setColor(ContextCompat.getColor(context, R.color.kbs_logo_blue))
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify((int) System.currentTimeMillis(), builder.build());
}
}

View file

@ -0,0 +1,84 @@
package com.kbs.kbsintranett;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {
private List<TaskItem> tasks;
private OnTaskClickListener listener;
private String currentUserEmail;
public interface OnTaskClickListener {
void onTaskClick(TaskItem task);
void onStatusChanged(TaskItem task, boolean isDone);
}
public TaskAdapter(List<TaskItem> tasks, OnTaskClickListener listener) {
this.tasks = tasks;
this.listener = listener;
this.currentUserEmail = UserManager.getInstance().getUserEmail();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_task, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
TaskItem task = tasks.get(position);
holder.title.setText(task.getTitle());
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
holder.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
boolean myStatus = task.getParticipantStatus(currentUserEmail);
holder.checkBox.setChecked(myStatus);
if (myStatus || task.isFullyCompleted()) {
holder.title.setPaintFlags(holder.title.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.title.setTextColor(Color.GRAY);
} else {
holder.title.setPaintFlags(holder.title.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
holder.title.setTextColor(Color.BLACK);
}
// Vis fremdrift (f.eks 1/3 fullført)
int total = task.getAssigneeStatus().size();
int done = 0;
for (Boolean b : task.getAssigneeStatus().values()) if (b) done++;
holder.progress.setText("Fremdrift: " + done + "/" + total);
holder.checkBox.setOnClickListener(v -> listener.onStatusChanged(task, holder.checkBox.isChecked()));
holder.itemView.setOnClickListener(v -> listener.onTaskClick(task));
}
@Override
public int getItemCount() { return tasks.size(); }
static class ViewHolder extends RecyclerView.ViewHolder {
TextView title, date, progress;
CheckBox checkBox;
ViewHolder(View v) {
super(v);
title = v.findViewById(R.id.task_title);
date = v.findViewById(R.id.task_date);
progress = v.findViewById(R.id.task_progress);
checkBox = v.findViewById(R.id.task_checkbox);
}
}
}

View file

@ -0,0 +1,82 @@
package com.kbs.kbsintranett;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.switchmaterial.SwitchMaterial;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
private TaskItem task;
private OnTaskChangeListener listener;
public interface OnTaskChangeListener {
void onTaskChanged();
void onTaskDeleted(TaskItem task);
}
public TaskDetailsBottomSheet(TaskItem task, OnTaskChangeListener listener) {
this.task = task;
this.listener = listener;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bottom_sheet_task_details, container, false);
TextView title = v.findViewById(R.id.detail_task_title);
TextView date = v.findViewById(R.id.detail_task_date);
TextView desc = v.findViewById(R.id.detail_task_desc);
LinearLayout participantsContainer = v.findViewById(R.id.container_participants_status);
SwitchMaterial switchNotify = v.findViewById(R.id.switch_notifications);
Button btnDelete = v.findViewById(R.id.btn_delete_task);
title.setText(task.getTitle());
SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd. MMMM yyyy", Locale.getDefault());
date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
if (task.getDescription() != null && !task.getDescription().isEmpty()) {
desc.setText(task.getDescription());
desc.setVisibility(View.VISIBLE);
}
// List opp alle deltakere og deres status
for (Map.Entry<String, Boolean> entry : task.getAssigneeStatus().entrySet()) {
TextView t = new TextView(getContext());
String status = entry.getValue() ? "✅ Fullført" : "⏳ Pågår";
t.setText("" + entry.getKey() + ": " + status);
t.setPadding(0, 4, 0, 4);
participantsContainer.addView(t);
}
switchNotify.setChecked(task.isNotificationsEnabled());
switchNotify.setOnCheckedChangeListener((btn, isChecked) -> {
task.setNotificationsEnabled(isChecked);
if (listener != null) listener.onTaskChanged();
});
// Kun eier kan slette oppgaven
if (task.getCreatedByEmail().equals(UserManager.getInstance().getUserEmail())) {
btnDelete.setVisibility(View.VISIBLE);
btnDelete.setOnClickListener(view -> {
if (listener != null) listener.onTaskDeleted(task);
dismiss();
});
}
return v;
}
}

View file

@ -1,6 +1,8 @@
package com.kbs.kbsintranett;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class TaskItem implements Serializable {
@ -8,26 +10,41 @@ public class TaskItem implements Serializable {
private String title;
private String description;
private long dueDate;
private boolean isCompleted;
private String assignedToEmail;
private String createdByEmail;
private String createdByName;
public TaskItem(String title, String description, long dueDate, String assignedToEmail) {
// Key: Bruker-epost, Value: Har fullført (true/false)
private Map<String, Boolean> assigneeStatus = new HashMap<>();
private boolean notificationsEnabled = true;
private boolean isFullyCompleted = false; // Satt av eier eller når alle er ferdige
public TaskItem(String title, String description, long dueDate) {
this.id = UUID.randomUUID().toString();
this.title = title;
this.description = description;
this.dueDate = dueDate;
this.assignedToEmail = assignedToEmail;
this.createdByEmail = UserManager.getInstance().getUserEmail();
this.isCompleted = false;
this.createdByName = UserManager.getInstance().getUserDisplayName();
}
// Getters og Setters
// Hjelpemetoder
public void addAssignee(String email) { assigneeStatus.put(email, false); }
public void setParticipantStatus(String email, boolean done) { assigneeStatus.put(email, done); }
public boolean getParticipantStatus(String email) { return assigneeStatus.getOrDefault(email, false); }
public boolean isUserParticipant(String email) { return assigneeStatus.containsKey(email); }
public Map<String, Boolean> getAssigneeStatus() { return assigneeStatus; }
// Getters / Setters
public String getId() { return id; }
public String getTitle() { return title; }
public String getDescription() { return description; }
public long getDueDate() { return dueDate; }
public boolean isCompleted() { return isCompleted; }
public void setCompleted(boolean completed) { isCompleted = completed; }
public String getAssignedToEmail() { return assignedToEmail; }
public String getCreatedByEmail() { return createdByEmail; }
public String getCreatedByName() { return createdByName; }
public boolean isNotificationsEnabled() { return notificationsEnabled; }
public void setNotificationsEnabled(boolean enabled) { this.notificationsEnabled = enabled; }
public boolean isFullyCompleted() { return isFullyCompleted; }
public void setFullyCompleted(boolean fullyCompleted) { isFullyCompleted = fullyCompleted; }
}

View file

@ -0,0 +1,39 @@
package com.kbs.kbsintranett;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import java.util.List;
public class TaskReminderWorker extends Worker {
public TaskReminderWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
Context context = getApplicationContext();
List<TaskItem> tasks = CacheManager.getTasks(context);
String myEmail = UserManager.getInstance().getUserEmail();
long now = System.currentTimeMillis();
for (TaskItem task : tasks) {
if (!task.isNotificationsEnabled() || task.isFullyCompleted() || task.getParticipantStatus(myEmail)) continue;
long diff = task.getDueDate() - now;
// Varsle hvis fristen er i dag (innenfor 24 timer)
if (diff > 0 && diff < 86400000L) {
NotificationHelper.showNotification(context, "Frist i dag!", "Oppgaven '" + task.getTitle() + "' forfaller snart.");
}
// Varsle hvis over frist (hver gang worker kjører, f.eks annenhver dag)
else if (diff < 0) {
NotificationHelper.showNotification(context, "Over frist!", "Oppgaven '" + task.getTitle() + "' er forsinket.");
}
}
return Result.success();
}
}

View file

@ -4,20 +4,23 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.List;
public class TasksFragment extends Fragment {
public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickListener {
private RecyclerView recyclerView;
private List<TaskItem> taskList = new ArrayList<>();
private TaskAdapter adapter;
private TabLayout tabLayout;
private List<TaskItem> allTasks = new ArrayList<>();
private String myEmail;
@Nullable
@Override
@ -29,21 +32,91 @@ public class TasksFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
myEmail = UserManager.getInstance().getUserEmail();
tabLayout = view.findViewById(R.id.task_tabs);
recyclerView = view.findViewById(R.id.recycler_tasks);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
FloatingActionButton fab = view.findViewById(R.id.fab_add_task);
fab.setOnClickListener(v -> {
Toast.makeText(getContext(), "Ny oppgave-funksjon kommer her", Toast.LENGTH_SHORT).show();
AddTaskBottomSheet dialog = new AddTaskBottomSheet();
dialog.setOnTaskAddedListener(task -> {
allTasks.add(0, task);
saveAndRefresh();
});
dialog.show(getChildFragmentManager(), "AddTask");
});
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override public void onTabSelected(TabLayout.Tab tab) { filterAndDisplay(); }
@Override public void onTabUnselected(TabLayout.Tab tab) {}
@Override public void onTabReselected(TabLayout.Tab tab) {}
});
loadTasks();
}
private void loadTasks() {
// Dummy data
taskList.clear();
taskList.add(new TaskItem("Husk å sjekke filter", "Serviceoppdrag hos kunde X", System.currentTimeMillis(), "meg@kbs.no"));
// Her vil vi senere koble en TaskAdapter
allTasks = CacheManager.getTasks(getContext());
filterAndDisplay();
}
private void filterAndDisplay() {
List<TaskItem> filtered = new ArrayList<>();
int selectedTab = tabLayout.getSelectedTabPosition();
for (TaskItem t : allTasks) {
if (selectedTab == 0) { // MINE (Oppgaver tildelt meg som ikke er fullført av meg ennå)
if (t.isUserParticipant(myEmail) && !t.getParticipantStatus(myEmail) && !t.isFullyCompleted()) {
filtered.add(t);
}
} else if (selectedTab == 1) { // TILDELT ANDRE (Oppgaver jeg har laget, uansett om jeg er med selv)
if (t.getCreatedByEmail().equals(myEmail) && !t.isFullyCompleted()) {
filtered.add(t);
}
} else { // FULLFØRTE (Alt som er arkivert som ferdig)
if (t.isFullyCompleted() || (t.isUserParticipant(myEmail) && t.getParticipantStatus(myEmail))) {
filtered.add(t);
}
}
}
adapter = new TaskAdapter(filtered, this);
recyclerView.setAdapter(adapter);
}
@Override
public void onTaskClick(TaskItem task) {
TaskDetailsBottomSheet sheet = new TaskDetailsBottomSheet(task, new TaskDetailsBottomSheet.OnTaskChangeListener() {
@Override
public void onTaskChanged() {
saveAndRefresh();
}
@Override
public void onTaskDeleted(TaskItem taskToDelete) {
allTasks.remove(taskToDelete);
saveAndRefresh();
}
});
sheet.show(getChildFragmentManager(), "TaskDetails");
}
@Override
public void onStatusChanged(TaskItem task, boolean isDone) {
task.setParticipantStatus(myEmail, isDone);
// Logikk: Hvis den som opprettet oppgaven merker den som ferdig,
// regnes hele oppgaven som ferdig for alle (arkiveres).
if (task.getCreatedByEmail().equals(myEmail) && isDone) {
task.setFullyCompleted(true);
}
saveAndRefresh();
}
private void saveAndRefresh() {
CacheManager.saveTasks(getContext(), allTasks);
filterAndDisplay();
}
}

View file

@ -0,0 +1,84 @@
<?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="wrap_content"
android:orientation="vertical"
android:padding="24dp"
android:background="@color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ny Oppgave"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_marginBottom="16dp"/>
<EditText
android:id="@+id/et_task_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Hva skal gjøres?"
android:inputType="textCapSentences"
android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/et_task_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Beskrivelse (valgfritt)"
android:inputType="textMultiLine"
android:minLines="2"
android:gravity="top"
android:layout_marginBottom="16dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="12dp">
<Button
android:id="@+id/btn_task_date"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sett frist" />
<TextView
android:id="@+id/txt_date_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Frist: -"
android:layout_marginStart="8dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="24dp">
<Button
android:id="@+id/btn_task_users"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Velg deltakere" />
<TextView
android:id="@+id/txt_users_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Kun meg"
android:layout_marginStart="8dp"/>
</LinearLayout>
<Button
android:id="@+id/btn_save_task"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Lagre Oppgave"
android:backgroundTint="@color/kbs_logo_blue"
android:textColor="@color/white"/>
</LinearLayout>

View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp"
android:background="@color/white">
<TextView
android:id="@+id/detail_task_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Oppgavetittel"
android:textSize="22sp"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_marginBottom="8dp"/>
<TextView
android:id="@+id/detail_task_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Frist: -"
android:textSize="14sp"
android:textColor="@color/kbs_muted_blue_gray"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/detail_task_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Beskrivelse kommer her..."
android:textSize="16sp"
android:textColor="#333"
android:layout_marginBottom="24dp"
android:visibility="gone"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#EEE"
android:layout_marginBottom="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Status deltakere:"
android:textStyle="bold"
android:textSize="14sp"
android:layout_marginBottom="8dp"/>
<LinearLayout
android:id="@+id/container_participants_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="24dp"/>
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switch_notifications"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Varsle meg om denne oppgaven"
android:checked="true"
android:layout_marginBottom="24dp"/>
<Button
android:id="@+id/btn_delete_task"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Slett oppgave"
android:backgroundTint="@color/kbs_logo_accent_red"
android:textColor="@color/white"
android:visibility="gone"/>
</LinearLayout>

View file

@ -4,32 +4,18 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/kbs_very_light_blue">
android:background="@color/white">
<RelativeLayout
<!-- Knappen ligger nå helt øverst i fragmentet (under den globale toolbaren) -->
<Button
android:id="@+id/btn_add_calendar_event"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/white"
android:elevation="4dp">
<ImageView
android:id="@+id/btn_back_calendar"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@android:drawable/ic_menu_revert"
android:layout_centerVertical="true"
app:tint="@color/kbs_logo_blue"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="KBS Kalender"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_centerInParent="true"/>
</RelativeLayout>
android:text="+ Ny Kalenderhendelse"
android:backgroundTint="@color/kbs_logo_blue"
android:textColor="@color/white"
android:layout_margin="16dp"
android:visibility="gone"/>
<FrameLayout
android:layout_width="match_parent"
@ -39,8 +25,7 @@
android:id="@+id/recycler_full_calendar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:clipToPadding="false"/>
android:clipToPadding="false" />
<ProgressBar
android:id="@+id/loading_full_calendar"

View file

@ -7,16 +7,6 @@
android:padding="16dp"
android:background="#F5F5F5">
<TextView
android:id="@+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Skjemaer"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="20dp"
android:textColor="#333333"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@ -13,19 +14,10 @@
android:background="@color/white"
android:elevation="4dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Håndbok"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#333333"
android:layout_marginBottom="12dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="KBS Klima og Byggservice ønsker å ta vare på miljø og helse, og har derfor utarbeidet en håndbok som omhandler disse temaene. For enkelthets skyld er denne publisert i KBS-appen og på Intranettet.\n\nDet forventes at alle ansatte skal ha lest hele håndboken minst én gang, og at de har lest og forstått bedriftens HMS-målsetting senest 1. april hvert år."
android:text="KBS Klima og Byggservice ønsker å ta vare på miljø og helse, og har derfor utarbeidet en håndbok som omhandler disse temaene.\n\nDet forventes at alle ansatte skal ha lest hele håndboken minst én gang, og at de har lest og forstått bedriftens HMS-målsetting senest 1. april hvert år."
android:textSize="14sp"
android:textColor="#666666"
android:lineSpacingExtra="4dp"

View file

@ -1,14 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/kbs_very_light_blue">
<com.google.android.material.tabs.TabLayout
android:id="@+id/task_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:tabSelectedTextColor="@color/kbs_logo_blue"
app:tabIndicatorColor="@color/kbs_logo_blue">
<com.google.android.material.tabs.TabItem android:text="Mine" />
<com.google.android.material.tabs.TabItem android:text="Tildelt andre" />
<com.google.android.material.tabs.TabItem android:text="Fullførte" />
</com.google.android.material.tabs.TabLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_tasks"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="8dp"
android:clipToPadding="false" />
@ -16,11 +30,10 @@
android:id="@+id/fab_add_task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_gravity="end"
android:layout_margin="24dp"
android:src="@android:drawable/ic_input_add"
app:backgroundTint="@color/kbs_logo_blue"
app:tint="@color/white"
android:contentDescription="Legg til oppgave" />
app:tint="@color/white" />
</FrameLayout>
</LinearLayout>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/txt_calendar_year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#4F5B66"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"
android:text="2026"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />

View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="12dp"
android:gravity="center_vertical">
<CheckBox
android:id="@+id/task_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/task_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Oppgavetittel"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@color/black" />
<TextView
android:id="@+id/task_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Frist: -"
android:textSize="12sp"
android:textColor="@color/kbs_muted_blue_gray" />
<TextView
android:id="@+id/task_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Fremdrift: 0/0"
android:textSize="11sp"
android:textStyle="italic"
android:textColor="@color/kbs_logo_blue"
android:layout_marginTop="2dp"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>

View file

@ -41,18 +41,18 @@
app:destination="@id/navigation_tasks" />
</fragment>
<!-- OPPGAVER (NYTT) -->
<!-- OPPGAVER (Navn endret for Toolbar) -->
<fragment
android:id="@+id/navigation_tasks"
android:name="com.kbs.kbsintranett.TasksFragment"
android:label="Oppgaver"
android:label="KBS Oppgaver"
tools:layout="@layout/fragment_tasks" />
<!-- KALENDER -->
<!-- KALENDER (Navn endret for Toolbar) -->
<fragment
android:id="@+id/navigation_calendar_full"
android:name="com.kbs.kbsintranett.CalendarFullFragment"
android:label="Kalender"
android:label="KBS Kalender"
tools:layout="@layout/fragment_calendar_full" />
<fragment
@ -65,7 +65,7 @@
<fragment
android:id="@+id/navigation_news_full"
android:name="com.kbs.kbsintranett.NewsFullFragment"
android:label="Nyheter"
android:label="Siste nytt"
tools:layout="@layout/fragment_news_full">
<action
android:id="@+id/action_newsFull_to_newsDetail"
@ -82,7 +82,7 @@
<fragment
android:id="@+id/navigation_forms"
android:name="com.kbs.kbsintranett.FormsListFragment"
android:label="Skjemaer"
android:label="KBS Skjemaer"
tools:layout="@layout/fragment_forms_list">
<action
android:id="@+id/action_formsListFragment_to_formsDetailFragment"
@ -104,7 +104,7 @@
<fragment
android:id="@+id/navigation_handbook"
android:name="com.kbs.kbsintranett.HandbookFragment"
android:label="Håndbok"
android:label="KBS Håndbok"
tools:layout="@layout/fragment_handbook">
<action
android:id="@+id/action_handbook_to_detail"