From 2e014bbaec803f528ced648fea0f197eeaff470e Mon Sep 17 00:00:00 2001 From: yuval Date: Sun, 31 Aug 2025 20:27:21 +0300 Subject: [PATCH] fixed bugs --- android_app/app/build.gradle | 2 +- .../java/com/asp/android_app/api/UserApi.java | 4 +- .../caching/entities/MailEntity.java | 5 +- .../caching/utils/JsonConverters.java | 20 +++- .../caching/utils/MailMappers.java | 82 ++++++++++++---- .../java/com/asp/android_app/model/Mail.java | 14 ++- .../java/com/asp/android_app/model/User.java | 8 ++ .../model/request/RegisterRequest.java | 39 ++++++++ .../model/request/SendMailRequest.java | 6 +- .../response/{Attachment.java => File.java} | 22 ++--- .../repository/MailRepository.java | 78 +++++++++++---- .../repository/UserRepository.java | 9 +- .../asp/android_app/ui/ReadingActivity.java | 20 ++-- .../ui/auth_activity/SignupFragment.java | 6 +- .../ui/compose_activity/ComposeFragment.java | 35 ++++--- .../ui/inbox_activity/InboxFragment.java | 97 +++++++++++++++++-- .../ui/inbox_activity/MailAdapter.java | 42 +++++++- .../asp/android_app/utils/ComposeParams.java | 8 +- .../android_app/viewmodel/MailViewModel.java | 33 ++++++- .../src/main/res/layout/activity_reading.xml | 2 +- .../src/main/res/layout/compose_fragment.xml | 4 +- .../app/src/main/res/values/strings.xml | 8 +- 22 files changed, 421 insertions(+), 123 deletions(-) create mode 100644 android_app/app/src/main/java/com/asp/android_app/model/request/RegisterRequest.java rename android_app/app/src/main/java/com/asp/android_app/model/response/{Attachment.java => File.java} (72%) diff --git a/android_app/app/build.gradle b/android_app/app/build.gradle index ee7989ab..23c8c8ab 100644 --- a/android_app/app/build.gradle +++ b/android_app/app/build.gradle @@ -12,7 +12,7 @@ android { defaultConfig { applicationId "com.asp.android_app" - minSdk 24 + minSdk 26 targetSdk 35 versionCode 1 versionName "1.0" diff --git a/android_app/app/src/main/java/com/asp/android_app/api/UserApi.java b/android_app/app/src/main/java/com/asp/android_app/api/UserApi.java index 6a6539f7..ba3dd610 100644 --- a/android_app/app/src/main/java/com/asp/android_app/api/UserApi.java +++ b/android_app/app/src/main/java/com/asp/android_app/api/UserApi.java @@ -1,8 +1,8 @@ package com.asp.android_app.api; -import com.asp.android_app.model.User; import com.asp.android_app.model.request.LoginRequest; import com.asp.android_app.model.request.ProfileImageRequest; +import com.asp.android_app.model.request.RegisterRequest; import com.asp.android_app.model.response.AuthResponse; import com.asp.android_app.model.response.UserInfo; import com.asp.android_app.model.response.UserSearchResult; @@ -25,7 +25,7 @@ public interface UserApi { @POST("users") - Call register(@Body User user); + Call register(@Body RegisterRequest request); @POST("tokens") Call login(@Body LoginRequest loginRequest); diff --git a/android_app/app/src/main/java/com/asp/android_app/caching/entities/MailEntity.java b/android_app/app/src/main/java/com/asp/android_app/caching/entities/MailEntity.java index 11e67008..cdfae2df 100644 --- a/android_app/app/src/main/java/com/asp/android_app/caching/entities/MailEntity.java +++ b/android_app/app/src/main/java/com/asp/android_app/caching/entities/MailEntity.java @@ -1,3 +1,4 @@ +// Updated MailEntity.java package com.asp.android_app.caching.entities; import androidx.room.Entity; @@ -8,7 +9,7 @@ * Room cache model for a Mail. * - Denormalized for speed (flags directly on the row). * - We keep a few denormalized sender fields so lists render without joins. - * - Recipients & attachment names are stored as JSON strings (simple & fast). + * - Recipients & attachment data are stored as JSON strings (simple & fast). *

* NOTE: We sort by sentAtEpoch (fallback to createdAtEpoch) to match backend's order. */ @@ -56,7 +57,7 @@ public class MailEntity { // JSON blobs to keep schema simple public String recipientsJson; // List as JSON - public String attachmentNamesJson; // List names only + public String attachmentsJson; // CHANGED: Full File objects as JSON instead of just names // Bookkeeping for cache eviction (simple LRU) public long lastAccessEpoch; // updated whenever we read/open diff --git a/android_app/app/src/main/java/com/asp/android_app/caching/utils/JsonConverters.java b/android_app/app/src/main/java/com/asp/android_app/caching/utils/JsonConverters.java index 5426dd3d..4da0dfff 100644 --- a/android_app/app/src/main/java/com/asp/android_app/caching/utils/JsonConverters.java +++ b/android_app/app/src/main/java/com/asp/android_app/caching/utils/JsonConverters.java @@ -1,9 +1,11 @@ +// Updated JsonConverters.java package com.asp.android_app.caching.utils; import androidx.annotation.Nullable; import androidx.room.TypeConverter; import com.asp.android_app.caching.entities.UserLite; +import com.asp.android_app.model.response.File; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -13,14 +15,16 @@ /** * Room TypeConverters for small JSON blobs. - * - List for attachment names + * - List for simple string lists * - List for recipients + * - List for complete attachment information * Keeping JSON simple avoids extra tables until we actually need them. */ public class JsonConverters { private static final Gson gson = new Gson(); private static final Type LIST_STRING = new TypeToken>() {}.getType(); private static final Type LIST_USERLITE = new TypeToken>() {}.getType(); + private static final Type LIST_FILE = new TypeToken>() {}.getType(); // ADDED @TypeConverter public static String listStringToJson(@Nullable List list) { @@ -43,4 +47,16 @@ public static List jsonToListUserLite(@Nullable String json) { if (json == null || json.isEmpty()) return Collections.emptyList(); return gson.fromJson(json, LIST_USERLITE); } -} + + // ADDED: Converters for File objects + @TypeConverter + public static String listFileToJson(@Nullable List list) { + return gson.toJson(list == null ? Collections.emptyList() : list, LIST_FILE); + } + + @TypeConverter + public static List jsonToListFile(@Nullable String json) { + if (json == null || json.isEmpty()) return Collections.emptyList(); + return gson.fromJson(json, LIST_FILE); + } +} \ No newline at end of file diff --git a/android_app/app/src/main/java/com/asp/android_app/caching/utils/MailMappers.java b/android_app/app/src/main/java/com/asp/android_app/caching/utils/MailMappers.java index c31c0378..0352c3c3 100644 --- a/android_app/app/src/main/java/com/asp/android_app/caching/utils/MailMappers.java +++ b/android_app/app/src/main/java/com/asp/android_app/caching/utils/MailMappers.java @@ -4,7 +4,7 @@ import com.asp.android_app.caching.entities.UserLite; import com.asp.android_app.model.Label; import com.asp.android_app.model.Mail; -import com.asp.android_app.model.response.Attachment; +import com.asp.android_app.model.response.File; import com.asp.android_app.model.response.UserInfo; import com.google.gson.Gson; @@ -26,7 +26,8 @@ private MailMappers() {} public static MailEntity toEntity(Mail m) { MailEntity e = new MailEntity(); e.id = m.getId(); - e.ownerId = m.getSender() != null ? m.getSender().getId() : 0; // server stores owner separately; we keep sender id for "sent" check using fromId==ownerId + e.ownerId = m.getSender() != null ? m.getSender().getId() : 0; + // sender UserInfo from = m.getSender(); e.fromId = (from != null ? from.getId() : 0); @@ -37,20 +38,18 @@ public static MailEntity toEntity(Mail m) { e.subject = m.getSubject(); e.body = m.getBody(); - e.sentAtRaw = m.getSentAt(); // can be null/empty - e.createdAtRaw = m.getSentAt() == null ? "" : m.getSentAt(); // if you also expose createdAt in model, swap here - // you have createdAt in Mail; let's parse both - e.createdAtRaw = m.getClass().getDeclaredFields() != null ? (m.getClass() != null ? m.getClass().getName() : "") : e.createdAtRaw; // noop to avoid warnings - // actually parse safely: + e.sentAtRaw = m.getSentAt(); e.sentAtEpoch = parseIsoToEpoch(m.getSentAt()); - // createdAt exists in Mail class: + + // Handle createdAt properly try { java.lang.reflect.Method getCreatedAt = m.getClass().getMethod("getCreatedAt"); Object raw = getCreatedAt.invoke(m); e.createdAtRaw = raw != null ? raw.toString() : ""; e.createdAtEpoch = parseIsoToEpoch(e.createdAtRaw); } catch (Exception ignore) { - e.createdAtEpoch = 0L; + e.createdAtRaw = e.sentAtRaw; // fallback + e.createdAtEpoch = e.sentAtEpoch; } e.isDraft = m.isDraft(); @@ -68,20 +67,67 @@ public static MailEntity toEntity(Mail m) { } e.recipientsJson = gson.toJson(recips); - // attachment names only - List names = new ArrayList<>(); - if (m.getAttachments() != null) { - for (Attachment a : m.getAttachments()) { - names.add(a.getName()); - } - } - e.attachmentNamesJson = gson.toJson(names); + // FIXED: Store complete File objects instead of just names + e.attachmentsJson = gson.toJson(m.getAttachments() != null ? m.getAttachments() : new ArrayList<>()); // default LRU timestamp now e.lastAccessEpoch = System.currentTimeMillis(); return e; } + /** + * Convert MailEntity back to Mail object with complete attachment information + */ + public static Mail fromEntity(MailEntity e) { + Mail m = new Mail(); + m.setId(e.id); + + // Reconstruct sender + UserInfo sender = new UserInfo(e.fromEmail, e.fromName); + try { + java.lang.reflect.Field fId = sender.getClass().getDeclaredField("id"); + fId.setAccessible(true); + fId.set(sender, e.fromId); + java.lang.reflect.Field fImg = sender.getClass().getDeclaredField("image"); + fImg.setAccessible(true); + fImg.set(sender, e.fromImageUrl); + } catch (Exception ignore) {} + + m.setSubject(e.subject); + m.setBody(e.body); + + // Set dates + try { + java.lang.reflect.Field fSentAt = m.getClass().getDeclaredField("sentAt"); + fSentAt.setAccessible(true); + fSentAt.set(m, e.sentAtRaw); + java.lang.reflect.Field fCreatedAt = m.getClass().getDeclaredField("createdAt"); + fCreatedAt.setAccessible(true); + fCreatedAt.set(m, e.createdAtRaw); + java.lang.reflect.Field fFrom = m.getClass().getDeclaredField("from"); + fFrom.setAccessible(true); + fFrom.set(m, sender); + } catch (Exception ignore) {} + + // flags + m.setIsRead(e.isRead); + m.setStarred(e.isStarred); + m.setTrashed(e.isTrashed); + m.setSpam(e.isSpam); + m.setIsDraft(e.isDraft); + + // FIXED: Reconstruct complete attachments from JSON + try { + java.lang.reflect.Type fileListType = new com.google.gson.reflect.TypeToken>(){}.getType(); + List attachments = gson.fromJson(e.attachmentsJson, fileListType); + m.setAttachments(attachments != null ? attachments : new ArrayList<>()); + } catch (Exception ignore) { + m.setAttachments(new ArrayList<>()); + } + + return m; + } + public static List labelIds(List