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 5a2e999..3876ffd 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 @@ -8,9 +8,15 @@ import com.example.tampopo_client.models.Activity; import com.example.tampopo_client.resources.ActivitiesResource; +import com.example.tampopo_client.resources.UserResource; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import retrofit2.Call; import retrofit2.Callback; @@ -18,6 +24,8 @@ import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; +// NOTE: MainActivityでLiveDataをobserveし表示する + /** * アクティビティを扱うためのViewModel * @@ -26,17 +34,91 @@ */ public class ActivityViewModel extends ViewModel { private final ActivitiesResource activitiesResource; + private final UserResource userResource; - private final MutableLiveData> activitiesLiveData; + private final MutableLiveData> activitiesLiveData; // key=userId, value=activity + private final MutableLiveData> friendUserIdsLiveData; + + private final ScheduledExecutorService fetchActivitiesTaskScheduler; + private ScheduledFuture fetchActivitiesTask; + + private final ScheduledExecutorService fetchFriendIdsTaskScheduler; + private ScheduledFuture fetchFriendIdsTask; public ActivityViewModel() { + // Retrofitの初期化 final Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/tampopo/") .addConverterFactory(JacksonConverterFactory.create()) .build(); activitiesResource = retrofit.create(ActivitiesResource.class); + userResource = retrofit.create(UserResource.class); - activitiesLiveData = new MutableLiveData<>(List.of()); + // LiveDataの初期化 + activitiesLiveData = new MutableLiveData<>(Map.of()); + friendUserIdsLiveData = new MutableLiveData<>(List.of()); + + // スケジューラーの作成 + fetchActivitiesTaskScheduler = Executors.newSingleThreadScheduledExecutor(); + fetchFriendIdsTaskScheduler = Executors.newSingleThreadScheduledExecutor(); + } + + @Override + protected void onCleared() { + super.onCleared(); + + // ViewModelの破棄時にタスクを即停止する + if (fetchActivitiesTaskScheduler != null) { + fetchActivitiesTaskScheduler.shutdownNow(); + } + if (fetchFriendIdsTaskScheduler != null) { + fetchFriendIdsTaskScheduler.shutdownNow(); + } + } + + /** + * 定期的に最新のアクティビティ一覧を取得・更新する + */ + public void startFetchingLatestActivities() { + if (fetchActivitiesTask != null && !fetchActivitiesTask.isDone()) { + return; + } + + final Runnable task = () -> { + if (!friendUserIdsLiveData.isInitialized()) { + return; + } + if (friendUserIdsLiveData.getValue() == null) { + return; + } + for (String userId : friendUserIdsLiveData.getValue()) { + pullLatestActivity(userId); + } + }; + fetchActivitiesTask = fetchActivitiesTaskScheduler.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS); + } + + /** + * 定期的に最新のフレンド一覧を取得・更新する + * + * @param userId ユーザーID + * @param token ユーザーの認証用トークン + */ + public void startFetchingLatestFriends(String userId, String token) { + if (fetchFriendIdsTask != null && !fetchFriendIdsTask.isDone()) { + return; + } + + final Runnable task = () -> { + if (!friendUserIdsLiveData.isInitialized()) { + return; + } + + pullLatestFriendUserIds(userId, token); + + Log.d(ActivityViewModel.class.getSimpleName(), "Polling friends data from the server."); + }; + fetchFriendIdsTask = fetchFriendIdsTaskScheduler.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS); } public void createActivity(String userId, String token, String newActivity) { @@ -52,12 +134,18 @@ getActivityCall.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { - List activities = activitiesLiveData.getValue(); + Map activities = activitiesLiveData.getValue(); if (activities == null) { - activities = new ArrayList<>(); + activities = new HashMap<>(); } + Activity createdActivity = response.body(); - activities.add(createdActivity); + if (activities.containsKey(userId)) { + activities.replace(userId, createdActivity); + } else { + activities.put(userId, createdActivity); + } + activitiesLiveData.postValue(activities); } @@ -73,12 +161,75 @@ @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { - Log.e(ActivityViewModel.class.getSimpleName(), "An error has occurred.", t); + Log.e(ActivityViewModel.class.getSimpleName(), "An error has occurred while creating new activity.", t); } }); } - public MutableLiveData> getActivitiesLiveData() { + /** + * 最新のユーザーのアクティビティを取得・更新する + * + * @param userId 取得対象のユーザーのID + */ + private void pullLatestActivity(String userId) { + Call> fetchActivityCall = activitiesResource.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) { + Activity latestActivity = response.body().get(0); + Map activities = activitiesLiveData.getValue(); + if (activities == null) { + activities = new HashMap<>(); + } + if (activities.containsKey(userId)) { + activities.replace(userId, latestActivity); + } else { + activities.put(userId, latestActivity); + } + + activitiesLiveData.postValue(activities); + } + } + } + + @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); + } + }); + } + + /** + * 最新の自分のフレンドのユーザーIDをすべて取得・更新する。 + * + * @param userId 自分のユーザーID + * @param token 自分のユーザー認証用トークン + */ + private void pullLatestFriendUserIds(String userId, String token) { + Call> fetchFriendUserIdsCall = userResource.getFriends(userId, token); + fetchFriendUserIdsCall.enqueue(new Callback>() { + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + if (response.isSuccessful()) { + friendUserIdsLiveData.postValue(response.body()); + } + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + Log.e(ActivityViewModel.class.getSimpleName(), "An error has occurred while fetching all friends' id.", t); + } + }); + } + + public MutableLiveData> getActivitiesLiveData() { return activitiesLiveData; } + + public MutableLiveData> getFriendUserIdsLiveData() { + return friendUserIdsLiveData; + } }