diff --git a/app/src/main/java/com/example/tampopo_client/resources/ActivitiesResource.java b/app/src/main/java/com/example/tampopo_client/resources/ActivitiesResource.java index 7df8292..c30407e 100644 --- a/app/src/main/java/com/example/tampopo_client/resources/ActivitiesResource.java +++ b/app/src/main/java/com/example/tampopo_client/resources/ActivitiesResource.java @@ -31,8 +31,8 @@ Call getText(@Path("user_id") String userId, @Path("activity_id") String activityId); @GET("users/{user_id}/activities/{activity_id}/updated-time") - Call getUpdatedTime(@Path("user_id") String userId, @Path("activity_id") String activityId); + Call getUpdatedTime(@Path("user_id") String userId, @Path("activity_id") String activityId); @GET("users/{user_id}/activities/last-updated-time") - Call getLastUpdatedTime(@Path("user_id") String userId, @Path("activity_id") String activityId); + Call getLastUpdatedTime(@Path("user_id") String userId, @Path("activity_id") String activityId); } diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/ActivitiesFetchCallback.java b/app/src/main/java/com/example/tampopo_client/viewmodels/ActivitiesFetchCallback.java new file mode 100644 index 0000000..ad6ca5f --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/ActivitiesFetchCallback.java @@ -0,0 +1,11 @@ +package com.example.tampopo_client.viewmodels; + +import com.example.tampopo_client.models.Activity; + +import java.util.List; + +public interface ActivitiesFetchCallback { + void onSuccess(List activities); + + void onFailure(Throwable throwable); +} diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/ActivityViewModel.java b/app/src/main/java/com/example/tampopo_client/viewmodels/ActivityViewModel.java index 2c455f2..08364f9 100644 --- a/app/src/main/java/com/example/tampopo_client/viewmodels/ActivityViewModel.java +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/ActivityViewModel.java @@ -9,6 +9,9 @@ import com.example.tampopo_client.resources.ActivitiesResource; import com.example.tampopo_client.resources.UserResource; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -28,16 +31,24 @@ private final ActivitiesResource activitiesResource; private final UserResource userResource; - private final Map>> friendToActivitiesLiveData; // key=userId - private final MutableLiveData> friendUserIdsLiveData; // フレンドのユーザーIDのリスト private final MutableLiveData myLatestActivityLiveData; // 自分の最新のアクティビティ + private final Map>> friendToActivitiesLiveData; // <フレンドのユーザーID, フレンドのアクティビティのリスト> + private final MutableLiveData> friendUserIdsLiveData; // フレンドのユーザーIDのリスト - private final String userId; - private final String token; + private final String myUserId; + private final String myToken; - public ActivityViewModel(String userId, String token) { - this.userId = userId; - this.token = token; + private final List userActivityStatusChangeListeners; + + /** + * ActivityのViewModelを作成する。 + * + * @param myUserId 自身のユーザーID + * @param myToken 自身の認証用トークン + */ + public ActivityViewModel(String myUserId, String myToken) { + this.myUserId = myUserId; + this.myToken = myToken; // Retrofitの初期化 final Retrofit retrofit = new Retrofit.Builder().baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/tampopo/").addConverterFactory(JacksonConverterFactory.create()).build(); @@ -46,7 +57,9 @@ friendToActivitiesLiveData = Map.of(); friendUserIdsLiveData = new MutableLiveData<>(List.of()); - myLatestActivityLiveData = new MutableLiveData<>(); + myLatestActivityLiveData = new MutableLiveData<>(null); + + userActivityStatusChangeListeners = new ArrayList<>(); } @Override @@ -56,23 +69,97 @@ return; } - // 最新のアクティビティを取得して更新する + // 自分の最新のアクティビティを取得する + pullLatestActivity(myUserId, activitiesResource, new ActivityFetchCallback() { + @Override + public void onSuccess(Activity activity) { + Activity prevActivity = myLatestActivityLiveData.getValue(); + + // アクティビティが更新されていない場合 + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastUpdatedTime = getDateTimeFromString(activity.getUpdateTime()); + + if (now.isAfter(lastUpdatedTime.plusSeconds(5))) { + for (UserActivityStatusChangeListener observer : userActivityStatusChangeListeners) { + // アクティビティのステータス変更をリスナーに通知する + if (activity.equals(prevActivity)) { + observer.onUserStatusChanged(myUserId, UserActivityStatusChangeListener.Status.INACTIVE); + } else { + observer.onUserStatusChanged(myUserId, UserActivityStatusChangeListener.Status.ACTIVE); + } + } + } + + // 自分の新しいアクティビティをLiveDataに反映する + myLatestActivityLiveData.postValue(activity); + } + + @Override + public void onFailure(Throwable throwable) { + Log.e(ActivityViewModel.class.getSimpleName(), "An error has occurred while fetching my latest activity."); + } + }); + + // 自分のフレンドの最新のアクティビティを取得して更新する if (friendUserIdsLiveData.isInitialized() && friendUserIdsLiveData.getValue() != null) { for (String userId : friendUserIdsLiveData.getValue()) { - pullLatestActivity(userId, activitiesResource, friendToActivitiesLiveData); + pullLatestActivity(userId, activitiesResource, new ActivityFetchCallback() { + @Override + public void onSuccess(Activity activity) { + updateFriendToActivitiesLiveData(activity, userId); + } + + @Override + public void onFailure(Throwable throwable) { + Log.e(ActivityViewModel.class.getSimpleName(), "An error has occurred while fetching friend's latest activity.", throwable); + } + }); } } // 最新のフレンドのユーザーIDを取得して更新する if (friendUserIdsLiveData.isInitialized()) { - pullLatestFriendUserIds(userId, token); + pullLatestFriendUserIds(myUserId, myToken); } - - // Logging - Log.d(ActivityViewModel.class.getSimpleName(), "Polling data from the server."); }; } + /** + * ユーザIDからそのユーザーのアクティビティのリストのライブデータを取得し、最新のアクティビティに更新する + * + * @param latestActivity 最新のアクティビティ + * @param userId 更新対象のユーザーID + */ + private void updateFriendToActivitiesLiveData(Activity latestActivity, String userId) { + if (friendToActivitiesLiveData.get(userId) == null) { + friendToActivitiesLiveData.put(userId, new MutableLiveData<>(List.of())); + } + MutableLiveData> userActivitiesLiveData = friendToActivitiesLiveData.get(userId); + assert userActivitiesLiveData != null; + + List userActivities = userActivitiesLiveData.getValue(); + if (userActivities == null) { + return; + } + + if (userActivities.isEmpty()) { + userActivitiesLiveData.postValue(List.of(latestActivity)); + } else { + if (userActivities.equals(List.of(latestActivity))) { + return; + } + + userActivitiesLiveData.postValue(List.of(latestActivity)); + } + } + + /** + * 自身のアクティビティを新しく作成する + * + * @param userId 自身のユーザーID + * @param token 自身の認証用トークン + * @param newActivity 新しいアクティビティのテキスト + */ public void createActivity(String userId, String token, String newActivity) { if (!myLatestActivityLiveData.isInitialized()) { return; @@ -85,7 +172,6 @@ if (response.isSuccessful()) { String createdActivityId = response.body(); - // TODO: 仮作成なので改善したい Call getActivityCall = activitiesResource.getActivity(userId, createdActivityId); getActivityCall.enqueue(new Callback() { @Override @@ -117,54 +203,47 @@ /** * 最新のユーザーのアクティビティを取得・更新する * - * @param userId 取得対象のユーザーのID - * @param resource アクティビティのリソース - * @param friendToActivitiesLiveData フレンドのユーザーIDからアクティビティへの写像のライブデータ + * @param userId 取得対象のユーザーのID + * @param resource アクティビティのリソース + * @param callback アクティビティが取得された後に呼び出されるコールバック */ - private void pullLatestActivity(String userId, ActivitiesResource resource, Map>> friendToActivitiesLiveData) { + private void pullLatestActivity(String userId, ActivitiesResource resource, ActivityFetchCallback callback) { Call> fetchActivityCall = resource.getActivities(userId, "LATEST"); fetchActivityCall.enqueue(new Callback>() { @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { if (response.isSuccessful()) { - List fetchedActivities = response.body(); // アクティビティが存在しない場合は空のリスト - if (fetchedActivities == null) { + List activities = response.body(); // アクティビティが存在しない場合は空のリスト + if (activities == null || activities.isEmpty()) { return; } - if (friendToActivitiesLiveData.get(userId) == null) { - friendToActivitiesLiveData.put(userId, new MutableLiveData<>(List.of())); - } - MutableLiveData> userActivitiesLiveData = friendToActivitiesLiveData.get(userId); - assert userActivitiesLiveData != null; - - List userActivities = userActivitiesLiveData.getValue(); - if (userActivities == null) { - return; - } - if (fetchedActivities.isEmpty()) { - return; - } - - if (userActivities.isEmpty()) { - userActivitiesLiveData.postValue(fetchedActivities); - } else { - if (userActivities.equals(fetchedActivities)) { - return; - } - userActivitiesLiveData.postValue(fetchedActivities); - } + Activity latestActivity = activities.get(0); + callback.onSuccess(latestActivity); } } @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { Log.e(ActivityViewModel.class.getSimpleName(), "An error has occurred while fetching the latest activity.", t); + + callback.onFailure(t); } }); } /** + * String型の日時をLocalDateTime型に変換する + * + * @param dateTime 変換対象の日時(文字列) + * @return 変換後の日時(LocalDateTime) + */ + private LocalDateTime getDateTimeFromString(String dateTime) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"); + return LocalDateTime.parse(dateTime, formatter); + } + + /** * 最新の自分のフレンドのユーザーIDをすべて取得・更新する。 * * @param userId 自分のユーザーID @@ -187,10 +266,23 @@ }); } + /** + * + */ + private void sortFriendsByLatestUpdateTime() { + + } + public MutableLiveData getMyLatestActivityLiveData() { return myLatestActivityLiveData; } + /** + * ユーザIDからそのユーザーのアクティビティのリストのライブデータを取得する + * + * @param userId 取得対象のユーザーID + * @return 取得対象のユーザーのアクティビティのリストのライブデータ + */ public MutableLiveData> getActivitiesLiveDataFromUserId(String userId) { return friendToActivitiesLiveData.get(userId); } @@ -199,11 +291,25 @@ return friendUserIdsLiveData; } - public String getUserId() { - return userId; + public String getMyUserId() { + return myUserId; } - public String getToken() { - return token; + public String getMyToken() { + return myToken; + } + + public void addActivityStatusChangeObserver(UserActivityStatusChangeListener observer) { + userActivityStatusChangeListeners.add(observer); + } + + public void clearActivityStatusChangeObservers() { + userActivityStatusChangeListeners.clear(); + } + + private interface ActivityFetchCallback { + void onSuccess(Activity activity); + + void onFailure(Throwable throwable); } } diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/UserActivityStatusChangeListener.java b/app/src/main/java/com/example/tampopo_client/viewmodels/UserActivityStatusChangeListener.java new file mode 100644 index 0000000..30724cc --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/UserActivityStatusChangeListener.java @@ -0,0 +1,10 @@ +package com.example.tampopo_client.viewmodels; + +public interface UserActivityStatusChangeListener { + void onUserStatusChanged(String userId, Status activityStatus); + + enum Status { + INACTIVE, + ACTIVE + } +}