Før endringer av strømforbruket V2
This commit is contained in:
parent
f7eeba2037
commit
e89f7a3522
11 changed files with 519 additions and 164 deletions
|
|
@ -30,10 +30,12 @@ import retrofit2.Response;
|
||||||
public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
||||||
|
|
||||||
private EditText etTitle, etDesc;
|
private EditText etTitle, etDesc;
|
||||||
private Button btnDate, btnUsers, btnSave;
|
private Button btnDate, btnUsers, btnSave, btnClearDate; // NYTT
|
||||||
private TextView txtSheetTitle, txtDatePreview, txtUsersPreview;
|
private TextView txtSheetTitle, txtDatePreview, txtUsersPreview;
|
||||||
|
|
||||||
private Calendar dueDate = Calendar.getInstance();
|
private Calendar dueDate = Calendar.getInstance();
|
||||||
|
private boolean hasDate = true; // NYTT
|
||||||
|
|
||||||
private List<User> filteredUsers = new ArrayList<>();
|
private List<User> filteredUsers = new ArrayList<>();
|
||||||
private List<User> selectedUsers = new ArrayList<>();
|
private List<User> selectedUsers = new ArrayList<>();
|
||||||
private TaskItem taskToEdit = null;
|
private TaskItem taskToEdit = null;
|
||||||
|
|
@ -77,6 +79,7 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
||||||
etTitle = v.findViewById(R.id.et_task_title);
|
etTitle = v.findViewById(R.id.et_task_title);
|
||||||
etDesc = v.findViewById(R.id.et_task_desc);
|
etDesc = v.findViewById(R.id.et_task_desc);
|
||||||
btnDate = v.findViewById(R.id.btn_task_date);
|
btnDate = v.findViewById(R.id.btn_task_date);
|
||||||
|
btnClearDate = v.findViewById(R.id.btn_clear_date); // NYTT
|
||||||
btnUsers = v.findViewById(R.id.btn_task_users);
|
btnUsers = v.findViewById(R.id.btn_task_users);
|
||||||
btnSave = v.findViewById(R.id.btn_save_task);
|
btnSave = v.findViewById(R.id.btn_save_task);
|
||||||
txtDatePreview = v.findViewById(R.id.txt_date_preview);
|
txtDatePreview = v.findViewById(R.id.txt_date_preview);
|
||||||
|
|
@ -86,10 +89,17 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
||||||
txtSheetTitle.setText("Rediger Oppgave");
|
txtSheetTitle.setText("Rediger Oppgave");
|
||||||
etTitle.setText(taskToEdit.getTitle());
|
etTitle.setText(taskToEdit.getTitle());
|
||||||
etDesc.setText(taskToEdit.getDescription());
|
etDesc.setText(taskToEdit.getDescription());
|
||||||
dueDate.setTimeInMillis(taskToEdit.getDueDate());
|
|
||||||
|
if (taskToEdit.getDueDate() > 0) {
|
||||||
|
dueDate.setTimeInMillis(taskToEdit.getDueDate());
|
||||||
|
hasDate = true;
|
||||||
|
} else {
|
||||||
|
hasDate = false;
|
||||||
|
}
|
||||||
btnSave.setText("Oppdater Oppgave");
|
btnSave.setText("Oppdater Oppgave");
|
||||||
} else {
|
} else {
|
||||||
dueDate.add(Calendar.DAY_OF_MONTH, 1);
|
dueDate.add(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
hasDate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDatePreview();
|
updateDatePreview();
|
||||||
|
|
@ -97,10 +107,17 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
||||||
btnDate.setOnClickListener(view -> {
|
btnDate.setOnClickListener(view -> {
|
||||||
new DatePickerDialog(getContext(), (d, y, m, day) -> {
|
new DatePickerDialog(getContext(), (d, y, m, day) -> {
|
||||||
dueDate.set(y, m, day);
|
dueDate.set(y, m, day);
|
||||||
|
hasDate = true;
|
||||||
updateDatePreview();
|
updateDatePreview();
|
||||||
}, dueDate.get(Calendar.YEAR), dueDate.get(Calendar.MONTH), dueDate.get(Calendar.DAY_OF_MONTH)).show();
|
}, dueDate.get(Calendar.YEAR), dueDate.get(Calendar.MONTH), dueDate.get(Calendar.DAY_OF_MONTH)).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NYTT: Knapp for å fjerne frist
|
||||||
|
btnClearDate.setOnClickListener(v1 -> {
|
||||||
|
hasDate = false;
|
||||||
|
updateDatePreview();
|
||||||
|
});
|
||||||
|
|
||||||
btnUsers.setOnClickListener(view -> showUserSelectionDialog());
|
btnUsers.setOnClickListener(view -> showUserSelectionDialog());
|
||||||
btnSave.setOnClickListener(view -> saveTask());
|
btnSave.setOnClickListener(view -> saveTask());
|
||||||
|
|
||||||
|
|
@ -114,7 +131,6 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
|
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
// BRUKER HJELPEKLASSEN HER:
|
|
||||||
filteredUsers = UserFilterHelper.getFilteredUsers(response.body());
|
filteredUsers = UserFilterHelper.getFilteredUsers(response.body());
|
||||||
|
|
||||||
if (taskToEdit != null) {
|
if (taskToEdit != null) {
|
||||||
|
|
@ -173,8 +189,14 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDatePreview() {
|
private void updateDatePreview() {
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
|
if (hasDate) {
|
||||||
txtDatePreview.setText("Frist: " + sdf.format(dueDate.getTime()));
|
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
|
||||||
|
txtDatePreview.setText("Frist: " + sdf.format(dueDate.getTime()));
|
||||||
|
btnClearDate.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
txtDatePreview.setText("Ingen frist");
|
||||||
|
btnClearDate.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveTask() {
|
private void saveTask() {
|
||||||
|
|
@ -184,6 +206,8 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long finalDueDate = hasDate ? dueDate.getTimeInMillis() : 0;
|
||||||
|
|
||||||
if (taskToEdit != null) {
|
if (taskToEdit != null) {
|
||||||
Map<String, Boolean> oldStatus = taskToEdit.getAssigneeStatus();
|
Map<String, Boolean> oldStatus = taskToEdit.getAssigneeStatus();
|
||||||
taskToEdit.getAssigneeStatus().clear();
|
taskToEdit.getAssigneeStatus().clear();
|
||||||
|
|
@ -202,10 +226,10 @@ public class AddTaskBottomSheet extends BottomSheetDialogFragment {
|
||||||
}
|
}
|
||||||
taskToEdit.setTitle(title);
|
taskToEdit.setTitle(title);
|
||||||
taskToEdit.setDescription(etDesc.getText().toString());
|
taskToEdit.setDescription(etDesc.getText().toString());
|
||||||
taskToEdit.setDueDate(dueDate.getTimeInMillis());
|
taskToEdit.setDueDate(finalDueDate);
|
||||||
if (listener != null) listener.onTaskUpdated(taskToEdit);
|
if (listener != null) listener.onTaskUpdated(taskToEdit);
|
||||||
} else {
|
} else {
|
||||||
TaskItem newTask = new TaskItem(title, etDesc.getText().toString(), dueDate.getTimeInMillis());
|
TaskItem newTask = new TaskItem(title, etDesc.getText().toString(), finalDueDate);
|
||||||
if (selectedUsers.isEmpty()) {
|
if (selectedUsers.isEmpty()) {
|
||||||
newTask.addAssignee(UserManager.getInstance().getUserEmail());
|
newTask.addAssignee(UserManager.getInstance().getUserEmail());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.kbs.kbsintranett;
|
package com.kbs.kbsintranett;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences; // NYTT
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
@ -17,13 +18,18 @@ public class CacheManager {
|
||||||
private static final String FILE_CALENDAR = "cache_calendar.json";
|
private static final String FILE_CALENDAR = "cache_calendar.json";
|
||||||
private static final String FILE_NEWS = "cache_news.json";
|
private static final String FILE_NEWS = "cache_news.json";
|
||||||
private static final String FILE_HANDBOOK = "cache_handbook.json";
|
private static final String FILE_HANDBOOK = "cache_handbook.json";
|
||||||
|
private static final String FILE_TASKS = "cache_tasks.json";
|
||||||
|
|
||||||
private static final String TAG = "CacheManager";
|
private static final String TAG = "CacheManager";
|
||||||
private static final Gson gson = new Gson();
|
private static final Gson gson = new Gson();
|
||||||
|
|
||||||
|
// NYTT: SharedPreferences for tidsstempler
|
||||||
|
private static final String PREFS_CACHE = "kbs_cache_prefs";
|
||||||
|
|
||||||
// --- KALENDER ---
|
// --- KALENDER ---
|
||||||
public static void saveCalendarEvents(Context context, List<CalendarEvent> events) {
|
public static void saveCalendarEvents(Context context, List<CalendarEvent> events) {
|
||||||
saveList(context, FILE_CALENDAR, events);
|
saveList(context, FILE_CALENDAR, events);
|
||||||
|
setLastFetchTime(context, "calendar"); // NYTT
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<CalendarEvent> getCachedCalendarEvents(Context context) {
|
public static List<CalendarEvent> getCachedCalendarEvents(Context context) {
|
||||||
|
|
@ -35,6 +41,7 @@ public class CacheManager {
|
||||||
// --- NYHETER ---
|
// --- NYHETER ---
|
||||||
public static void saveNewsPosts(Context context, List<WpPost> posts) {
|
public static void saveNewsPosts(Context context, List<WpPost> posts) {
|
||||||
saveList(context, FILE_NEWS, posts);
|
saveList(context, FILE_NEWS, posts);
|
||||||
|
setLastFetchTime(context, "news"); // NYTT
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<WpPost> getCachedNewsPosts(Context context) {
|
public static List<WpPost> getCachedNewsPosts(Context context) {
|
||||||
|
|
@ -46,6 +53,7 @@ public class CacheManager {
|
||||||
// --- HÅNDBOK ---
|
// --- HÅNDBOK ---
|
||||||
public static void saveHandbookItems(Context context, List<HandbookItem> items) {
|
public static void saveHandbookItems(Context context, List<HandbookItem> items) {
|
||||||
saveList(context, FILE_HANDBOOK, items);
|
saveList(context, FILE_HANDBOOK, items);
|
||||||
|
// Håndboken endres sjelden, trenger kanskje ikke tidssjekk, men greit å ha
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<HandbookItem> getCachedHandbookItems(Context context) {
|
public static List<HandbookItem> getCachedHandbookItems(Context context) {
|
||||||
|
|
@ -54,7 +62,47 @@ public class CacheManager {
|
||||||
return list != null ? list : new ArrayList<>();
|
return list != null ? list : new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- GENERISKE HJELPEMETODER ---
|
// --- OPPGAVER ---
|
||||||
|
public static void saveTasks(Context context, List<TaskItem> tasks) {
|
||||||
|
saveList(context, FILE_TASKS, tasks);
|
||||||
|
setLastFetchTime(context, "tasks"); // NYTT
|
||||||
|
}
|
||||||
|
|
||||||
|
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<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- LOGIKK FOR TID ---
|
||||||
|
|
||||||
|
private static void setLastFetchTime(Context context, String key) {
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_CACHE, Context.MODE_PRIVATE);
|
||||||
|
prefs.edit().putLong(key + "_timestamp", System.currentTimeMillis()).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sjekker om cachen er gyldig.
|
||||||
|
* @param maxAgeMinutes Hvor gammel cachen kan være før vi henter på nytt (f.eks. 60 minutter).
|
||||||
|
* Hvis push fungerer, kan vi sette denne høyt!
|
||||||
|
*/
|
||||||
|
public static boolean isCacheValid(Context context, String key, int maxAgeMinutes) {
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_CACHE, Context.MODE_PRIVATE);
|
||||||
|
long lastTime = prefs.getLong(key + "_timestamp", 0);
|
||||||
|
long diff = System.currentTimeMillis() - lastTime;
|
||||||
|
// Konverter minutter til millisekunder
|
||||||
|
long maxDiff = maxAgeMinutes * 60 * 1000L;
|
||||||
|
|
||||||
|
return diff < maxDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metode for å tvinge oppdatering neste gang (brukes av FCM)
|
||||||
|
public static void invalidateCache(Context context, String key) {
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_CACHE, Context.MODE_PRIVATE);
|
||||||
|
prefs.edit().remove(key + "_timestamp").apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- GENERISKE HJELPEMETODER (Uendret) ---
|
||||||
|
|
||||||
private static <T> void saveList(Context context, String filename, List<T> list) {
|
private static <T> void saveList(Context context, String filename, List<T> list) {
|
||||||
if (context == null || list == null) return;
|
if (context == null || list == null) return;
|
||||||
|
|
@ -64,7 +112,6 @@ public class CacheManager {
|
||||||
FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
|
FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
|
||||||
fos.write(json.getBytes());
|
fos.write(json.getBytes());
|
||||||
fos.close();
|
fos.close();
|
||||||
Log.d(TAG, "Lagret cache til " + filename);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Feil ved lagring av cache: " + filename, e);
|
Log.e(TAG, "Feil ved lagring av cache: " + filename, e);
|
||||||
}
|
}
|
||||||
|
|
@ -92,15 +139,4 @@ public class CacheManager {
|
||||||
return null;
|
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<>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -121,15 +121,21 @@ public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
TaskItem task = (TaskItem) item;
|
TaskItem task = (TaskItem) item;
|
||||||
TaskViewHolder vh = (TaskViewHolder) holder;
|
TaskViewHolder vh = (TaskViewHolder) holder;
|
||||||
vh.title.setText(task.getTitle());
|
vh.title.setText(task.getTitle());
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
|
|
||||||
vh.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
|
// FIKS: Vis "Ingen frist" hvis dato er 0
|
||||||
|
if (task.getDueDate() > 0) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
|
||||||
|
vh.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
|
||||||
|
} else {
|
||||||
|
vh.date.setText("Ingen frist");
|
||||||
|
}
|
||||||
|
|
||||||
String myEmail = UserManager.getInstance().getUserEmail();
|
String myEmail = UserManager.getInstance().getUserEmail();
|
||||||
boolean myStatus = task.getParticipantStatus(myEmail);
|
boolean myStatus = task.getParticipantStatus(myEmail);
|
||||||
vh.checkBox.setChecked(myStatus);
|
vh.checkBox.setChecked(myStatus);
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
boolean isOverdue = task.getDueDate() < now && !task.isFullyCompleted() && !myStatus;
|
boolean isOverdue = task.getDueDate() > 0 && task.getDueDate() < now && !task.isFullyCompleted() && !myStatus;
|
||||||
|
|
||||||
if (isOverdue) {
|
if (isOverdue) {
|
||||||
vh.cardView.setCardBackgroundColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_soft_light_pink_beige));
|
vh.cardView.setCardBackgroundColor(ContextCompat.getColor(vh.itemView.getContext(), R.color.kbs_soft_light_pink_beige));
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
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.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.Toast;
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
@ -33,6 +33,7 @@ import retrofit2.Response;
|
||||||
|
|
||||||
public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickListener {
|
public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickListener {
|
||||||
|
|
||||||
|
private static final String TAG = "HomeFragment";
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private HomeAdapter adapter;
|
private HomeAdapter adapter;
|
||||||
private ProgressBar mainProgressBar;
|
private ProgressBar mainProgressBar;
|
||||||
|
|
@ -41,8 +42,14 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
|
||||||
private List<CalendarEvent> currentEvents = new ArrayList<>();
|
private List<CalendarEvent> currentEvents = new ArrayList<>();
|
||||||
private List<WpPost> currentNews = new ArrayList<>();
|
private List<WpPost> currentNews = new ArrayList<>();
|
||||||
private List<TaskItem> currentTasks = new ArrayList<>();
|
private List<TaskItem> currentTasks = new ArrayList<>();
|
||||||
|
|
||||||
private int activeNetworkCalls = 0;
|
private int activeNetworkCalls = 0;
|
||||||
|
|
||||||
|
private Handler timeoutHandler = new Handler(Looper.getMainLooper());
|
||||||
|
private Runnable timeoutRunnable = this::forceStopLoading;
|
||||||
|
|
||||||
|
private static final int CACHE_TTL_MINUTES = 60;
|
||||||
|
|
||||||
private ActivityResultLauncher<String> requestPermissionLauncher;
|
private ActivityResultLauncher<String> requestPermissionLauncher;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -50,7 +57,7 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
requestPermissionLauncher = registerForActivityResult(
|
requestPermissionLauncher = registerForActivityResult(
|
||||||
new ActivityResultContracts.RequestPermission(),
|
new ActivityResultContracts.RequestPermission(),
|
||||||
isGranted -> refreshData()
|
isGranted -> refreshData(true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,22 +76,82 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
|
||||||
recyclerView = view.findViewById(R.id.main_recycler_view);
|
recyclerView = view.findViewById(R.id.main_recycler_view);
|
||||||
|
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
swipeRefreshLayout.setOnRefreshListener(this::refreshData);
|
|
||||||
|
|
||||||
refreshData();
|
swipeRefreshLayout.setOnRefreshListener(() -> refreshData(true));
|
||||||
|
|
||||||
|
refreshData(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshData() {
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
timeoutHandler.removeCallbacks(timeoutRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshData(boolean forceNetwork) {
|
||||||
if (activeNetworkCalls > 0) return;
|
if (activeNetworkCalls > 0) return;
|
||||||
|
|
||||||
|
boolean useCacheCalendar = !forceNetwork && CacheManager.isCacheValid(getContext(), "calendar", CACHE_TTL_MINUTES);
|
||||||
|
boolean useCacheNews = !forceNetwork && CacheManager.isCacheValid(getContext(), "news", CACHE_TTL_MINUTES);
|
||||||
|
boolean useCacheTasks = !forceNetwork && CacheManager.isCacheValid(getContext(), "tasks", CACHE_TTL_MINUTES);
|
||||||
|
|
||||||
|
// Hvis alt er i cache, last direkte
|
||||||
|
if (useCacheCalendar && useCacheNews && useCacheTasks) {
|
||||||
|
loadAllFromCache();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
activeNetworkCalls = 3;
|
activeNetworkCalls = 3;
|
||||||
if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE);
|
if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
fetchCalendarData();
|
timeoutHandler.removeCallbacks(timeoutRunnable);
|
||||||
fetchNewsData();
|
timeoutHandler.postDelayed(timeoutRunnable, 10000);
|
||||||
fetchTaskData();
|
|
||||||
|
fetchCalendarData(useCacheCalendar);
|
||||||
|
fetchNewsData(useCacheNews);
|
||||||
|
fetchTaskData(useCacheTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchCalendarData() {
|
private void forceStopLoading() {
|
||||||
|
if (activeNetworkCalls > 0) {
|
||||||
|
activeNetworkCalls = 0;
|
||||||
|
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
|
||||||
|
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
|
||||||
|
buildAndDisplayList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAllFromCache() {
|
||||||
|
if (mainProgressBar != null) mainProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
|
||||||
|
new Handler(Looper.getMainLooper()).post(() -> {
|
||||||
|
if (!isAdded()) return;
|
||||||
|
|
||||||
|
List<CalendarEvent> cachedApi = CacheManager.getCachedCalendarEvents(getContext());
|
||||||
|
for (CalendarEvent e : cachedApi) CalendarManager.formatEventForUI(e);
|
||||||
|
currentEvents = CalendarManager.mergeAndSort(cachedApi, deviceEvents);
|
||||||
|
|
||||||
|
currentNews = CacheManager.getCachedNewsPosts(getContext());
|
||||||
|
formatNewsDates(currentNews);
|
||||||
|
|
||||||
|
currentTasks = CacheManager.getTasks(getContext());
|
||||||
|
|
||||||
|
// FIKS: Hvis cache er tom, prøv nettverk likevel for å unngå tom skjerm
|
||||||
|
if (currentTasks.isEmpty() && !CacheManager.isCacheValid(getContext(), "tasks", CACHE_TTL_MINUTES)) {
|
||||||
|
fetchTaskData(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
|
||||||
|
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
|
||||||
|
|
||||||
|
buildAndDisplayList();
|
||||||
|
});
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchCalendarData(boolean useCache) {
|
||||||
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
|
||||||
requestPermissionLauncher.launch(Manifest.permission.READ_CALENDAR);
|
requestPermissionLauncher.launch(Manifest.permission.READ_CALENDAR);
|
||||||
currentEvents.clear();
|
currentEvents.clear();
|
||||||
|
|
@ -93,74 +160,113 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
|
||||||
}
|
}
|
||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
|
try {
|
||||||
new Handler(Looper.getMainLooper()).post(() -> {
|
if (getContext() == null) {
|
||||||
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
checkLoadingCompleteAsync();
|
||||||
@Override
|
return;
|
||||||
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
}
|
||||||
List<CalendarEvent> apiEvents = new ArrayList<>();
|
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
List<CalendarEvent> deviceEvents = CalendarManager.getDeviceEvents(getContext(), true);
|
||||||
apiEvents = response.body();
|
|
||||||
CacheManager.saveCalendarEvents(getContext(), apiEvents);
|
new Handler(Looper.getMainLooper()).post(() -> {
|
||||||
} else {
|
if (!isAdded()) return;
|
||||||
apiEvents = CacheManager.getCachedCalendarEvents(getContext());
|
|
||||||
}
|
if (useCache) {
|
||||||
for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
|
|
||||||
currentEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
|
|
||||||
checkLoadingComplete();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
|
||||||
List<CalendarEvent> cached = CacheManager.getCachedCalendarEvents(getContext());
|
List<CalendarEvent> cached = CacheManager.getCachedCalendarEvents(getContext());
|
||||||
for (CalendarEvent e : cached) CalendarManager.formatEventForUI(e);
|
for (CalendarEvent e : cached) CalendarManager.formatEventForUI(e);
|
||||||
currentEvents = CalendarManager.mergeAndSort(cached, deviceEvents);
|
currentEvents = CalendarManager.mergeAndSort(cached, deviceEvents);
|
||||||
checkLoadingComplete();
|
checkLoadingComplete();
|
||||||
|
} else {
|
||||||
|
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
||||||
|
List<CalendarEvent> apiEvents = new ArrayList<>();
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
apiEvents = response.body();
|
||||||
|
CacheManager.saveCalendarEvents(getContext(), apiEvents);
|
||||||
|
} else {
|
||||||
|
apiEvents = CacheManager.getCachedCalendarEvents(getContext());
|
||||||
|
}
|
||||||
|
for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
|
||||||
|
currentEvents = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
|
||||||
|
checkLoadingComplete();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
||||||
|
List<CalendarEvent> cached = CacheManager.getCachedCalendarEvents(getContext());
|
||||||
|
for (CalendarEvent e : cached) CalendarManager.formatEventForUI(e);
|
||||||
|
currentEvents = CalendarManager.mergeAndSort(cached, deviceEvents);
|
||||||
|
checkLoadingComplete();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
} catch (Exception e) {
|
||||||
|
checkLoadingCompleteAsync();
|
||||||
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchNewsData() {
|
private void fetchNewsData(boolean useCache) {
|
||||||
RetrofitClient.getApiService().getPosts().enqueue(new Callback<List<WpPost>>() {
|
if (useCache) {
|
||||||
@Override
|
currentNews = CacheManager.getCachedNewsPosts(getContext());
|
||||||
public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
|
formatNewsDates(currentNews);
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
checkLoadingComplete();
|
||||||
currentNews = response.body();
|
} else {
|
||||||
CacheManager.saveNewsPosts(getContext(), currentNews);
|
RetrofitClient.getApiService().getPosts().enqueue(new Callback<List<WpPost>>() {
|
||||||
} else {
|
@Override
|
||||||
currentNews = CacheManager.getCachedNewsPosts(getContext());
|
public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
currentNews = response.body();
|
||||||
|
CacheManager.saveNewsPosts(getContext(), currentNews);
|
||||||
|
} else {
|
||||||
|
currentNews = CacheManager.getCachedNewsPosts(getContext());
|
||||||
|
}
|
||||||
|
formatNewsDates(currentNews);
|
||||||
|
checkLoadingComplete();
|
||||||
}
|
}
|
||||||
formatNewsDates(currentNews);
|
@Override
|
||||||
checkLoadingComplete();
|
public void onFailure(Call<List<WpPost>> call, Throwable t) {
|
||||||
}
|
currentNews = CacheManager.getCachedNewsPosts(getContext());
|
||||||
@Override
|
formatNewsDates(currentNews);
|
||||||
public void onFailure(Call<List<WpPost>> call, Throwable t) {
|
checkLoadingComplete();
|
||||||
currentNews = CacheManager.getCachedNewsPosts(getContext());
|
}
|
||||||
formatNewsDates(currentNews);
|
});
|
||||||
checkLoadingComplete();
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchTaskData() {
|
private void fetchTaskData(boolean useCache) {
|
||||||
RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
|
// FIKS: Hvis cache er valgt, men listen er tom, hent fra nett!
|
||||||
@Override
|
if (useCache) {
|
||||||
public void onResponse(Call<List<TaskItem>> call, Response<List<TaskItem>> response) {
|
currentTasks = CacheManager.getTasks(getContext());
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
if (currentTasks.isEmpty()) {
|
||||||
currentTasks = response.body();
|
fetchTaskData(false); // Rekursivt kall men tvinger nettverk
|
||||||
CacheManager.saveTasks(getContext(), currentTasks);
|
return;
|
||||||
} else {
|
}
|
||||||
currentTasks = CacheManager.getTasks(getContext());
|
checkLoadingComplete();
|
||||||
|
} else {
|
||||||
|
RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<List<TaskItem>> call, Response<List<TaskItem>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
currentTasks = response.body();
|
||||||
|
CacheManager.saveTasks(getContext(), currentTasks);
|
||||||
|
} else {
|
||||||
|
currentTasks = CacheManager.getTasks(getContext());
|
||||||
|
}
|
||||||
|
checkLoadingComplete();
|
||||||
}
|
}
|
||||||
checkLoadingComplete();
|
@Override
|
||||||
}
|
public void onFailure(Call<List<TaskItem>> call, Throwable t) {
|
||||||
@Override
|
currentTasks = CacheManager.getTasks(getContext());
|
||||||
public void onFailure(Call<List<TaskItem>> call, Throwable t) {
|
checkLoadingComplete();
|
||||||
currentTasks = CacheManager.getTasks(getContext());
|
}
|
||||||
checkLoadingComplete();
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
private void checkLoadingCompleteAsync() {
|
||||||
|
new Handler(Looper.getMainLooper()).post(this::checkLoadingComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatNewsDates(List<WpPost> posts) {
|
private void formatNewsDates(List<WpPost> posts) {
|
||||||
|
|
@ -174,17 +280,22 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkLoadingComplete() {
|
private synchronized void checkLoadingComplete() {
|
||||||
activeNetworkCalls--;
|
activeNetworkCalls--;
|
||||||
if (activeNetworkCalls <= 0) {
|
if (activeNetworkCalls <= 0) {
|
||||||
activeNetworkCalls = 0;
|
activeNetworkCalls = 0;
|
||||||
|
timeoutHandler.removeCallbacks(timeoutRunnable);
|
||||||
|
|
||||||
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
|
if (mainProgressBar != null) mainProgressBar.setVisibility(View.GONE);
|
||||||
swipeRefreshLayout.setRefreshing(false);
|
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
|
||||||
|
|
||||||
buildAndDisplayList();
|
buildAndDisplayList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildAndDisplayList() {
|
private void buildAndDisplayList() {
|
||||||
|
if (!isAdded()) return;
|
||||||
|
|
||||||
List<Object> items = new ArrayList<>();
|
List<Object> items = new ArrayList<>();
|
||||||
String myEmail = UserManager.getInstance().getUserEmail();
|
String myEmail = UserManager.getInstance().getUserEmail();
|
||||||
|
|
||||||
|
|
@ -201,23 +312,38 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
|
||||||
|
|
||||||
items.add(new HomeAdapter.SectionTitleItem("Mine oppgaver", HomeAdapter.SectionTitleItem.TYPE_TASKS));
|
items.add(new HomeAdapter.SectionTitleItem("Mine oppgaver", HomeAdapter.SectionTitleItem.TYPE_TASKS));
|
||||||
List<TaskItem> myActiveTasks = new ArrayList<>();
|
List<TaskItem> myActiveTasks = new ArrayList<>();
|
||||||
for (TaskItem t : currentTasks) {
|
if (currentTasks != null) {
|
||||||
if (t.isUserParticipant(myEmail) && !t.getParticipantStatus(myEmail) && !t.isFullyCompleted()) {
|
for (TaskItem t : currentTasks) {
|
||||||
myActiveTasks.add(t);
|
if (t.isUserParticipant(myEmail) && !t.getParticipantStatus(myEmail) && !t.isFullyCompleted()) {
|
||||||
|
myActiveTasks.add(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myActiveTasks.isEmpty()) {
|
if (myActiveTasks.isEmpty()) {
|
||||||
items.add(new HomeAdapter.EmptyTasksItem());
|
items.add(new HomeAdapter.EmptyTasksItem());
|
||||||
} else {
|
} else {
|
||||||
Collections.sort(myActiveTasks, (t1, t2) -> Long.compare(t1.getDueDate(), t2.getDueDate()));
|
// NY SORTERING: Med frist først, så uten frist (alfabetisk)
|
||||||
|
Collections.sort(myActiveTasks, (t1, t2) -> {
|
||||||
|
boolean t1HasDate = t1.getDueDate() > 0;
|
||||||
|
boolean t2HasDate = t2.getDueDate() > 0;
|
||||||
|
|
||||||
|
if (t1HasDate && !t2HasDate) return -1; // t1 først
|
||||||
|
if (!t1HasDate && t2HasDate) return 1; // t2 først
|
||||||
|
if (!t1HasDate && !t2HasDate) return t1.getTitle().compareToIgnoreCase(t2.getTitle()); // Alfabetisk
|
||||||
|
|
||||||
|
return Long.compare(t1.getDueDate(), t2.getDueDate()); // Dato
|
||||||
|
});
|
||||||
|
|
||||||
for (int i = 0; i < Math.min(myActiveTasks.size(), 3); i++) {
|
for (int i = 0; i < Math.min(myActiveTasks.size(), 3); i++) {
|
||||||
items.add(myActiveTasks.get(i));
|
items.add(myActiveTasks.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items.add(new HomeAdapter.SectionTitleItem("Siste nytt", HomeAdapter.SectionTitleItem.TYPE_NEWS));
|
items.add(new HomeAdapter.SectionTitleItem("Siste nytt", HomeAdapter.SectionTitleItem.TYPE_NEWS));
|
||||||
items.addAll(currentNews);
|
if (currentNews != null) {
|
||||||
|
items.addAll(currentNews);
|
||||||
|
}
|
||||||
|
|
||||||
adapter = new HomeAdapter(items, this);
|
adapter = new HomeAdapter(items, this);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
@ -230,7 +356,7 @@ public class HomeFragment extends Fragment implements HomeAdapter.OnHomeClickLis
|
||||||
|
|
||||||
@Override public void onCalendarItemClick(CalendarEvent event) {
|
@Override public void onCalendarItemClick(CalendarEvent event) {
|
||||||
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
|
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
|
||||||
sheet.setOnEventChangeListener(this::refreshData);
|
sheet.setOnEventChangeListener(() -> refreshData(true));
|
||||||
sheet.show(getParentFragmentManager(), "CalendarDetails");
|
sheet.show(getParentFragmentManager(), "CalendarDetails");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
@Override
|
@Override
|
||||||
public void onNewToken(@NonNull String token) {
|
public void onNewToken(@NonNull String token) {
|
||||||
super.onNewToken(token);
|
super.onNewToken(token);
|
||||||
Log.d(TAG, "Ny FCM Token: " + token);
|
|
||||||
AuthRepository.updateDeviceToken(token);
|
AuthRepository.updateDeviceToken(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,28 +35,32 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
|
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
|
||||||
super.onMessageReceived(remoteMessage);
|
super.onMessageReceived(remoteMessage);
|
||||||
|
|
||||||
Log.d(TAG, "Melding mottatt fra: " + remoteMessage.getFrom());
|
// 1. DATA PAYLOAD (Bakgrunnsoppdatering)
|
||||||
|
|
||||||
// 1. Sjekk data payload (Bakgrunnsoppdatering)
|
|
||||||
if (remoteMessage.getData().size() > 0) {
|
if (remoteMessage.getData().size() > 0) {
|
||||||
String forceRefresh = remoteMessage.getData().get("force_refresh");
|
String forceRefresh = remoteMessage.getData().get("force_refresh");
|
||||||
|
String type = remoteMessage.getData().get("type"); // "tasks_update", "news_update", "calendar_update"
|
||||||
|
|
||||||
if ("true".equalsIgnoreCase(forceRefresh)) {
|
if ("true".equalsIgnoreCase(forceRefresh)) {
|
||||||
Log.d(TAG, "Mottok 'force_refresh' - oppdaterer kalender og alarmer...");
|
Log.d(TAG, "Mottok force_refresh. Type: " + type);
|
||||||
updateCalendarAndAlarms();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hvis meldingen også har egne titler i data-feltet (valgfritt)
|
// Uansett hva det er, ugyldiggjør cachen slik at neste gang brukeren åpner appen, hentes ferske data.
|
||||||
String title = remoteMessage.getData().get("title");
|
if ("news_update".equals(type)) {
|
||||||
String body = remoteMessage.getData().get("body");
|
CacheManager.invalidateCache(getApplicationContext(), "news");
|
||||||
if (title != null && body != null) {
|
} else if ("tasks_update".equals(type)) {
|
||||||
showNotification(title, body);
|
CacheManager.invalidateCache(getApplicationContext(), "tasks");
|
||||||
|
updateTasksInBackground(); // Hent oppgaver med en gang i bakgrunnen
|
||||||
|
} else {
|
||||||
|
// Kalender eller generelt
|
||||||
|
CacheManager.invalidateCache(getApplicationContext(), "calendar");
|
||||||
|
updateCalendarAndAlarms();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Sjekk notification payload (Vises automatisk når app er i bakgrunn, men vi håndterer den her for forgrunn)
|
// 2. NOTIFICATION PAYLOAD (Synlig varsel - Nyheter etc)
|
||||||
|
// Android viser disse automatisk når appen er i bakgrunnen.
|
||||||
|
// Denne koden kjører hvis appen er i forgrunnen, eller hvis meldingen kun er "Data" og vi vil lage varsel manuelt.
|
||||||
if (remoteMessage.getNotification() != null) {
|
if (remoteMessage.getNotification() != null) {
|
||||||
Log.d(TAG, "Melding varsel body: " + remoteMessage.getNotification().getBody());
|
|
||||||
showNotification(
|
showNotification(
|
||||||
remoteMessage.getNotification().getTitle(),
|
remoteMessage.getNotification().getTitle(),
|
||||||
remoteMessage.getNotification().getBody()
|
remoteMessage.getNotification().getBody()
|
||||||
|
|
@ -66,24 +69,28 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCalendarAndAlarms() {
|
private void updateCalendarAndAlarms() {
|
||||||
// Vi bruker Retrofit for å hente kalenderen på nytt
|
|
||||||
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
RetrofitClient.getApiService().getCalendarEvents().enqueue(new Callback<List<CalendarEvent>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
public void onResponse(Call<List<CalendarEvent>> call, Response<List<CalendarEvent>> response) {
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
// Lagre til cache først (god praksis)
|
|
||||||
CacheManager.saveCalendarEvents(getApplicationContext(), response.body());
|
CacheManager.saveCalendarEvents(getApplicationContext(), response.body());
|
||||||
|
|
||||||
// Oppdater alarmer lokalt
|
|
||||||
AlarmScheduler.scheduleAlarmsForEvents(getApplicationContext(), response.body());
|
AlarmScheduler.scheduleAlarmsForEvents(getApplicationContext(), response.body());
|
||||||
Log.d(TAG, "Kalender og alarmer oppdatert via Push.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Override public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTasksInBackground() {
|
||||||
|
// Henter oppgaver stille i bakgrunnen slik at de er klare når brukeren åpner appen
|
||||||
|
RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
public void onResponse(Call<List<TaskItem>> call, Response<List<TaskItem>> response) {
|
||||||
Log.e(TAG, "Feil ved push-oppdatering av kalender", t);
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
CacheManager.saveTasks(getApplicationContext(), response.body());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@Override public void onFailure(Call<List<TaskItem>> call, Throwable t) {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,10 +106,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
Intent tapIntent = new Intent(this, MainActivity.class);
|
Intent tapIntent = new Intent(this, MainActivity.class);
|
||||||
tapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
tapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||||
this,
|
this, 0, tapIntent, PendingIntent.FLAG_IMMUTABLE
|
||||||
0,
|
|
||||||
tapIntent,
|
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
|
|
@ -114,22 +118,15 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setAutoCancel(true);
|
.setAutoCancel(true);
|
||||||
|
|
||||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
NotificationManagerCompat.from(this).notify((int) System.currentTimeMillis(), builder.build());
|
||||||
notificationManager.notify((int) System.currentTimeMillis(), builder.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNotificationChannel() {
|
private void createNotificationChannel() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
NotificationChannel channel = new NotificationChannel(
|
NotificationChannel channel = new NotificationChannel(
|
||||||
CHANNEL_ID,
|
CHANNEL_ID, "KBS Varsler", NotificationManager.IMPORTANCE_HIGH
|
||||||
"KBS Kalendervarsler",
|
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
|
||||||
);
|
);
|
||||||
channel.setDescription("Varsler fra KBS Intranett");
|
getSystemService(NotificationManager.class).createNotificationChannel(channel);
|
||||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
|
||||||
if (manager != null) {
|
|
||||||
manager.createNotificationChannel(channel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,10 +45,13 @@ public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {
|
||||||
TaskItem task = tasks.get(position);
|
TaskItem task = tasks.get(position);
|
||||||
holder.title.setText(task.getTitle());
|
holder.title.setText(task.getTitle());
|
||||||
|
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
|
if (task.getDueDate() > 0) {
|
||||||
holder.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
|
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
|
||||||
|
holder.date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
|
||||||
|
} else {
|
||||||
|
holder.date.setText("Ingen frist");
|
||||||
|
}
|
||||||
|
|
||||||
// Vis hvem som tildelte oppgaven hvis det ikke er meg selv (Nytt krav)
|
|
||||||
if (task.getCreatedByEmail() != null && !task.getCreatedByEmail().equalsIgnoreCase(currentUserEmail)) {
|
if (task.getCreatedByEmail() != null && !task.getCreatedByEmail().equalsIgnoreCase(currentUserEmail)) {
|
||||||
holder.creator.setText("Tildelt av: " + task.getCreatedByName());
|
holder.creator.setText("Tildelt av: " + task.getCreatedByName());
|
||||||
holder.creator.setVisibility(View.VISIBLE);
|
holder.creator.setVisibility(View.VISIBLE);
|
||||||
|
|
@ -56,22 +59,40 @@ public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {
|
||||||
holder.creator.setVisibility(View.GONE);
|
holder.creator.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean myStatus = task.getParticipantStatus(currentUserEmail);
|
// SJEKK: Er jeg en deltaker?
|
||||||
holder.checkBox.setChecked(myStatus);
|
boolean isParticipant = task.isUserParticipant(currentUserEmail);
|
||||||
|
|
||||||
|
if (isParticipant) {
|
||||||
|
// Hvis jeg er deltaker: Vis checkbox og la meg endre MIN status
|
||||||
|
holder.checkBox.setVisibility(View.VISIBLE);
|
||||||
|
boolean myStatus = task.getParticipantStatus(currentUserEmail);
|
||||||
|
holder.checkBox.setOnCheckedChangeListener(null); // Hindre trigging ved resirkulering
|
||||||
|
holder.checkBox.setChecked(myStatus);
|
||||||
|
holder.checkBox.setOnClickListener(v -> listener.onStatusChanged(task, holder.checkBox.isChecked()));
|
||||||
|
} else {
|
||||||
|
// Hvis jeg IKKE er deltaker (f.eks. Admin som ser på "Alle"):
|
||||||
|
// Skjul checkboxen i listen. Admin må åpne detaljer for å endre andres status.
|
||||||
|
holder.checkBox.setVisibility(View.INVISIBLE);
|
||||||
|
holder.checkBox.setOnClickListener(null);
|
||||||
|
}
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
boolean isOverdue = task.getDueDate() < now && !task.isFullyCompleted() && !myStatus;
|
// Sjekk overtid (kun hvis frist er satt)
|
||||||
|
// Er jeg ikke deltaker, sjekker vi om hele oppgaven er ferdig
|
||||||
|
boolean isDoneToCheck = isParticipant ? task.getParticipantStatus(currentUserEmail) : task.isFullyCompleted();
|
||||||
|
boolean isOverdue = task.getDueDate() > 0 && task.getDueDate() < now && !task.isFullyCompleted() && !isDoneToCheck;
|
||||||
|
|
||||||
if (isOverdue) {
|
if (isOverdue) {
|
||||||
holder.cardView.setCardBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_soft_light_pink_beige));
|
holder.cardView.setCardBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_soft_light_pink_beige));
|
||||||
holder.date.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_logo_accent_red));
|
holder.date.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_logo_accent_red));
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("dd. MMM", Locale.getDefault());
|
||||||
holder.date.setText("FORFALT: " + sdf.format(new Date(task.getDueDate())));
|
holder.date.setText("FORFALT: " + sdf.format(new Date(task.getDueDate())));
|
||||||
} else {
|
} else {
|
||||||
holder.cardView.setCardBackgroundColor(Color.WHITE);
|
holder.cardView.setCardBackgroundColor(Color.WHITE);
|
||||||
holder.date.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_muted_blue_gray));
|
holder.date.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), R.color.kbs_muted_blue_gray));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myStatus || task.isFullyCompleted()) {
|
if (isDoneToCheck || task.isFullyCompleted()) {
|
||||||
holder.title.setPaintFlags(holder.title.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
holder.title.setPaintFlags(holder.title.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||||
holder.title.setTextColor(Color.GRAY);
|
holder.title.setTextColor(Color.GRAY);
|
||||||
holder.cardView.setCardBackgroundColor(Color.parseColor("#F5F5F5"));
|
holder.cardView.setCardBackgroundColor(Color.parseColor("#F5F5F5"));
|
||||||
|
|
@ -85,7 +106,6 @@ public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {
|
||||||
for (Boolean b : task.getAssigneeStatus().values()) if (b) done++;
|
for (Boolean b : task.getAssigneeStatus().values()) if (b) done++;
|
||||||
holder.progress.setText("Fremdrift: " + done + "/" + total);
|
holder.progress.setText("Fremdrift: " + done + "/" + total);
|
||||||
|
|
||||||
holder.checkBox.setOnClickListener(v -> listener.onStatusChanged(task, holder.checkBox.isChecked()));
|
|
||||||
holder.itemView.setOnClickListener(v -> listener.onTaskClick(task));
|
holder.itemView.setOnClickListener(v -> listener.onTaskClick(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ 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.CheckBox;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
@ -24,7 +25,7 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
|
||||||
public interface OnTaskChangeListener {
|
public interface OnTaskChangeListener {
|
||||||
void onTaskChanged();
|
void onTaskChanged();
|
||||||
void onTaskDeleted(TaskItem task);
|
void onTaskDeleted(TaskItem task);
|
||||||
void onEditRequested(TaskItem task); // NYTT
|
void onEditRequested(TaskItem task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskDetailsBottomSheet(TaskItem task, OnTaskChangeListener listener) {
|
public TaskDetailsBottomSheet(TaskItem task, OnTaskChangeListener listener) {
|
||||||
|
|
@ -45,10 +46,15 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
|
||||||
LinearLayout ownerActions = v.findViewById(R.id.layout_owner_actions);
|
LinearLayout ownerActions = v.findViewById(R.id.layout_owner_actions);
|
||||||
Button btnDelete = v.findViewById(R.id.btn_delete_task);
|
Button btnDelete = v.findViewById(R.id.btn_delete_task);
|
||||||
Button btnEdit = v.findViewById(R.id.btn_edit_task);
|
Button btnEdit = v.findViewById(R.id.btn_edit_task);
|
||||||
|
Button btnClose = v.findViewById(R.id.btn_close_details); // NYTT
|
||||||
|
|
||||||
title.setText(task.getTitle());
|
title.setText(task.getTitle());
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd. MMMM yyyy", Locale.getDefault());
|
if (task.getDueDate() > 0) {
|
||||||
date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
|
SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd. MMMM yyyy", Locale.getDefault());
|
||||||
|
date.setText("Frist: " + sdf.format(new Date(task.getDueDate())));
|
||||||
|
} else {
|
||||||
|
date.setText("Ingen frist");
|
||||||
|
}
|
||||||
|
|
||||||
if (task.getDescription() != null && !task.getDescription().isEmpty()) {
|
if (task.getDescription() != null && !task.getDescription().isEmpty()) {
|
||||||
desc.setText(task.getDescription());
|
desc.setText(task.getDescription());
|
||||||
|
|
@ -56,12 +62,47 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
participantsContainer.removeAllViews();
|
participantsContainer.removeAllViews();
|
||||||
|
UserManager userManager = UserManager.getInstance();
|
||||||
|
String myEmail = userManager.getUserEmail();
|
||||||
|
|
||||||
|
boolean canManageOthers = task.getCreatedByEmail().equalsIgnoreCase(myEmail) || userManager.isAdmin();
|
||||||
|
|
||||||
for (Map.Entry<String, Boolean> entry : task.getAssigneeStatus().entrySet()) {
|
for (Map.Entry<String, Boolean> entry : task.getAssigneeStatus().entrySet()) {
|
||||||
TextView t = new TextView(getContext());
|
String email = entry.getKey();
|
||||||
String status = entry.getValue() ? "✅ Fullført" : "⏳ Pågår";
|
boolean isDone = entry.getValue();
|
||||||
t.setText("• " + entry.getKey() + ": " + status);
|
|
||||||
t.setPadding(0, 4, 0, 4);
|
if (canManageOthers) {
|
||||||
participantsContainer.addView(t);
|
CheckBox cb = new CheckBox(getContext());
|
||||||
|
cb.setText(email + (isDone ? " (Fullført)" : ""));
|
||||||
|
cb.setChecked(isDone);
|
||||||
|
|
||||||
|
if (isDone) cb.setTextColor(getResources().getColor(android.R.color.darker_gray));
|
||||||
|
else cb.setTextColor(getResources().getColor(R.color.black));
|
||||||
|
|
||||||
|
cb.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
task.setParticipantStatus(email, isChecked);
|
||||||
|
|
||||||
|
cb.setText(email + (isChecked ? " (Fullført)" : ""));
|
||||||
|
if (isChecked) cb.setTextColor(getResources().getColor(android.R.color.darker_gray));
|
||||||
|
else cb.setTextColor(getResources().getColor(R.color.black));
|
||||||
|
|
||||||
|
if (listener != null) listener.onTaskChanged();
|
||||||
|
|
||||||
|
// Sjekk om alle er ferdige, i så fall lukk vinduet
|
||||||
|
if (isChecked && areAllParticipantsFinished()) {
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
participantsContainer.addView(cb);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
TextView t = new TextView(getContext());
|
||||||
|
String status = isDone ? "✅ Fullført" : "⏳ Pågår";
|
||||||
|
t.setText("• " + email + ": " + status);
|
||||||
|
t.setPadding(0, 8, 0, 8);
|
||||||
|
t.setTextSize(14);
|
||||||
|
participantsContainer.addView(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switchNotify.setChecked(task.isNotificationsEnabled());
|
switchNotify.setChecked(task.isNotificationsEnabled());
|
||||||
|
|
@ -70,7 +111,7 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
|
||||||
if (listener != null) listener.onTaskChanged();
|
if (listener != null) listener.onTaskChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (task.getCreatedByEmail().equalsIgnoreCase(UserManager.getInstance().getUserEmail())) {
|
if (canManageOthers) {
|
||||||
ownerActions.setVisibility(View.VISIBLE);
|
ownerActions.setVisibility(View.VISIBLE);
|
||||||
btnDelete.setOnClickListener(view -> {
|
btnDelete.setOnClickListener(view -> {
|
||||||
if (listener != null) listener.onTaskDeleted(task);
|
if (listener != null) listener.onTaskDeleted(task);
|
||||||
|
|
@ -80,8 +121,20 @@ public class TaskDetailsBottomSheet extends BottomSheetDialogFragment {
|
||||||
if (listener != null) listener.onEditRequested(task);
|
if (listener != null) listener.onEditRequested(task);
|
||||||
dismiss();
|
dismiss();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
ownerActions.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NYTT: Lukk-knapp funksjonalitet
|
||||||
|
btnClose.setOnClickListener(view -> dismiss());
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean areAllParticipantsFinished() {
|
||||||
|
for (Boolean isDone : task.getAssigneeStatus().values()) {
|
||||||
|
if (!isDone) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import android.os.Bundle;
|
||||||
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.CheckBox;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
@ -27,6 +28,7 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
|
||||||
private TaskAdapter adapter;
|
private TaskAdapter adapter;
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
private SwipeRefreshLayout swipeRefresh;
|
private SwipeRefreshLayout swipeRefresh;
|
||||||
|
private CheckBox cbShowCompleted;
|
||||||
private List<TaskItem> allTasks = new ArrayList<>();
|
private List<TaskItem> allTasks = new ArrayList<>();
|
||||||
private String myEmail;
|
private String myEmail;
|
||||||
|
|
||||||
|
|
@ -44,6 +46,8 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
|
||||||
tabLayout = view.findViewById(R.id.task_tabs);
|
tabLayout = view.findViewById(R.id.task_tabs);
|
||||||
recyclerView = view.findViewById(R.id.recycler_tasks);
|
recyclerView = view.findViewById(R.id.recycler_tasks);
|
||||||
swipeRefresh = view.findViewById(R.id.swipe_refresh_tasks);
|
swipeRefresh = view.findViewById(R.id.swipe_refresh_tasks);
|
||||||
|
cbShowCompleted = view.findViewById(R.id.cb_show_completed);
|
||||||
|
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
|
||||||
setupTabs();
|
setupTabs();
|
||||||
|
|
@ -67,14 +71,20 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
|
||||||
});
|
});
|
||||||
|
|
||||||
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||||
@Override public void onTabSelected(TabLayout.Tab tab) { filterAndDisplay(); }
|
@Override public void onTabSelected(TabLayout.Tab tab) {
|
||||||
|
updateFilterUI(tab.getPosition());
|
||||||
|
filterAndDisplay();
|
||||||
|
}
|
||||||
@Override public void onTabUnselected(TabLayout.Tab tab) {}
|
@Override public void onTabUnselected(TabLayout.Tab tab) {}
|
||||||
@Override public void onTabReselected(TabLayout.Tab tab) {}
|
@Override public void onTabReselected(TabLayout.Tab tab) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cbShowCompleted.setOnCheckedChangeListener((buttonView, isChecked) -> filterAndDisplay());
|
||||||
|
|
||||||
swipeRefresh.setOnRefreshListener(this::fetchTasksFromServer);
|
swipeRefresh.setOnRefreshListener(this::fetchTasksFromServer);
|
||||||
|
|
||||||
allTasks = CacheManager.getTasks(getContext());
|
allTasks = CacheManager.getTasks(getContext());
|
||||||
|
updateFilterUI(tabLayout.getSelectedTabPosition());
|
||||||
filterAndDisplay();
|
filterAndDisplay();
|
||||||
fetchTasksFromServer();
|
fetchTasksFromServer();
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +99,14 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateFilterUI(int tabIndex) {
|
||||||
|
if (tabIndex == 3) {
|
||||||
|
cbShowCompleted.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
cbShowCompleted.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void fetchTasksFromServer() {
|
private void fetchTasksFromServer() {
|
||||||
swipeRefresh.setRefreshing(true);
|
swipeRefresh.setRefreshing(true);
|
||||||
RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
|
RetrofitClient.getApiService().getTasks().enqueue(new Callback<List<TaskItem>>() {
|
||||||
|
|
@ -122,6 +140,27 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
|
||||||
if (!email.equalsIgnoreCase(myEmail)) { hasOtherParticipants = true; break; }
|
if (!email.equalsIgnoreCase(myEmail)) { hasOtherParticipants = true; break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NYTT: Sjekk dynamisk om ALLE deltakerne har fullført oppgaven
|
||||||
|
boolean allParticipantsFinished = true;
|
||||||
|
if (t.getAssigneeStatus().isEmpty()) {
|
||||||
|
allParticipantsFinished = false;
|
||||||
|
} else {
|
||||||
|
for (Boolean isDone : t.getAssigneeStatus().values()) {
|
||||||
|
if (!isDone) {
|
||||||
|
allParticipantsFinished = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// En oppgave er "helt ferdig" hvis flagget er satt ELLER alle har krysset av
|
||||||
|
boolean effectivelyFinished = t.isFullyCompleted() || allParticipantsFinished;
|
||||||
|
|
||||||
|
// Oppdater objektet slik at det lagres riktig neste gang (valgfritt men lurt)
|
||||||
|
if (effectivelyFinished && !t.isFullyCompleted()) {
|
||||||
|
t.setFullyCompleted(true);
|
||||||
|
}
|
||||||
|
|
||||||
switch (selectedTab) {
|
switch (selectedTab) {
|
||||||
case 0: // MINE
|
case 0: // MINE
|
||||||
if (isParticipant && !iHaveDoneIt && !t.isFullyCompleted()) filtered.add(t);
|
if (isParticipant && !iHaveDoneIt && !t.isFullyCompleted()) filtered.add(t);
|
||||||
|
|
@ -132,13 +171,37 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
|
||||||
case 2: // TILDELT ANDRE
|
case 2: // TILDELT ANDRE
|
||||||
if (isCreator && !t.isFullyCompleted() && hasOtherParticipants) filtered.add(t);
|
if (isCreator && !t.isFullyCompleted() && hasOtherParticipants) filtered.add(t);
|
||||||
break;
|
break;
|
||||||
case 3: // ALLE
|
case 3: // ALLE (Admin)
|
||||||
if (!t.isFullyCompleted()) filtered.add(t);
|
if (cbShowCompleted.isChecked()) {
|
||||||
|
// Vis alt
|
||||||
|
filtered.add(t);
|
||||||
|
} else {
|
||||||
|
// Vis kun hvis IKKE ferdig
|
||||||
|
if (!effectivelyFinished) filtered.add(t);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selectedTab == 1) Collections.sort(filtered, (t1, t2) -> Long.compare(t2.getDueDate(), t1.getDueDate()));
|
|
||||||
else Collections.sort(filtered, (t1, t2) -> Long.compare(t1.getDueDate(), t2.getDueDate()));
|
// SORTERING
|
||||||
|
Collections.sort(filtered, (t1, t2) -> {
|
||||||
|
boolean t1HasDate = t1.getDueDate() > 0;
|
||||||
|
boolean t2HasDate = t2.getDueDate() > 0;
|
||||||
|
|
||||||
|
if (selectedTab == 1) {
|
||||||
|
if (t1HasDate && !t2HasDate) return -1;
|
||||||
|
if (!t1HasDate && t2HasDate) return 1;
|
||||||
|
if (!t1HasDate && !t2HasDate) return t1.getTitle().compareToIgnoreCase(t2.getTitle());
|
||||||
|
return Long.compare(t2.getDueDate(), t1.getDueDate());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (t1HasDate && !t2HasDate) return -1;
|
||||||
|
if (!t1HasDate && t2HasDate) return 1;
|
||||||
|
if (!t1HasDate && !t2HasDate) return t1.getTitle().compareToIgnoreCase(t2.getTitle());
|
||||||
|
return Long.compare(t1.getDueDate(), t2.getDueDate());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
adapter = new TaskAdapter(filtered, this);
|
adapter = new TaskAdapter(filtered, this);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
@ -155,7 +218,7 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
|
||||||
AddTaskBottomSheet editDialog = new AddTaskBottomSheet();
|
AddTaskBottomSheet editDialog = new AddTaskBottomSheet();
|
||||||
editDialog.setTaskToEdit(taskToEdit);
|
editDialog.setTaskToEdit(taskToEdit);
|
||||||
editDialog.setOnTaskAddedListener(new AddTaskBottomSheet.OnTaskAddedListener() {
|
editDialog.setOnTaskAddedListener(new AddTaskBottomSheet.OnTaskAddedListener() {
|
||||||
@Override public void onTaskAdded(TaskItem task) {} // Ikke i bruk her
|
@Override public void onTaskAdded(TaskItem task) {}
|
||||||
@Override public void onTaskUpdated(TaskItem task) {
|
@Override public void onTaskUpdated(TaskItem task) {
|
||||||
saveAndSync();
|
saveAndSync();
|
||||||
}
|
}
|
||||||
|
|
@ -169,19 +232,14 @@ public class TasksFragment extends Fragment implements TaskAdapter.OnTaskClickLi
|
||||||
@Override
|
@Override
|
||||||
public void onStatusChanged(TaskItem task, boolean isDone) {
|
public void onStatusChanged(TaskItem task, boolean isDone) {
|
||||||
task.setParticipantStatus(myEmail, isDone);
|
task.setParticipantStatus(myEmail, isDone);
|
||||||
boolean hasOthers = false;
|
// Her trenger vi ikke logikk for "hasOthers" osv, fordi saveAndSync()
|
||||||
for (String email : task.getAssigneeStatus().keySet()) {
|
// kaller filterAndDisplay() som nå regner ut status dynamisk.
|
||||||
if (!email.equalsIgnoreCase(myEmail)) { hasOthers = true; break; }
|
|
||||||
}
|
|
||||||
if (task.getCreatedByEmail().equalsIgnoreCase(myEmail) && isDone && !hasOthers) {
|
|
||||||
task.setFullyCompleted(true);
|
|
||||||
}
|
|
||||||
saveAndSync();
|
saveAndSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveAndSync() {
|
private void saveAndSync() {
|
||||||
CacheManager.saveTasks(getContext(), allTasks);
|
CacheManager.saveTasks(getContext(), allTasks);
|
||||||
filterAndDisplay();
|
filterAndDisplay(); // Oppdaterer visningen umiddelbart
|
||||||
RetrofitClient.getApiService().syncTasks(allTasks).enqueue(new Callback<JsonElement>() {
|
RetrofitClient.getApiService().syncTasks(allTasks).enqueue(new Callback<JsonElement>() {
|
||||||
@Override public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {}
|
@Override public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {}
|
||||||
@Override public void onFailure(Call<JsonElement> call, Throwable t) {}
|
@Override public void onFailure(Call<JsonElement> call, Throwable t) {}
|
||||||
|
|
|
||||||
|
|
@ -45,18 +45,31 @@
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:layout_marginBottom="12dp">
|
android:layout_marginBottom="12dp">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btn_task_date"
|
android:id="@+id/btn_task_date"
|
||||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Sett frist" />
|
android:text="Sett frist" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txt_date_preview"
|
android:id="@+id/txt_date_preview"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
android:text="Frist: -"
|
android:text="Frist: -"
|
||||||
android:layout_marginStart="8dp"/>
|
android:layout_marginStart="8dp"/>
|
||||||
|
|
||||||
|
<!-- NYTT: Fjern frist knapp -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_clear_date"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="X"
|
||||||
|
android:textColor="#999999"
|
||||||
|
android:visibility="gone"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
android:id="@+id/switch_notifications"
|
android:id="@+id/switch_notifications"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Varsle meg om denne oppgaven"
|
android:text="Aktiver påminnelser til deltakere"
|
||||||
android:checked="true"
|
android:checked="true"
|
||||||
android:layout_marginBottom="24dp"/>
|
android:layout_marginBottom="24dp"/>
|
||||||
|
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -93,4 +94,13 @@
|
||||||
android:layout_marginStart="8dp"/>
|
android:layout_marginStart="8dp"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- NY LUKK KNAPP -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_close_details"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Lukk / Ferdig"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:textColor="@color/kbs_muted_blue_gray"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
@ -13,9 +13,21 @@
|
||||||
android:background="@color/white"
|
android:background="@color/white"
|
||||||
app:tabSelectedTextColor="@color/kbs_logo_blue"
|
app:tabSelectedTextColor="@color/kbs_logo_blue"
|
||||||
app:tabIndicatorColor="@color/kbs_logo_blue">
|
app:tabIndicatorColor="@color/kbs_logo_blue">
|
||||||
<!-- Faner legges til programmatisk i TasksFragment.java -->
|
|
||||||
</com.google.android.material.tabs.TabLayout>
|
</com.google.android.material.tabs.TabLayout>
|
||||||
|
|
||||||
|
<!-- NY SJEKKBOKS: Vises kun under "Alle" -->
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/cb_show_completed"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Vis også fullførte oppgaver"
|
||||||
|
android:textColor="@color/kbs_muted_blue_gray"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/swipe_refresh_tasks"
|
android:id="@+id/swipe_refresh_tasks"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue