Før lenker i detaljvisning av kalenderoppføringer

This commit is contained in:
ErolHaagenrud 2025-12-18 16:02:02 +01:00
parent 5497e68110
commit 845ccf22c2
10 changed files with 1143 additions and 419 deletions

View file

@ -59,6 +59,16 @@
android:resource="@xml/file_paths" />
</provider>
<!-- Standard ikon for Firebase varsler -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_kbs" />
<!-- Standard farge for Firebase varsler (KBS Blå) -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/kbs_logo_blue" />
</application>
</manifest>

View file

@ -12,6 +12,7 @@ import android.os.Build;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat; // <-- Denne manglet
public class AlarmReceiver extends BroadcastReceiver {
@ -48,7 +49,8 @@ public class AlarmReceiver extends BroadcastReceiver {
);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground) // Pass at du har et ikon her, ellers bruk R.mipmap.ic_launcher
.setSmallIcon(R.drawable.ic_stat_kbs)
.setColor(ContextCompat.getColor(context, R.color.kbs_logo_blue)) // Setter KBS-blå farge ikonet
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_HIGH)

View file

@ -0,0 +1,95 @@
package com.kbs.kbsintranett;
import android.content.Context;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class CacheManager {
private static final String FILE_CALENDAR = "cache_calendar.json";
private static final String FILE_NEWS = "cache_news.json";
private static final String FILE_HANDBOOK = "cache_handbook.json";
private static final String TAG = "CacheManager";
private static final Gson gson = new Gson();
// --- KALENDER ---
public static void saveCalendarEvents(Context context, List<CalendarEvent> events) {
saveList(context, FILE_CALENDAR, events);
}
public static List<CalendarEvent> getCachedCalendarEvents(Context context) {
Type type = new TypeToken<List<CalendarEvent>>() {}.getType();
List<CalendarEvent> list = loadList(context, FILE_CALENDAR, type);
return list != null ? list : new ArrayList<>();
}
// --- NYHETER ---
public static void saveNewsPosts(Context context, List<WpPost> posts) {
saveList(context, FILE_NEWS, posts);
}
public static List<WpPost> getCachedNewsPosts(Context context) {
Type type = new TypeToken<List<WpPost>>() {}.getType();
List<WpPost> list = loadList(context, FILE_NEWS, type);
return list != null ? list : new ArrayList<>();
}
// --- HÅNDBOK ---
public static void saveHandbookItems(Context context, List<HandbookItem> items) {
saveList(context, FILE_HANDBOOK, items);
}
public static List<HandbookItem> getCachedHandbookItems(Context context) {
Type type = new TypeToken<List<HandbookItem>>() {}.getType();
List<HandbookItem> list = loadList(context, FILE_HANDBOOK, type);
return list != null ? list : new ArrayList<>();
}
// --- GENERISKE HJELPEMETODER ---
private static <T> void saveList(Context context, String filename, List<T> list) {
if (context == null || list == null) return;
new Thread(() -> {
try {
String json = gson.toJson(list);
FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
fos.write(json.getBytes());
fos.close();
Log.d(TAG, "Lagret cache til " + filename);
} catch (Exception e) {
Log.e(TAG, "Feil ved lagring av cache: " + filename, e);
}
}).start();
}
private static <T> List<T> loadList(Context context, String filename, Type type) {
if (context == null) return null;
File file = new File(context.getFilesDir(), filename);
if (!file.exists()) return null;
try {
FileInputStream fis = context.openFileInput(filename);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader bufferedReader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
fis.close();
return gson.fromJson(sb.toString(), type);
} catch (Exception e) {
Log.e(TAG, "Feil ved lesing av cache: " + filename, e);
return null;
}
}
}

View file

@ -11,6 +11,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
@ -89,7 +90,6 @@ public class HomeFragment extends Fragment {
btnCreateEvent.setVisibility(View.GONE);
}
// KALENDER: Standard scrolling (slik at man kan scrolle i vinduet 230dp)
calendarRecycler = view.findViewById(R.id.recycler_calendar);
calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {}));
@ -105,7 +105,6 @@ public class HomeFragment extends Fragment {
}
}
// NYHETER: Nested scrolling disabled (skal vises i full høyde under kalenderen)
newsRecycler = view.findViewById(R.id.recycler_news);
newsRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
newsRecycler.setNestedScrollingEnabled(false);
@ -167,33 +166,21 @@ public class HomeFragment extends Fragment {
List<CalendarEvent> apiEvents = new ArrayList<>();
if (response.isSuccessful() && response.body() != null) {
apiEvents = response.body();
CacheManager.saveCalendarEvents(getContext(), apiEvents);
for (CalendarEvent e : apiEvents) {
CalendarManager.formatEventForUI(e);
}
}
List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
List<CalendarEvent> upcomingEvents = new ArrayList<>();
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
for (CalendarEvent e : merged) {
if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) {
upcomingEvents.add(e);
} else {
apiEvents = CacheManager.getCachedCalendarEvents(getContext());
for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
if (!apiEvents.isEmpty()) {
Toast.makeText(getContext(), "Server utilgjengelig. Viser lagret kalender.", Toast.LENGTH_SHORT).show();
}
}
List<CalendarEvent> topEvents = new ArrayList<>();
// ENDRET: Grensen er satt tilbake til 5
for(int i=0; i<Math.min(upcomingEvents.size(), 5); i++) {
topEvents.add(upcomingEvents.get(i));
}
recyclerView.setAdapter(new CalendarAdapter(topEvents, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(HomeFragment.this::refreshData);
sheet.show(getParentFragmentManager(), "CalendarDetails");
}));
updateCalendarUI(recyclerView, apiEvents, deviceEvents);
checkLoadingComplete();
}
@ -201,23 +188,42 @@ public class HomeFragment extends Fragment {
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
if (!isAdded()) return;
if (!deviceEvents.isEmpty()) {
List<CalendarEvent> topEvents = new ArrayList<>();
// ENDRET: Grensen er satt tilbake til 5 (Fallback)
for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) topEvents.add(deviceEvents.get(i));
recyclerView.setAdapter(new CalendarAdapter(topEvents, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.show(getParentFragmentManager(), "CalendarDetails");
}));
} else {
recyclerView.setAdapter(new CalendarAdapter(new ArrayList<>(), null));
List<CalendarEvent> cachedApiEvents = CacheManager.getCachedCalendarEvents(getContext());
for (CalendarEvent e : cachedApiEvents) CalendarManager.formatEventForUI(e);
if (!cachedApiEvents.isEmpty()) {
Toast.makeText(getContext(), "Ingen nettverk. Viser lagret kalender.", Toast.LENGTH_SHORT).show();
}
updateCalendarUI(recyclerView, cachedApiEvents, deviceEvents);
checkLoadingComplete();
}
});
}
private void updateCalendarUI(RecyclerView recyclerView, List<CalendarEvent> apiEvents, List<CalendarEvent> deviceEvents) {
List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
List<CalendarEvent> upcomingEvents = new ArrayList<>();
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
for (CalendarEvent e : merged) {
if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) {
upcomingEvents.add(e);
}
}
List<CalendarEvent> topEvents = new ArrayList<>();
for(int i=0; i<Math.min(upcomingEvents.size(), 5); i++) {
topEvents.add(upcomingEvents.get(i));
}
recyclerView.setAdapter(new CalendarAdapter(topEvents, event -> {
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
sheet.setOnEventChangeListener(HomeFragment.this::refreshData);
sheet.show(getParentFragmentManager(), "CalendarDetails");
}));
}
private void fetchNewsFromWordpress(RecyclerView recyclerView) {
WordPressApiService apiService = RetrofitClient.getApiService();
apiService.getPosts().enqueue(new Callback<List<WpPost>>() {
@ -225,39 +231,58 @@ public class HomeFragment extends Fragment {
public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
if (getContext() == null) return;
List<WpPost> postsToShow = new ArrayList<>();
if (response.isSuccessful() && response.body() != null) {
List<WpPost> wpPosts = response.body();
SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
rawFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
targetFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
for (WpPost post : wpPosts) {
try {
Date date = rawFormat.parse(post.date);
post.date = targetFormat.format(date);
} catch (Exception e) {}
postsToShow = response.body();
CacheManager.saveNewsPosts(getContext(), postsToShow);
} else {
postsToShow = CacheManager.getCachedNewsPosts(getContext());
if (!postsToShow.isEmpty()) {
Toast.makeText(getContext(), "Server utilgjengelig. Viser lagrede nyheter.", Toast.LENGTH_SHORT).show();
}
NewsAdapter adapter = new NewsAdapter(wpPosts, post -> {
Bundle bundle = new Bundle();
bundle.putSerializable("post_data", post);
Navigation.findNavController(getView()).navigate(R.id.action_home_to_newsDetail, bundle);
});
recyclerView.setAdapter(adapter);
}
updateNewsUI(recyclerView, postsToShow);
checkLoadingComplete();
}
@Override
public void onFailure(Call<List<WpPost>> call, Throwable t) {
if (getContext() == null) return;
recyclerView.setAdapter(new NewsAdapter(new ArrayList<>(), null));
List<WpPost> cachedPosts = CacheManager.getCachedNewsPosts(getContext());
if (!cachedPosts.isEmpty()) {
Toast.makeText(getContext(), "Ingen nettverk. Viser lagrede nyheter.", Toast.LENGTH_SHORT).show();
}
updateNewsUI(recyclerView, cachedPosts);
checkLoadingComplete();
}
});
}
private void updateNewsUI(RecyclerView recyclerView, List<WpPost> posts) {
SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
rawFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
targetFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
for (WpPost post : posts) {
try {
Date date = rawFormat.parse(post.date);
post.date = targetFormat.format(date);
} catch (Exception e) {}
}
NewsAdapter adapter = new NewsAdapter(posts, post -> {
Bundle bundle = new Bundle();
bundle.putSerializable("post_data", post);
Navigation.findNavController(getView()).navigate(R.id.action_home_to_newsDetail, bundle);
});
recyclerView.setAdapter(adapter);
}
private void startNotificationWorker() {
PeriodicWorkRequest notifRequest =
new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because it is too large Load diff