Før lenker i detaljvisning av kalenderoppføringer
This commit is contained in:
parent
5497e68110
commit
845ccf22c2
10 changed files with 1143 additions and 419 deletions
|
|
@ -59,6 +59,16 @@
|
||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
@ -12,6 +12,7 @@ import android.os.Build;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.core.content.ContextCompat; // <-- Denne manglet
|
||||||
|
|
||||||
public class AlarmReceiver extends BroadcastReceiver {
|
public class AlarmReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
|
@ -48,7 +49,8 @@ public class AlarmReceiver extends BroadcastReceiver {
|
||||||
);
|
);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
|
||||||
.setSmallIcon(R.drawable.ic_launcher_foreground) // Pass på 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 på ikonet
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setContentText(message)
|
.setContentText(message)
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
|
|
|
||||||
95
app/src/main/java/com/kbs/kbsintranett/CacheManager.java
Normal file
95
app/src/main/java/com/kbs/kbsintranett/CacheManager.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
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;
|
||||||
|
|
@ -89,7 +90,6 @@ public class HomeFragment extends Fragment {
|
||||||
btnCreateEvent.setVisibility(View.GONE);
|
btnCreateEvent.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// KALENDER: Standard scrolling (slik at man kan scrolle i vinduet på 230dp)
|
|
||||||
calendarRecycler = view.findViewById(R.id.recycler_calendar);
|
calendarRecycler = view.findViewById(R.id.recycler_calendar);
|
||||||
calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
|
calendarRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
calendarRecycler.setAdapter(new CalendarAdapter(new ArrayList<>(), event -> {}));
|
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 = view.findViewById(R.id.recycler_news);
|
||||||
newsRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
|
newsRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
newsRecycler.setNestedScrollingEnabled(false);
|
newsRecycler.setNestedScrollingEnabled(false);
|
||||||
|
|
@ -167,33 +166,21 @@ public class HomeFragment extends Fragment {
|
||||||
List<CalendarEvent> apiEvents = new ArrayList<>();
|
List<CalendarEvent> apiEvents = new ArrayList<>();
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
apiEvents = response.body();
|
apiEvents = response.body();
|
||||||
|
|
||||||
|
CacheManager.saveCalendarEvents(getContext(), apiEvents);
|
||||||
|
|
||||||
for (CalendarEvent e : apiEvents) {
|
for (CalendarEvent e : apiEvents) {
|
||||||
CalendarManager.formatEventForUI(e);
|
CalendarManager.formatEventForUI(e);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
apiEvents = CacheManager.getCachedCalendarEvents(getContext());
|
||||||
List<CalendarEvent> merged = CalendarManager.mergeAndSort(apiEvents, deviceEvents);
|
for (CalendarEvent e : apiEvents) CalendarManager.formatEventForUI(e);
|
||||||
List<CalendarEvent> upcomingEvents = new ArrayList<>();
|
if (!apiEvents.isEmpty()) {
|
||||||
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
|
Toast.makeText(getContext(), "Server utilgjengelig. Viser lagret kalender.", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
for (CalendarEvent e : merged) {
|
|
||||||
if (e.getRawDate() != null && e.getRawDate().compareTo(today) >= 0) {
|
|
||||||
upcomingEvents.add(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<CalendarEvent> topEvents = new ArrayList<>();
|
updateCalendarUI(recyclerView, apiEvents, deviceEvents);
|
||||||
// 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");
|
|
||||||
}));
|
|
||||||
|
|
||||||
checkLoadingComplete();
|
checkLoadingComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,23 +188,42 @@ public class HomeFragment extends Fragment {
|
||||||
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
public void onFailure(Call<List<CalendarEvent>> call, Throwable t) {
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
|
|
||||||
if (!deviceEvents.isEmpty()) {
|
List<CalendarEvent> cachedApiEvents = CacheManager.getCachedCalendarEvents(getContext());
|
||||||
List<CalendarEvent> topEvents = new ArrayList<>();
|
for (CalendarEvent e : cachedApiEvents) CalendarManager.formatEventForUI(e);
|
||||||
// ENDRET: Grensen er satt tilbake til 5 (Fallback)
|
|
||||||
for(int i=0; i<Math.min(deviceEvents.size(), 5); i++) topEvents.add(deviceEvents.get(i));
|
if (!cachedApiEvents.isEmpty()) {
|
||||||
recyclerView.setAdapter(new CalendarAdapter(topEvents, event -> {
|
Toast.makeText(getContext(), "Ingen nettverk. Viser lagret kalender.", Toast.LENGTH_SHORT).show();
|
||||||
CalendarDetailsBottomSheet sheet = new CalendarDetailsBottomSheet(event);
|
|
||||||
sheet.show(getParentFragmentManager(), "CalendarDetails");
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
recyclerView.setAdapter(new CalendarAdapter(new ArrayList<>(), null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateCalendarUI(recyclerView, cachedApiEvents, deviceEvents);
|
||||||
checkLoadingComplete();
|
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) {
|
private void fetchNewsFromWordpress(RecyclerView recyclerView) {
|
||||||
WordPressApiService apiService = RetrofitClient.getApiService();
|
WordPressApiService apiService = RetrofitClient.getApiService();
|
||||||
apiService.getPosts().enqueue(new Callback<List<WpPost>>() {
|
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) {
|
public void onResponse(Call<List<WpPost>> call, Response<List<WpPost>> response) {
|
||||||
if (getContext() == null) return;
|
if (getContext() == null) return;
|
||||||
|
|
||||||
|
List<WpPost> postsToShow = new ArrayList<>();
|
||||||
|
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
List<WpPost> wpPosts = response.body();
|
postsToShow = response.body();
|
||||||
SimpleDateFormat rawFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
|
CacheManager.saveNewsPosts(getContext(), postsToShow);
|
||||||
rawFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
} else {
|
||||||
SimpleDateFormat targetFormat = new SimpleDateFormat("dd. MMM yyyy", Locale.getDefault());
|
postsToShow = CacheManager.getCachedNewsPosts(getContext());
|
||||||
targetFormat.setTimeZone(TimeZone.getTimeZone("Europe/Oslo"));
|
if (!postsToShow.isEmpty()) {
|
||||||
|
Toast.makeText(getContext(), "Server utilgjengelig. Viser lagrede nyheter.", Toast.LENGTH_SHORT).show();
|
||||||
for (WpPost post : wpPosts) {
|
|
||||||
try {
|
|
||||||
Date date = rawFormat.parse(post.date);
|
|
||||||
post.date = targetFormat.format(date);
|
|
||||||
} catch (Exception e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
checkLoadingComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<List<WpPost>> call, Throwable t) {
|
public void onFailure(Call<List<WpPost>> call, Throwable t) {
|
||||||
if (getContext() == null) return;
|
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();
|
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() {
|
private void startNotificationWorker() {
|
||||||
PeriodicWorkRequest notifRequest =
|
PeriodicWorkRequest notifRequest =
|
||||||
new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
|
new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
|
||||||
|
|
|
||||||
BIN
app/src/main/res/drawable-hdpi/ic_stat_kbs.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_stat_kbs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 630 B |
BIN
app/src/main/res/drawable-mdpi/ic_stat_kbs.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_stat_kbs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 395 B |
BIN
app/src/main/res/drawable-xhdpi/ic_stat_kbs.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_stat_kbs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 895 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_stat_kbs.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_stat_kbs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_stat_kbs.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_stat_kbs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
1324
hele_prosjektet.txt
1324
hele_prosjektet.txt
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue