diff --git a/app/src/main/java/com/example/tampopo_client/models/Chatroom.java b/app/src/main/java/com/example/tampopo_client/models/Chatroom.java index 5a08e92..74622ba 100644 --- a/app/src/main/java/com/example/tampopo_client/models/Chatroom.java +++ b/app/src/main/java/com/example/tampopo_client/models/Chatroom.java @@ -2,21 +2,48 @@ public class Chatroom { private String chatroomId; // チャットルームのID + private String partnerUserId; // 相手ユーザーID + private String user1Id; // 1人目のユーザーID + private String user2Id; // 2人目のユーザーID public Chatroom() {} - - public Chatroom(String chatroomId) { + public Chatroom(String chatroomId, String user1Id, String user2Id) { this.chatroomId = chatroomId; + this.user1Id = user1Id; + this.user2Id = user2Id; + } + + + public Chatroom(String chatroomId,String partnerUserId) { + this.chatroomId = chatroomId; + this.partnerUserId = partnerUserId; } public String getChatroomId() { return chatroomId; } - public String foundChatroomId(){return chatroomId;} - public void setChatroomId(String chatroomId) { this.chatroomId = chatroomId; } + public String getUser1Id() { + return user1Id; + } + + + + public String getUser2Id() { + return user2Id; + } + + + public String getPartnerUserId() { + return partnerUserId; + } + + public void setPartnerUserId(String partnerUserId) { + this.partnerUserId = partnerUserId; + } + } diff --git a/app/src/main/java/com/example/tampopo_client/resources/ChatroomResource.java b/app/src/main/java/com/example/tampopo_client/resources/ChatroomResource.java index 2574364..4384636 100644 --- a/app/src/main/java/com/example/tampopo_client/resources/ChatroomResource.java +++ b/app/src/main/java/com/example/tampopo_client/resources/ChatroomResource.java @@ -27,6 +27,7 @@ @GET("foundChatroom") Call getMyChatroom( @Query("EnterUserId") String userId + ); diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/ChatViewModel.java b/app/src/main/java/com/example/tampopo_client/viewmodels/ChatViewModel.java index 0c5a721..7712406 100644 --- a/app/src/main/java/com/example/tampopo_client/viewmodels/ChatViewModel.java +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/ChatViewModel.java @@ -3,46 +3,267 @@ import android.os.Handler; import android.os.Looper; import android.util.Log; - +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import com.example.tampopo_client.models.ChatMessage; +import com.example.tampopo_client.models.Chatroom; +import com.example.tampopo_client.resources.ChatroomResource; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; public class ChatViewModel extends RealTimeViewModel { + + private final Retrofit retrofit; + private final ChatroomResource chatroomResource; + + // --- LiveData --- + private final MutableLiveData chatroomIdLiveData = new MutableLiveData<>(); + private final MutableLiveData> chatMessages = new MutableLiveData<>(new ArrayList<>()); + private final MutableLiveData latestMessage = new MutableLiveData<>(); + private final MutableLiveData chatroomClosed = new MutableLiveData<>(); + private final HashMap> chatFriendToFriendLiveData = new HashMap<>(); + + // 通話中(リアルタイム監視用) + private final MutableLiveData chatFriendToMeLiveData = new MutableLiveData<>(); + private final MutableLiveData chatToFriendLiveData = new MutableLiveData<>(); + + // ===== 以下は追加部分(Handlerループ) ===== + private final Handler handler = new Handler(Looper.getMainLooper()); + private boolean isChecking = false; // 二重起動防止フラグ + + + // =============================== + // 通知・リアルタイム関連 + // =============================== private final List notificationListeners = new ArrayList<>(); + private boolean notificationSent = false; // 一度だけ通知を送るためのフラグ - //private static final double NOTIFICATION_RECEIVE_PROBABILITY = 0.5; + private String userId; + private String token; + private String chatroomId; - //natty - private boolean notificationSent = false; // 一度だけ送信するためのフラグ + public ChatViewModel() { + this.retrofit = new Retrofit.Builder() + .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/tampopo/") + .addConverterFactory(JacksonConverterFactory.create()) + .build(); + + this.chatroomResource = retrofit.create(ChatroomResource.class); + } + + public ChatViewModel(String userId, String token,String chatroomId) { + this.userId = userId; + this.token = token; + this.chatroomId = chatroomId; -// @Override -// public Runnable onUpdate() { -// return () -> { -// // 1% の確率で onNotificationReceived() が呼び出される -// double borderValue = Math.floor(Math.random() * 100); -// double currentValue = NOTIFICATION_RECEIVE_PROBABILITY * 100; -// if (currentValue >= borderValue) { -// Log.d("ChatViewModel", "Received test notification."); -// notificationListeners.forEach(listener -> listener.onNotificationReceived()); -// } -// }; -// } - //natty + this.retrofit = new Retrofit.Builder() + .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/tampopo/") + .addConverterFactory(JacksonConverterFactory.create()) + .build(); + + this.chatroomResource = retrofit.create(ChatroomResource.class); + } + @Override public Runnable onUpdate() { return () -> { if (!notificationSent) { - // 10秒後に一回だけ通知を送る - notificationSent = true; // もう送らない + notificationSent = true; new Handler(Looper.getMainLooper()).postDelayed(() -> { Log.d("ChatViewModel", "Received test notification (after 10s)."); notificationListeners.forEach(NotificationListener::onNotificationReceived); - }, 10_000); // 10秒(10000ms) + }, 10_000); // 10秒後に通知 } + foundChatroom(userId); + loadLatestMessage(chatroomId, userId, token); }; } + + // getter + public MutableLiveData getChatroomIdLiveData() { return chatroomIdLiveData; } + public MutableLiveData> getChatMessages() { return chatMessages; } + public MutableLiveData getLatestMessageLiveData() { return latestMessage; } + public MutableLiveData getChatroomClosed() { return chatroomClosed; } + public MutableLiveData getChatFriendToMeLiveData() { return chatFriendToMeLiveData; } + public MutableLiveData getChatToFriendLiveData() { return chatToFriendLiveData; } + + // =============================== + // 1. チャットルームに入る(かける側) + // =============================== + public void enterChatroom(String myId, String partnerId, String token) { + Call call = chatroomResource.enterChatroom(myId, partnerId, token); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful() && response.body() != null) { + chatroomIdLiveData.setValue(response.body().getChatroomId()); + Log.d("ChatVM", "enterChatroom success → chatroomId: " + response.body().getChatroomId()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + Log.e("ChatVM", "enterChatroom error: " + t.getMessage()); + } + }); + } + + // 1.5 自分がchatroomに入っているのか確認する + public void foundChatroom(String userId) { + Call call = chatroomResource.getMyChatroom(userId); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful() && response.body() != null) { + Chatroom chatroom = response.body(); + + chatroomIdLiveData.setValue(chatroom.getChatroomId()); + chatFriendToMeLiveData.setValue(chatroom.getPartnerUserId()); + } else { + chatroomIdLiveData.setValue(null); + chatroomClosed.setValue(true); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + Log.e("ChatroomVM", "checkCurrentChatroom error: " + t.getMessage()); + } + }); + } + + // =============================== + // 1.55 現在通話中のペアを確認 + // =============================== + public void fetchActiveChatPair(String userId, String token) { + Call call = chatroomResource.getMyChatroom(userId); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful() && response.body() != null) { + Chatroom activeRoom = response.body(); + chatroomIdLiveData.setValue(activeRoom.getChatroomId()); + + String user1 = activeRoom.getUser1Id(); + String user2 = activeRoom.getUser2Id(); + + // ログインユーザーが user1 の場合 + if (userId.equals(user1)) { + chatFriendToMeLiveData.setValue(user2); // もう一方のユーザーを LiveData にセット + // HashMap にもセット + chatFriendToFriendLiveData.put(userId, new MutableLiveData<>(user2)); + } + // ログインユーザーが user2 の場合 + else if (userId.equals(user2)) { + chatToFriendLiveData.setValue(user1); // もう一方のユーザーを LiveData にセット + chatFriendToFriendLiveData.put(userId, new MutableLiveData<>(user1)); + } + + Log.d("ChatVM", "Active pair found: " + user1 + " ↔ " + user2); + } else { + chatroomIdLiveData.setValue(null); + chatroomClosed.setValue(true); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + Log.e("ChatVM", "fetchActiveChatPair error: " + t.getMessage()); + } + }); + } + + + // =============================== + // 2. メッセージ送信 + // =============================== + public void sendMessage(String chatroomId, String senderId, String message, String token, boolean isFromMainActivity) { + Call call = chatroomResource.sendMessage(token, chatroomId, senderId, message); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful() && response.body() != null) { + ChatMessage newMessage = response.body(); + if (isFromMainActivity) { + chatFriendToMeLiveData.setValue(newMessage.getSenderId()); + } else { + latestMessage.setValue(newMessage); + } + } + } + + @Override + public void onFailure(Call call, Throwable t) { + Log.e("ChatVM", "sendMessage error: " + t.getMessage()); + } + }); + } + + // =============================== + // 3. 最新メッセージ取得 + // =============================== + public void loadLatestMessage(String chatroomId, String userId, String token) { + Call> call = chatroomResource.getMessages(token, chatroomId, userId); + call.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful() && response.body() != null && !response.body().isEmpty()) { + latestMessage.setValue(response.body().get(response.body().size() - 1)); + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + Log.e("ChatVM", "loadLatestMessage error: " + t.getMessage()); + } + }); + } + + // =============================== + // 4. チャットルーム削除 + // =============================== + public void destroyChatroom(String chatroomId, String userId, String token) { + Call call = chatroomResource.destroyChatroom(token, chatroomId, userId); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + chatroomClosed.setValue(true); + if (!response.isSuccessful()) { + Log.e("ChatVM", "destroyChatroom failed: " + response.code()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + chatroomClosed.setValue(true); + Log.e("ChatVM", "destroyChatroom error: " + t.getMessage()); + } + }); + } + + // ===== ここから Handlerループ部分をそのまま追記 ===== + /** + * 10秒ごとにサーバー確認を行う + */ + private void startServerCheck() { + handler.postDelayed(new Runnable() { + @Override + public void run() { + Log.d("ChatViewModel", "Checking server / Received test notification."); + notificationListeners.forEach(NotificationListener::onNotificationReceived); + handler.postDelayed(this, 10_000); + } + }, 10_000); + } + public void addNotificationListener(NotificationListener listener) { notificationListeners.add(listener); } @@ -50,4 +271,12 @@ public void clearNotificationListener() { notificationListeners.clear(); } + + /** + * 定期確認を止めたい場合に使う + */ + public void stopServerCheck() { + handler.removeCallbacksAndMessages(null); + isChecking = false; + } } diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/ChatViewModelFactory.java b/app/src/main/java/com/example/tampopo_client/viewmodels/ChatViewModelFactory.java new file mode 100644 index 0000000..48d5ce0 --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/ChatViewModelFactory.java @@ -0,0 +1,34 @@ +package com.example.tampopo_client.viewmodels; + +import androidx.annotation.NonNull; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; + +import java.lang.reflect.InvocationTargetException; + +public class ChatViewModelFactory implements ViewModelProvider.Factory { + private final String userId; + private final String token; + private final String chatroomId; + + public ChatViewModelFactory(String userId, String token, String chatroomId) { + this.userId = userId; + this.token = token; + this.chatroomId = chatroomId; + } + + @NonNull + @Override + public T create(@NonNull Class modelClass) { + if (modelClass.isAssignableFrom(ChatViewModel.class)) { + try { + return modelClass.getConstructor(String.class, String.class).newInstance(userId, token); + } catch (InvocationTargetException | IllegalAccessException | InstantiationException | + NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + throw new IllegalStateException("作成するViewModelが異なります。"); + } +} + diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/ChatroomViewModel.java b/app/src/main/java/com/example/tampopo_client/viewmodels/ChatroomViewModel.java deleted file mode 100644 index 901b43c..0000000 --- a/app/src/main/java/com/example/tampopo_client/viewmodels/ChatroomViewModel.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.example.tampopo_client.viewmodels; - -import android.util.Log; - -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; - -import com.example.tampopo_client.models.ChatMessage; -import com.example.tampopo_client.models.Chatroom; -import com.example.tampopo_client.resources.ChatroomResource; - -import java.util.ArrayList; -import java.util.List; - -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; -import retrofit2.Retrofit; -import retrofit2.converter.jackson.JacksonConverterFactory; - -public class ChatroomViewModel extends ViewModel { - - private final Retrofit retrofit; - private final ChatroomResource chatroomResource; - - private final MutableLiveData chatroomId = new MutableLiveData<>(); - private final MutableLiveData> chatMessages = new MutableLiveData<>(new ArrayList<>()); - private final MutableLiveData latestMessage = new MutableLiveData<>(); - private final MutableLiveData chatroomClosed = new MutableLiveData<>(); - - public ChatroomViewModel() { - this.retrofit = new Retrofit.Builder() - .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/tampopo/") - .addConverterFactory(JacksonConverterFactory.create()) - .build(); - - this.chatroomResource = retrofit.create(ChatroomResource.class); - } - - // LiveData getter - public MutableLiveData getChatroomIdLiveData() { - return chatroomId; - } - - public MutableLiveData> getChatMessages() { - return chatMessages; - } - - public MutableLiveData getLatestMessageLiveData() { - return latestMessage; - } - - public MutableLiveData getChatroomClosed() { - return chatroomClosed; - } - - // 1. チャットルームに入る(入れる側) - public void enterChatroom(String myId, String partnerId, String token) { - //自分のIdと相手のidと自分tokenを送る - Call call = chatroomResource.enterChatroom(myId, partnerId, token); - call.enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - if (response.isSuccessful() && response.body() != null) { - chatroomId.setValue(response.body().getChatroomId()); - } - }//サーバーからchatroomIdが送られてきてそれを保持する処理 - - @Override - public void onFailure(Call call, Throwable t) { - Log.e("ChatViewModel", "enterChatroom error: " + t.getMessage()); - } - }); - } - - //1.5自分がcahtroomに入っているのかを確認する - public void foundChatroom(String userId) { - Call call = chatroomResource.getMyChatroom(userId); - call.enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - if (response.isSuccessful() && response.body() != null) { - chatroomId.setValue(response.body().getChatroomId()); - //これがあっているのか確認chatroomIdどんなうごきをするのか確認せよ - } else { - chatroomId.setValue(null); // どこにも入っていない場合 - chatroomClosed.setValue(true); - } - } - - @Override - public void onFailure(Call call, Throwable t) { - Log.e("ChatroomVM", "checkCurrentChatroom error: " + t.getMessage()); - } - }); - } - - // 2. メッセージ送信(リアルタイム風)送る側 - //自分のchatはmychatmesaageに変更するかも - public void sendMessage(String chatroomId, String senderId, String content, String token) { - //このメソッドは 指定したチャットルーム (chatroomId) に、あるユーザー (senderId) が、入力したメッセージ (message) を、認証トークン (token) を使って送信する役割です。 - Call call = chatroomResource.sendMessage(token, chatroomId, senderId, content); - //Retrofit を使って 「サーバーのAPIにメッセージ送信リクエストを作る」 メソッド。 - call.enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - if (response.isSuccessful() && response.body() != null) { - ChatMessage newMessage = response.body(); - // 成功したらそのまま新しいメッセージをUIに流す - -// // 既存のメッセージリストを更新 - //受け取る側のコードっぽいので不要だと思ってます理由はnewMessageでUIを表示するのでそれ以外いらない -// //画面に表示されているメッセージリスト (chatMessages) を取り出し、そこに 新しいメッセージを追加して更新しています。 -// List current = chatMessages.getValue(); -// if (current == null) current = new ArrayList<>(); - - - // operationResult は表示不要なので更新しない - } - // 失敗した場合も「エラーです」とは出さない(履歴を残さない仕様) - } - - @Override - public void onFailure(Call call, Throwable t) { - // ネットワークエラーでも通知はしない(UI上に履歴が残らない通話風チャットだから) - // 必要なら内部ログにだけ残す - Log.e("ChatViewModel", "sendMessage error: " + t.getMessage()); - } - }); - } - - - // 3. 最新メッセージ取得(エラー時はUIに表示しない)受け取る側 - public void loadLatestMessage(String chatroomId, String partnerId, String token) { - Call> call = chatroomResource.getMessages(token, chatroomId, partnerId); - call.enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - if (response.isSuccessful() && response.body() != null && !response.body().isEmpty()) { - // 最新メッセージだけ反映 - latestMessage.setValue(response.body().get(response.body().size() - 1)); - } - // else の場合は何もしない(UIにエラーを出さない) - } - - @Override - public void onFailure(Call> call, Throwable t) { - // ネットワークエラー時もUIには表示しない - Log.e("ChatViewModel", "loadLatestMessage error: " + t.getMessage()); - } - }); - } - - - // 4. チャットルーム削除 削除を実行した人の処理。削除された側の処理は含まれてません - public void destroyChatroom(String chatroomId, String userId, String token) { - Call call = chatroomResource.destroyChatroom(token, chatroomId, userId); - call.enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - // 成功でも失敗でも、画面を閉じる処理をUIに通知 - // 例えば LiveData を使って Activity/Fragment に伝える - chatroomClosed.setValue(true); - - // ログには残す - if (!response.isSuccessful()) { - Log.e("ChatViewModel", "destroyChatroom failed: " + response.code()); - } - } - - @Override - public void onFailure(Call call, Throwable t) { - // ネットワークエラーでも UI は閉じる - chatroomClosed.setValue(true); - Log.e("ChatViewModel", "destroyChatroom error: " + t.getMessage()); - } - }); - } -} - diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/FriendReceivedRequestViewModel.java b/app/src/main/java/com/example/tampopo_client/viewmodels/FriendReceivedRequestViewModel.java index 0b01c76..2fe45a8 100644 --- a/app/src/main/java/com/example/tampopo_client/viewmodels/FriendReceivedRequestViewModel.java +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/FriendReceivedRequestViewModel.java @@ -17,58 +17,80 @@ import retrofit2.converter.scalars.ScalarsConverterFactory; public class FriendReceivedRequestViewModel extends ViewModel { - //サーバー(API)と通信するためのツール + // サーバー(API)と通信するためのツール private final Retrofit retrofit; - //APIの窓口 + // API の窓口 private final FriendRequestsResource friendRequestsResource; - //自分が受け取った申請 + // 自分が受け取った申請 private final MutableLiveData> receivedRequests; - //通信結果の状態 + // 通信結果の状態 private final MutableLiveData operationResult; - public FriendReceivedRequestViewModel() { + // 直近で使用したユーザーIDをキャッシュしておく + private String cachedUserId; + private String cachedToken; + public FriendReceivedRequestViewModel() { this.retrofit = new Retrofit.Builder() .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/tampopo/") .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(JacksonConverterFactory.create()) .build(); + this.friendRequestsResource = retrofit.create(FriendRequestsResource.class); this.receivedRequests = new MutableLiveData<>(new ArrayList<>()); this.operationResult = new MutableLiveData<>(); } - //viewがobserve出来るように + // viewがobserve出来るように public MutableLiveData> getReceivedRequestsLiveData() { return receivedRequests; } - //サーバーから受け取ったFriendReceivedRequestのデータを格納してキャッシュしていくぞ - public void loadReceivedRequests(String token) { - //tokenを渡して、受信フレンド申請一覧を取得するHTTPリクエスト(Webのサーバーに対して何かをお願いするメッセージ」)を作る準備をしている + public MutableLiveData getOperationResultLiveData() { + return operationResult; + } + + // サーバーから受け取った FriendRequest のデータを読み込む + public void loadReceivedRequests(String token, String myUserId) { + // キャッシュ保存(削除後の再読み込み用) + this.cachedUserId = myUserId; + this.cachedToken = token; + Call> call = friendRequestsResource.getFriendRequests(token); - //call.enqueueでサーバーへ送信(何を?) call.enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { if (response.isSuccessful()) { - //通信が成功したらLiveDataへのキャッシュ - receivedRequests.setValue(response.body()); - operationResult.setValue("Success"); - System.out.println("Success SetValue" + response.body()); + List allRequests = response.body(); + if (allRequests == null) allRequests = new ArrayList<>(); + + // ✅ 自分が受け取ったものだけを抽出 + List receivedOnly = new ArrayList<>(); + for (FriendRequest req : allRequests) { + if (req.getReceiverId() != null && req.getReceiverId().equals(myUserId)) { + receivedOnly.add(req); + } + } + + receivedRequests.setValue(receivedOnly); + operationResult.setValue("Success (filtered " + receivedOnly.size() + " requests)"); + System.out.println("Success SetValue (filtered): " + receivedOnly); + } else { operationResult.setValue("Error: " + response.code()); - System.out.println("response error"); + System.out.println("Response error: " + response.code()); } } @Override public void onFailure(Call> call, Throwable t) { operationResult.setValue("Network error: " + t.getMessage()); - System.out.println("ネットワークエラー: " + t); + System.out.println("Network error: " + t); } }); } + // フレンドリクエスト削除メソッド public void deleteFriendRequest(String friendRequestId, String token) { Call call = friendRequestsResource.deleteFriendRequest(friendRequestId, token); @@ -78,8 +100,13 @@ public void onResponse(Call call, Response response) { if (response.isSuccessful()) { operationResult.setValue("Friend request deleted successfully."); - loadReceivedRequests(token); // 削除後、一覧を更新 System.out.println("Deleted friend request ID: " + friendRequestId); + + // ✅ キャッシュがある場合は再読み込み + if (cachedToken != null && cachedUserId != null) { + loadReceivedRequests(cachedToken, cachedUserId); + } + } else { operationResult.setValue("Error deleting request: " + response.code()); System.out.println("Error deleting request: " + response.code()); @@ -97,3 +124,4 @@ + diff --git a/app/src/main/java/com/example/tampopo_client/views/FriendIconView.java b/app/src/main/java/com/example/tampopo_client/views/FriendIconView.java index 93b6087..61ad06e 100644 --- a/app/src/main/java/com/example/tampopo_client/views/FriendIconView.java +++ b/app/src/main/java/com/example/tampopo_client/views/FriendIconView.java @@ -1,73 +1,98 @@ package com.example.tampopo_client.views; -import static androidx.core.content.ContextCompat.startActivity; - +import android.app.Dialog; import android.content.Context; import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; -import android.text.TextPaint; import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.widget.Button; import android.widget.FrameLayout; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; -import androidx.activity.EdgeToEdge; import androidx.lifecycle.Observer; import com.bumptech.glide.Glide; import com.example.tampopo_client.R; import com.example.tampopo_client.models.Activity; -import com.example.tampopo_client.viewmodels.ActivityViewModel; import com.google.android.material.imageview.ShapeableImageView; import java.util.List; -import java.util.Map; -/** - * TODO: document your custom view class. - */ -public class FriendIconView extends FrameLayout implements Observer>{ - private String mExampleString; // TODO: use a default from R.string... -// private int mExampleColor = Color.RED; // TODO: use a default from R.color... - private float mExampleDimension = 0; // TODO: use a default from R.dimen... - private Drawable mExampleDrawable; - - private TextPaint mTextPaint; - private float mTextWidth; - private float mTextHeight; +public class FriendIconView extends FrameLayout { private ShapeableImageView mFriendIcon; private ImageView mFriendCommentImage; private TextView mFriendComment; private TextView mFriendNickname; private ImageView mFriendChatNotification; private boolean chatNotification = false; - private Observer> activityObserver; - String friendActivity = "123"; + private String friendActivity; + private String userId; + private String chatroomId = null; + + // TODO: CHANGE + //ActivityLiveData + private final Observer> activitiesObserver = new Observer>() { + @Override + public void onChanged(List activityList) { + if (activityList != null && !activityList.isEmpty()) { + Activity act = activityList.get(0); + friendActivity = act.getText(); + setComment(act.getUserId()); + } + } + }; + + //userLiveData + private final Observer userObserver = new Observer() { + @Override + public void onChanged(String user) { + } + }; + + //chatLiveData + private final Observer chatObserver = new Observer() { + @Override + public void onChanged(String chat) { + } + }; + + public Observer> getActivitiesObserver() { + return activitiesObserver; + } + + public Observer getChatObserver() { + return chatObserver; + } + + public FriendIconView(Context context, String userId) { + this(context); + this.userId = userId; + init(null, 0); + } + public FriendIconView(Context context) { super(context); + this.userId = null; init(null, 0); } public FriendIconView(Context context, AttributeSet attrs) { super(context, attrs); + this.userId = null; init(attrs, 0); } public FriendIconView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + this.userId = null; init(attrs, defStyle); } private void init(AttributeSet attrs, int defStyle) { - LayoutInflater.from(getContext()).inflate(R.layout.sample_friend_icon_view,this,true); + LayoutInflater.from(getContext()).inflate(R.layout.sample_friend_icon_view, this, true); mFriendCommentImage = findViewById(R.id.friend_comment_image); mFriendComment = findViewById(R.id.friend_comment); mFriendNickname = findViewById(R.id.friend_nickname); @@ -75,64 +100,60 @@ mFriendChatNotification = findViewById(R.id.chat_notification); //true(チャット通知が来た時)なら表示 - if(chatNotification){ + if (chatNotification) { mFriendChatNotification.setVisibility(View.VISIBLE); } //false(チャット通知が来ていない)なら非表示 - else{ + else { mFriendChatNotification.setVisibility(View.GONE); } //iconを押したらチャットを始めることができる(チャットのダイアログができたらFriendActivityを変更) mFriendIcon.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { - Context ctx = getContext(); - Intent intent = new Intent(ctx, ChatActivity. class); - ctx.startActivity(intent); + if (chatNotification) { + Context ctx = getContext(); + Intent intent = new Intent(ctx, ChatActivity.class); + ctx.startActivity(intent); + mFriendChatNotification.setVisibility(View.GONE); + } else { + // 通話をかける場合 + showCallRequestDialog(getContext(), FriendIconView.this.userId); + } } }); -// if(attrs != null){ -// TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FriendIconView, defStyle, 0); -// String nickname = a.getString(R.styleable.FriendIconView_nickname); -// if(nickname != null){ -// setNickname(nickname); -// } -// Drawable iconDrawable = a.getDrawable(R.styleable.FriendIconView_iconSrc); -// if(iconDrawable != null){ -// mFriendIcon.setImageDrawable(iconDrawable); -// } -// a.recycle(); -// } + } - // Load attributes -// final TypedArray a = getContext().obtainStyledAttributes( -// attrs, R.styleable.FriendIconView, defStyle, 0); -// -// mExampleString = a.getString( -// R.styleable.FriendIconView_exampleString); -// mExampleColor = a.getColor( -// R.styleable.FriendIconView_exampleColor, -// mExampleColor); -// // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with -// // values that should fall on pixel boundaries. -// mExampleDimension = a.getDimension( -// R.styleable.FriendIconView_exampleDimension, -// mExampleDimension); -// -// if (a.hasValue(R.styleable.FriendIconView_exampleDrawable)) { -// mExampleDrawable = a.getDrawable( -// R.styleable.FriendIconView_exampleDrawable); -// mExampleDrawable.setCallback(this); -// } + public void showCallRequestDialog(Context context, String fromUserName) { + Dialog dialog = new Dialog(context); + dialog.setContentView(R.layout.dialog_chat_receved); + dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); + dialog.getWindow().setDimAmount(0.5f); -// // Set up a default TextPaint object -// mTextPaint = new TextPaint(); -// mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); -// mTextPaint.setTextAlign(Paint.Align.LEFT); -// -// // Update TextPaint and text measurements from attributes -// invalidateTextPaintAndMeasurements(); -// + // メッセージテキスト + TextView tvMessage = dialog.findViewById(R.id.tv_message); + tvMessage.setText(fromUserName + " さんと通話を開始しますか?"); + + // 開始ボタン + Button btnStart = dialog.findViewById(R.id.btn_start); + btnStart.setOnClickListener(v -> { + Toast.makeText(context, "通話を開始しました", Toast.LENGTH_SHORT).show(); + dialog.dismiss(); + + // ChatActivityに画面遷移する + Context ctx = getContext(); + Intent intent = new Intent(ctx, ChatActivity.class); + intent.putExtra("friendId", userId); + intent.putExtra("chatroomId", chatroomId); + + ctx.startActivity(intent); + }); + + // キャンセルボタン + Button btnCancel = dialog.findViewById(R.id.btn_cancel); + btnCancel.setOnClickListener(v -> dialog.dismiss()); + + dialog.show(); } public void setNickname(String nickname) { @@ -141,17 +162,22 @@ } } + public void setChatroomId(String chatroomId) { + this.chatroomId = chatroomId; + } + public void setImageResource(int resId) { if (mFriendIcon != null) { mFriendIcon.setImageResource(resId); } } - public ImageView getImageView(){ + public ImageView getImageView() { return mFriendIcon; } - public void setComment(String comment){ - if(mFriendComment != null){ + + public void setComment(String comment) { + if (mFriendComment != null) { int comment_length = comment.length(); if (comment_length > 20) { mFriendComment.setTextSize(7); @@ -166,156 +192,46 @@ // public void setActivityLiveDataObserver(String uid, ActivityViewModel viewModel){ // activityObserver = new Observer>() { - @Override - public void onChanged(List activityList) { - if(activityList != null && !activityList.isEmpty()){ - Activity act = activityList.get(0); - friendActivity = act.getText(); - setAccount(act.getUserId()); - } - } + + // TODO: CHANGE +// @Override +// public void onChanged(List activityList) { +// if (activityList != null && !activityList.isEmpty()) { +// Activity act = activityList.get(0); +// friendActivity = act.getText(); +// setAccount(act.getUserId()); +// } +// } // }; // viewModel.getActivitiesLiveDataFromUserId(uid).observeForever(activityObserver); // } - //uidを引数にカスタムビューにニックネーム,コメント,アイコンをセットする - public void setAccount(String uid){ - setNickname("haru"); - setComment((friendActivity)); - String imageUrl = "http://nitta-lab-www.is.konan-u.ac.jp/tampopo-data/icon" + uid + ".jpg"; - Glide.with(getContext()) - .load(imageUrl) - .into(getImageView()); + /** + * FriendIconViewのアイコン画像を更新する + * + * @param iconUrl アイコン画像のURL + */ + public void setIconUrl(String iconUrl) { + Glide.with(getContext()).load(iconUrl).into(getImageView()); } - public void setChatNotification(boolean chat){ + + //uidを引数にカスタムビューにニックネーム,コメント,アイコンをセットする +// public void setAccount(String uid, String iconUrl) { +// this.userId = uid; +// setNickname("nitta"); +// //String imageUrl = "http://nitta-lab-www.is.konan-u.ac.jp/tampopo-data/icon" + uid + ".jpg"; +// Glide.with(getContext()).load(iconUrl).into(getImageView()); +// } + + public void setChatNotification(boolean chat) { //チャットを終了するときのonclickで一緒にsetChatNotification(false)もする(アイコン周りの赤丸を消す) //true(チャット通知が来た時)なら表示 - if(chat){ + if (chat) { mFriendChatNotification.setVisibility(View.VISIBLE); } //false(チャット通知が来ていない)なら非表示 - else{ + else { mFriendChatNotification.setVisibility(View.GONE); } } -// private void invalidateTextPaintAndMeasurements() { -// mTextPaint.setTextSize(mExampleDimension); -// mTextPaint.setColor(mExampleColor); -// mTextWidth = mTextPaint.measureText(mExampleString); -// -// Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); -// mTextHeight = fontMetrics.bottom; -// } - -// @Override -// protected void onDraw(Canvas canvas) { -// super.onDraw(canvas); -// -// // TODO: consider storing these as member variables to reduce -// // allocations per draw cycle. -// int paddingLeft = getPaddingLeft(); -// int paddingTop = getPaddingTop(); -// int paddingRight = getPaddingRight(); -// int paddingBottom = getPaddingBottom(); -// -// int contentWidth = getWidth() - paddingLeft - paddingRight; -// int contentHeight = getHeight() - paddingTop - paddingBottom; -// -// // Draw the text. -// canvas.drawText(mExampleString, -// paddingLeft + (contentWidth - mTextWidth) / 2, -// paddingTop + (contentHeight + mTextHeight) / 2, -// mTextPaint); -// -// // Draw the example drawable on top of the text. -// if (mExampleDrawable != null) { -// mExampleDrawable.setBounds(paddingLeft, paddingTop, -// paddingLeft + contentWidth, paddingTop + contentHeight); -// mExampleDrawable.draw(canvas); -// } -// } - - /** - * Gets the example string attribute value. - * - * @return The example string attribute value. - */ - public String getExampleString() { - return mExampleString; - } - - /** - * Sets the view"s example string attribute value. In the example view, this string - * is the text to draw. - * - * @param exampleString The example string attribute value to use. - */ -// public void setExampleString(String exampleString) { -// mExampleString = exampleString; -// invalidateTextPaintAndMeasurements(); -// } - - /** - * Gets the example color attribute value. - * - * @return The example color attribute value. - */ -// public int getExampleColor() { -// return mExampleColor; -// } - - /** - * Sets the view"s example color attribute value. In the example view, this color - * is the font color. - * - * @param exampleColor The example color attribute value to use. - */ -// public void setExampleColor(int exampleColor) { -// mExampleColor = exampleColor; -// invalidateTextPaintAndMeasurements(); -// } - - /** - * Gets the example dimension attribute value. - * - * @return The example dimension attribute value. - */ - public float getExampleDimension() { - return mExampleDimension; - } - - /** - * Sets the view"s example dimension attribute value. In the example view, this dimension - * is the font size. - * - * @param exampleDimension The example dimension attribute value to use. - */ -// public void setExampleDimension(float exampleDimension) { -// mExampleDimension = exampleDimension; -// invalidateTextPaintAndMeasurements(); -// } - - /** - * Gets the example drawable attribute value. - * - * @return The example drawable attribute value. - */ - public Drawable getExampleDrawable() { - return mExampleDrawable; - } - - /** - * Sets the view"s example drawable attribute value. In the example view, this drawable is - * drawn above the text. - * - * @param exampleDrawable The example drawable attribute value to use. - */ - public void setExampleDrawable(Drawable exampleDrawable) { - mExampleDrawable = exampleDrawable; - } - -// @Override -// public void onChanged(List activityList) { -// activityList.get(0). -// } } diff --git a/app/src/main/java/com/example/tampopo_client/views/MainActivity.java b/app/src/main/java/com/example/tampopo_client/views/MainActivity.java index d75baff..4ae89c3 100644 --- a/app/src/main/java/com/example/tampopo_client/views/MainActivity.java +++ b/app/src/main/java/com/example/tampopo_client/views/MainActivity.java @@ -39,12 +39,13 @@ import com.example.tampopo_client.Tampopo; import com.example.tampopo_client.models.Activity; import com.example.tampopo_client.viewmodels.ActivityViewModel; +import com.example.tampopo_client.viewmodels.ChatViewModelFactory; +import com.example.tampopo_client.viewmodels.UserViewModel; import com.google.android.material.imageview.ShapeableImageView; import com.example.tampopo_client.viewmodels.ActivityViewModelFactory; import com.example.tampopo_client.viewmodels.ChatViewModel; import com.example.tampopo_client.viewmodels.NotificationListener; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -62,8 +63,10 @@ private Map userViews = new HashMap<>(); ActivityViewModel activityViewModel; + private UserViewModel userViewModel; Tampopo tampopo; + //追加しました! private ChatViewModel chatViewModel; @@ -92,6 +95,10 @@ } } + // TODO: CHANGE + userViewModel = new ViewModelProvider(this).get(UserViewModel.class); + + // handleIncomingIntent(getIntent()); //メイン画面からフレンド一覧画面への遷移 ImageButton friendButton = (ImageButton) findViewById(R.id.friend); @@ -109,9 +116,10 @@ ActivityViewModelFactory factory = new ActivityViewModelFactory(tampopo.getUserId(), tampopo.getToken()); // Factoryを使って、引数をコンストラクタにわたしつつViewModelを作成 activityViewModel = new ViewModelProvider(this, factory).get(ActivityViewModel.class); + //追加しました!!!!!!!!!!! // ChatViewModelを初期化する - chatViewModel = new ViewModelProvider(this).get(ChatViewModel.class); - + ChatViewModelFactory factory1 = new ChatViewModelFactory(tampopo.getUserId(), tampopo.getToken(), tampopo.getChatroomId()); + chatViewModel = new ViewModelProvider(this, factory1).get(ChatViewModel.class); // MutableLiveData>friendUserIdsLiveDate = activityViewModel.getFriendUserIdsLiveData(); // friendUserIdsLiveDate.observe(this, new Observer>() { @@ -353,7 +361,9 @@ // Mapに登録、画面に追加 userViews.put(friendId, container); messageList.addView(container); - activitiesLiveData.observeForever(container); + + // TODO: CHANGE + activitiesLiveData.observeForever(container.getActivitiesObserver()); // } else { // // 既に表示されている → コメントだけ更新 diff --git a/app/src/main/java/com/example/tampopo_client/views/TestFriendIconActivity.java b/app/src/main/java/com/example/tampopo_client/views/TestFriendIconActivity.java index b70cc11..ffb0a47 100644 --- a/app/src/main/java/com/example/tampopo_client/views/TestFriendIconActivity.java +++ b/app/src/main/java/com/example/tampopo_client/views/TestFriendIconActivity.java @@ -33,26 +33,28 @@ float density = getResources().getDisplayMetrics().density; //FriendIconView 1個目 + String uid = "haru"; FriendIconView view1 = new FriendIconView(this); + view1.setAccount(uid); FrameLayout.LayoutParams params1 = new FrameLayout.LayoutParams( - (int) (160*density), // width in px - (int) (100*density) // height in px + (int) (250*density), // width in px + (int) (250*density) // height in px ); view1.setLayoutParams(params1); - view1.setX(100); // px単位 + view1.setX(130); // px単位 view1.setY(150); rootLayout.addView(view1); //FriendIconView 2個目 FriendIconView view2 = new FriendIconView(this); FrameLayout.LayoutParams params2 = new FrameLayout.LayoutParams( - (int) (160*density), - (int) (100*density) + (int) (250*density), + (int) (250*density) ); - view1.setLayoutParams(params2); - view1.setX(100); - view1.setY(150); - rootLayout.addView(view1); + view2.setLayoutParams(params2); + view2.setX(200); + view2.setY(600); + rootLayout.addView(view2); setContentView(rootLayout); // diff --git a/app/src/main/res/layout/activity_test_friend_icon.xml b/app/src/main/res/layout/activity_test_friend_icon.xml index 958bb93..cd14344 100644 --- a/app/src/main/res/layout/activity_test_friend_icon.xml +++ b/app/src/main/res/layout/activity_test_friend_icon.xml @@ -8,26 +8,26 @@ android:background="#E0F7FA" tools:context=".views.TestFriendIconActivity"> - + + + + + + + + + + - + + + + + + + + + + + \ No newline at end of file