diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0fbac27..7215bfc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -35,6 +35,7 @@ } dependencies { + implementation("com.github.bumptech.glide:glide:4.16.0") implementation(libs.appcompat) implementation(libs.material) implementation(libs.activity) diff --git a/app/src/main/java/com/example/tampopo_client/Tampopo.java b/app/src/main/java/com/example/tampopo_client/Tampopo.java index 735dada..4708cbd 100644 --- a/app/src/main/java/com/example/tampopo_client/Tampopo.java +++ b/app/src/main/java/com/example/tampopo_client/Tampopo.java @@ -3,6 +3,10 @@ import android.app.Application; public class Tampopo extends Application { + //いらないので下記2つをコメントアウトしました + //public String getUserId; + + //public String getToken; private String token; private String userId; private String password; diff --git a/app/src/main/java/com/example/tampopo_client/models/ChatMessage.java b/app/src/main/java/com/example/tampopo_client/models/ChatMessage.java new file mode 100644 index 0000000..754a7e1 --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/models/ChatMessage.java @@ -0,0 +1,26 @@ +package com.example.tampopo_client.models; + +public class ChatMessage { + private String senderId; // メッセージ送信者のID + private String content; // メッセージ本文 + + public ChatMessage() {} + + public ChatMessage(String senderId, String content) { + this.senderId = senderId; + this.content = content; + } + + public String getSenderId() { + return senderId; + } + public void setSenderId(String senderId) { + this.senderId = senderId; + } + public String getContent() { + return content; + } + public void setContent(String content) { + this.content = content; + } +} 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 new file mode 100644 index 0000000..5a08e92 --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/models/Chatroom.java @@ -0,0 +1,22 @@ +package com.example.tampopo_client.models; + +public class Chatroom { + private String chatroomId; // チャットルームのID + + public Chatroom() {} + + public Chatroom(String chatroomId) { + this.chatroomId = chatroomId; + } + + public String getChatroomId() { + return chatroomId; + } + + public String foundChatroomId(){return chatroomId;} + + public void setChatroomId(String chatroomId) { + this.chatroomId = chatroomId; + } + +} diff --git a/app/src/main/java/com/example/tampopo_client/models/FriendPair.java b/app/src/main/java/com/example/tampopo_client/models/FriendPair.java index 4f9c35f..3ba71cd 100644 --- a/app/src/main/java/com/example/tampopo_client/models/FriendPair.java +++ b/app/src/main/java/com/example/tampopo_client/models/FriendPair.java @@ -10,6 +10,7 @@ this.user1Id = user1Id; } + public Integer getId() { return id; } 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 new file mode 100644 index 0000000..2574364 --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/resources/ChatroomResource.java @@ -0,0 +1,57 @@ +package com.example.tampopo_client.resources; + +import com.example.tampopo_client.models.ChatMessage; +import com.example.tampopo_client.models.Chatroom; + +import java.util.List; + +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.DELETE; +import retrofit2.http.GET; +import retrofit2.http.Header; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.Query; + +public interface ChatroomResource { + + // チャットルームに入る + @POST("enterChatroom") + Call enterChatroom( + @Query("myId") String myId, + @Query("partnerId") String partnerId, + @Header("Authorization") String token + ); + //チャットルームを探す + @GET("foundChatroom") + Call getMyChatroom( + @Query("EnterUserId") String userId + ); + + + // メッセージ送信 + @POST("sendMessage") + Call sendMessage( + @Header("Authorization") String token, + @Query("chatroomId") String chatroomId, + @Query("senderId") String senderId, + @Query("content") String content + ); + + // メッセージ一覧取得 + @GET("getMessages") + Call> getMessages( + @Header("Authorization") String token, + @Query("chatroomId") String chatroomId, + @Query("partnerId") String partnerId + ); + + // チャットルーム削除 + @DELETE("destroyChatroom") + Call destroyChatroom( + @Header("Authorization") String token, + @Query("chatroomId") String chatroomId, + @Query("userId") String userId + ); +} diff --git a/app/src/main/java/com/example/tampopo_client/resources/FriendsResource.java b/app/src/main/java/com/example/tampopo_client/resources/FriendsResource.java index d323d6c..7c91307 100644 --- a/app/src/main/java/com/example/tampopo_client/resources/FriendsResource.java +++ b/app/src/main/java/com/example/tampopo_client/resources/FriendsResource.java @@ -2,6 +2,8 @@ import com.example.tampopo_client.models.FriendPair; +import java.util.List; + import retrofit2.Call; import retrofit2.http.DELETE; import retrofit2.http.Field; @@ -20,6 +22,13 @@ @Field("user1-id") String user1Id ); +// @GET("friends/{pair_id}/") +// Call getFriend( +// @Query("token") String token, +// @Path("pair_id") String pairId +// ); + + // @GET("friends/{pair_id}/") Call getFriend( @Query("token") String token, @@ -31,4 +40,11 @@ @Query("token") String token, @Path("pair_id") String pair_id ); + + //新しくサーバーで定義したので追加しました + @GET("friends/users/{user-id}") + Call> getFriends( + @Path("user-id") String userId, + @Query("token") String token + ); } 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 new file mode 100644 index 0000000..901b43c --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/ChatroomViewModel.java @@ -0,0 +1,180 @@ +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/FriendViewModel.java b/app/src/main/java/com/example/tampopo_client/viewmodels/FriendViewModel.java index 91b8021..6e958c5 100644 --- a/app/src/main/java/com/example/tampopo_client/viewmodels/FriendViewModel.java +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/FriendViewModel.java @@ -45,7 +45,8 @@ } public void loadFriends(String userId, String token){ - Call> call = usersResource.getFriends(userId, token); + //userResourceではなくfriendResourceに書き換え + Call> call = friendsResource.getFriends(userId, token); call.enqueue(new Callback>() { @Override diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/UserViewModel.java b/app/src/main/java/com/example/tampopo_client/viewmodels/UserViewModel.java index 4fe101d..1ddbd4b 100644 --- a/app/src/main/java/com/example/tampopo_client/viewmodels/UserViewModel.java +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/UserViewModel.java @@ -43,6 +43,7 @@ private final UserResource userResource; private final MutableLiveData user = new MutableLiveData<>(); private final MutableLiveData token = new MutableLiveData<>(); + private final MutableLiveData icon = new MutableLiveData<>(); private final MutableLiveDataloading = new MutableLiveData<>(false); private final MutableLiveData error = new MutableLiveData<>(); @@ -58,6 +59,7 @@ public LiveData getUser() { return user; } public LiveData getToken() { return token; } + public LiveData getIcon() { return icon; } public LiveDataisLoading() { return loading;} public LiveData getError() { return error; } @@ -104,5 +106,43 @@ }); } + //アイコン + public void getIcon(String id) { + Call call = userResource.getIcon(id); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + icon.setValue(response.body()); + System.out.println(response.code()); + } else { + System.out.println(response.code()); + } + } + @Override public void onFailure(Call call, Throwable t) { + System.out.println("エラー: " + t.getMessage()); + } + }); + } + + public void updateIcon(String id, String newIcon, String token) { + Call call = userResource.updateIcon(id, newIcon, token); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + icon.setValue(response.body()); + System.out.println(response.code()); + + } else { + System.out.println(response.code()); + } + } + @Override public void onFailure(Call call, Throwable t) { + System.out.println("エラー: " + t.getMessage()); + } + }); + } + //viewModelのところでを呼び出すがフレンド系は西村さんの方で管理する } diff --git a/app/src/main/java/com/example/tampopo_client/views/ChatActivity.java b/app/src/main/java/com/example/tampopo_client/views/ChatActivity.java index 10cc5ed..c88c250 100644 --- a/app/src/main/java/com/example/tampopo_client/views/ChatActivity.java +++ b/app/src/main/java/com/example/tampopo_client/views/ChatActivity.java @@ -9,18 +9,15 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; -import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsCompat.Type; import androidx.lifecycle.ViewModelProvider; import com.example.tampopo_client.R; import com.example.tampopo_client.Tampopo; import com.example.tampopo_client.viewmodels.ChatroomViewModel; -import com.example.tampopo_client.viewmodels.NotificationListener; -import com.example.tampopo_client.viewmodels.UserViewModel; public class ChatActivity extends AppCompatActivity { - private ChatroomViewModel chatViewModel; + private ChatroomViewModel chatroomViewModel; //メンバー変数 private EditText senderMessage; @@ -37,7 +34,7 @@ EdgeToEdge.enable(this);//画面の端っこまで使う(Edge-to-Edge)表示 setContentView(R.layout.activity_chat);//表示する画面のレイアウトXMLファイル(activity_chat.xml)を指定 - chatViewModel = new ViewModelProvider(this).get(ChatroomViewModel.class); + chatroomViewModel = new ViewModelProvider(this).get(ChatroomViewModel.class); senderMessage = findViewById(R.id.sender_message); receiverMessage = findViewById(R.id.receiver_message); @@ -72,13 +69,13 @@ String senderId = tampopo.getUserId(); String token = tampopo.getToken(); String chatroomId = tampopo.getChatroomId(); - ChatroomViewModel.senderMessage(chatroomId, senderId, senderText, token); + chatroomViewModel.sendMessage(chatroomId, senderId, senderText, token); senderMessage.setText(senderText);//送信欄に表示 } } }); - chatViewModel.getLatestMessageLiveData().observe(this, receiverText -> { + chatroomViewModel.getLatestMessageLiveData().observe(this, receiverText -> { if (receiverText != null) { receiverMessage.setText(receiverText.getContent()); } 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 c038a2d..93b6087 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 @@ -19,13 +19,22 @@ import android.widget.ImageView; import android.widget.TextView; +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 { +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... @@ -40,7 +49,8 @@ private TextView mFriendNickname; private ImageView mFriendChatNotification; private boolean chatNotification = false; - + private Observer> activityObserver; + String friendActivity = "123"; public FriendIconView(Context context) { super(context); init(null, 0); @@ -77,7 +87,7 @@ mFriendIcon.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Context ctx = getContext(); - Intent intent = new Intent(ctx, FriendActivity. class); + Intent intent = new Intent(ctx, ChatActivity. class); ctx.startActivity(intent); } }); @@ -137,6 +147,9 @@ } } + public ImageView getImageView(){ + return mFriendIcon; + } public void setComment(String comment){ if(mFriendComment != null){ int comment_length = comment.length(); @@ -151,7 +164,31 @@ } } +// 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()); + } + } +// }; +// 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()); + } public void setChatNotification(boolean chat){ + //チャットを終了するときのonclickで一緒にsetChatNotification(false)もする(アイコン周りの赤丸を消す) //true(チャット通知が来た時)なら表示 if(chat){ mFriendChatNotification.setVisibility(View.VISIBLE); @@ -276,4 +313,9 @@ public void setExampleDrawable(Drawable exampleDrawable) { mExampleDrawable = exampleDrawable; } -} \ No newline at end of file + +// @Override +// public void onChanged(List activityList) { +// activityList.get(0). +// } +} diff --git a/app/src/main/java/com/example/tampopo_client/views/FriendListFragment.java b/app/src/main/java/com/example/tampopo_client/views/FriendListFragment.java index 1d68ef5..17730c5 100644 --- a/app/src/main/java/com/example/tampopo_client/views/FriendListFragment.java +++ b/app/src/main/java/com/example/tampopo_client/views/FriendListFragment.java @@ -14,6 +14,7 @@ import android.view.ViewGroup; import com.example.tampopo_client.R; +import com.example.tampopo_client.Tampopo; import com.example.tampopo_client.viewmodels.FriendViewModel; import com.example.tampopo_client.views.placeholder.FriendContent; @@ -21,7 +22,7 @@ import androidx.lifecycle.Observer; /** - * A fragment representing a list of Items. + * フレンド一覧を表示するフラグメント */ public class FriendListFragment extends Fragment { @@ -30,6 +31,8 @@ // TODO: Customize parameters private int mColumnCount = 1; + private MyFriendRecyclerViewAdapter adapter; + /** * Mandatory empty constructor for the fragment manager to instantiate the * fragment (e.g. upon screen orientation changes). @@ -38,7 +41,15 @@ } // TODO: Customize parameter initialization - @SuppressWarnings("unused") +// @SuppressWarnings("unused") +// public static FriendListFragment newInstance(int columnCount) { +// FriendListFragment fragment = new FriendListFragment(); +// Bundle args = new Bundle(); +// args.putInt(ARG_COLUMN_COUNT, columnCount); +// fragment.setArguments(args); +// return fragment; +// } + public static FriendListFragment newInstance(int columnCount) { FriendListFragment fragment = new FriendListFragment(); Bundle args = new Bundle(); @@ -56,44 +67,113 @@ } } +// @Override +// public View onCreateView(LayoutInflater inflater, ViewGroup container, +// Bundle savedInstanceState) { +// View view = inflater.inflate(R.layout.fragment_friend_list_list, container, false); +// +// RecyclerView recyclerView = view.findViewById(R.id.list); +// Context context = view.getContext(); +// +// // Add some sample items. +// //for (int i = 1; i <= 30; i++) { +// // FriendContent.addItem(new FriendContent.FriendItem(Integer.toString(i), "ユーザ" + i)); +// //} +// +// // Set the adapter +// if (view instanceof RecyclerView) { +// Context context = view.getContext(); +// RecyclerView recyclerView = (RecyclerView) view; +// if (mColumnCount <= 1) { +// recyclerView.setLayoutManager(new LinearLayoutManager(context)); +// } else { +// recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount)); +// } +// recyclerView.setAdapter(new MyFriendRecyclerViewAdapter(FriendContent.ITEMS)); +// } +// adapter = new MyFriendRecyclerViewAdapter(FriendContent.ITEMS); +// recyclerView.setAdapter(adapter); +// return view; +// } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_friend_list_list, container, false); - // Add some sample items. - //for (int i = 1; i <= 30; i++) { - // FriendContent.addItem(new FriendContent.FriendItem(Integer.toString(i), "ユーザ" + i)); - //} + RecyclerView recyclerView = view.findViewById(R.id.list); + Context context = view.getContext(); - // Set the adapter - if (view instanceof RecyclerView) { - Context context = view.getContext(); - RecyclerView recyclerView = (RecyclerView) view; - if (mColumnCount <= 1) { - recyclerView.setLayoutManager(new LinearLayoutManager(context)); - } else { - recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount)); - } - recyclerView.setAdapter(new MyFriendRecyclerViewAdapter(FriendContent.ITEMS)); + if (mColumnCount <= 1) { + recyclerView.setLayoutManager(new LinearLayoutManager(context)); + } else { + recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount)); } + + // adapterをここで初期化 + adapter = new MyFriendRecyclerViewAdapter(FriendContent.ITEMS); + recyclerView.setAdapter(adapter); + return view; } + +// @Override +// public void onViewCreated(View view, Bundle savedInstanceState) { +// super.onViewCreated(view, savedInstanceState); +// FriendViewModel friendViewModel = new ViewModelProvider(this).get(FriendViewModel.class); +// +// String userId = ((Tampopo) ((FriendActivity) view.getContext()).getApplication()).getUserId; +// String token = ((Tampopo) ((FriendActivity) view.getContext()).getApplication()).getToken; +// friendViewModel.loadFriends(userId, token); +// +// friendViewModel.getFriendIdsLiveData().observe(getViewLifecycleOwner(), new Observer>() { +// +// @Override +// public void onChanged(List friendIds) { +// for (String i : friendIds) { +// FriendContent.addItem(new FriendContent.FriendItem(i, "")); +// } +// adapter.notifyDataSetChanged(); +// } +// }); +// } + @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); FriendViewModel friendViewModel = new ViewModelProvider(this).get(FriendViewModel.class); - friendViewModel.getFriendIdsLiveData().observe(getViewLifecycleOwner(), new Observer>() { +//自分の情報を取得する + Tampopo app = (Tampopo) requireActivity().getApplication(); + String userId = app.getUserId(); + String token = app.getToken(); - @Override - public void onChanged(List friendIds) { - for (String i : friendIds) { - FriendContent.addItem(new FriendContent.FriendItem(i, "")); +//サーバーからフレンド一覧を取得する + friendViewModel.loadFriends(userId, token); + +//一旦旧コードをコメントアウトしました。 +// friendViewModel.getFriendIdsLiveData().observe(getViewLifecycleOwner(), new Observer>() { +// @Override +// public void onChanged(List friendIds) { +// // 一度リセットして新しいリストを追加 +// FriendContent.clear(); +// for (String id : friendIds) { +// FriendContent.addItem(new FriendContent.FriendItem(id, "")); +// } +// // adapterがクラス変数なのでここで使える +// adapter.notifyDataSetChanged(); +// } +// }); + friendViewModel.getFriendIdsLiveData().observe(getViewLifecycleOwner(), friendIds -> { + FriendContent.clear(); + if (friendIds != null) { + for (String id : friendIds) { + FriendContent.addItem(new FriendContent.FriendItem(id, "")); } } + adapter.notifyDataSetChanged(); }); } } \ No newline at end of file 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 d5021fd..29af578 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 @@ -59,7 +59,7 @@ //アクティビティの選択肢 private String[] words = {"ひまnow", "あそぼ!", "そろそろ会いたない〜?", "勉強なう", "電話しよ~", "お風呂入ってくる~", "今暇だよー!", "いそがしい~!!"}; private Button openDialogButton; - private Map userViews = new HashMap<>(); + private Map userViews = new HashMap<>(); ActivityViewModel activityViewModel; Tampopo tampopo; @@ -124,14 +124,26 @@ // } // }); - MutableLiveData> activitiesLiveData = activityViewModel.getActivitiesLiveDataFromUserId(tampopo.getUserId()); - activitiesLiveData.observe(this, new Observer>() { + MutableLiveData> friendsLiveData = activityViewModel.getFriendUserIdsLiveData(); + friendsLiveData.observe(this, new Observer>() { @Override - public void onChanged(List activities) { - updateActivityView(activities); + public void onChanged(List friends) { + // フレンドの追加と削除 + updateActivityView(friends); } }); + for (String friendId: userViews.keySet()) { + MutableLiveData> activitiesLiveData = activityViewModel.getActivitiesLiveDataFromUserId(friendId); + activitiesLiveData.observe(this, new Observer>() { + @Override + public void onChanged(List activities) { + // 更新したフレンドの再登場,更新してないフレンドの退場」 + } + }); + activitiesLiveData.observe(this, userViews.get(friendId)); + } + //メイン画面から通知一覧画面への遷移 // ImageButton notificationButton = (ImageButton)findViewById(R.id.notification); // notificationButton.setOnClickListener(new View.OnClickListener() { @@ -157,14 +169,14 @@ openDialogButton.setOnClickListener(v -> showInputDialog()); // 仮データを作る(ユーザID、コメント) - List mockActivities = new ArrayList<>(); - mockActivities.add(new Activity("user01", "act01","ひま〜", "2025-09-25 10:00" )); - mockActivities.add(new Activity("user02", "act02","勉強してるよ", "2025-09-25 10:01")); - mockActivities.add(new Activity("user03", "act03","ねむい〜", "2025-09-25 10:02")); +// List mockActivities = new ArrayList<>(); +// mockActivities.add(new Activity("user01", "act01","ひま〜", "2025-09-25 10:00" )); +// mockActivities.add(new Activity("user02", "act02","勉強してるよ", "2025-09-25 10:01")); +// mockActivities.add(new Activity("user03", "act03","ねむい〜", "2025-09-25 10:02")); // 表示を更新 - updateActivityView(mockActivities); +// updateActivityView(mockActivities); // 疑似通知ボタン chat申請後ダイアログを表示するため、それの疑似的な申請コード //まだ動くかわからない @@ -281,7 +293,7 @@ } } - private void updateActivityView(List activities) { + private void updateActivityView(List friends) { //TextView comment = this.findViewById(R.id.friend01_comment); //for (Activity ac: activities.values()) { // comment.setText(ac.getText()); @@ -290,16 +302,15 @@ ///natty ユーザごとにコメントの更新をする LinearLayout messageList = findViewById(R.id.messageList); - for (Activity entry : activities) { - String userId = entry.getUserId(); - Activity activity = entry; + for (String friendId : friends) { +// String friendId = entry.getUserId(); +// Activity activity = entry; - View userView = userViews.get(userId); + FriendIconView userView = userViews.get(friendId); if (userView == null) { // 新しいユーザなので、アイコン+コメントを作成 - LinearLayout container = new LinearLayout(this); - container.setOrientation(LinearLayout.HORIZONTAL); + FriendIconView container = new FriendIconView(this); container.setPadding(16, 16, 16, 16); // ユーザのアイコン(固定) @@ -315,26 +326,27 @@ ); // ユーザIDに応じてアイコンリソースを決定(仮にハードコード or マッピング) - iconView.setImageResource(getUserIconResource(userId)); // ←ここがポイント + iconView.setImageResource(getUserIconResource(friendId)); // ←ここがポイント // コメント部分 - TextView commentView = new TextView(this); - commentView.setTextSize(16); - commentView.setPadding(16, 0, 0, 0); - commentView.setText(activity.getText()); - - // コンテナに追加 - container.addView(iconView); - container.addView(commentView); +// TextView commentView = new TextView(this); +// commentView.setTextSize(16); +// commentView.setPadding(16, 0, 0, 0); +// commentView.setText(activity.getText()); +// +// // コンテナに追加 +// container.addView(iconView); +// container.addView(commentView); // Mapに登録、画面に追加 - userViews.put(userId, container); + userViews.put(friendId, container); messageList.addView(container); - } else { - // 既に表示されている → コメントだけ更新 - TextView commentView = (TextView) ((LinearLayout) userView).getChildAt(1); - commentView.setText(activity.getText()); +// } else { +// // 既に表示されている → コメントだけ更新 +// TextView commentView = (TextView) ((LinearLayout) userView).getChildAt(1); +// commentView.setText(activity.getText()); +// } } } @@ -444,7 +456,7 @@ // } private void highlightUserIcon(String userId) { runOnUiThread(() -> { - LinearLayout container = userViews.get(userId); + FriendIconView container = userViews.get(userId); if (container != null && container.getChildCount() > 0) { View v = container.getChildAt(0); if (v instanceof ShapeableImageView) { @@ -458,7 +470,7 @@ private void clearUserIconHighlight(String userId) { runOnUiThread(() -> { - LinearLayout container = userViews.get(userId); + FriendIconView container = userViews.get(userId); if (container != null && container.getChildCount() > 0) { View v = container.getChildAt(0); if (v instanceof ShapeableImageView) { diff --git a/app/src/main/java/com/example/tampopo_client/views/MyFragmentAdapter.java b/app/src/main/java/com/example/tampopo_client/views/MyFragmentAdapter.java index 5786821..bdde5e5 100644 --- a/app/src/main/java/com/example/tampopo_client/views/MyFragmentAdapter.java +++ b/app/src/main/java/com/example/tampopo_client/views/MyFragmentAdapter.java @@ -28,3 +28,4 @@ return 2; } } + diff --git a/app/src/main/java/com/example/tampopo_client/views/MyFriendRecyclerViewAdapter.java b/app/src/main/java/com/example/tampopo_client/views/MyFriendRecyclerViewAdapter.java index 22ef86b..e2a231d 100644 --- a/app/src/main/java/com/example/tampopo_client/views/MyFriendRecyclerViewAdapter.java +++ b/app/src/main/java/com/example/tampopo_client/views/MyFriendRecyclerViewAdapter.java @@ -1,40 +1,105 @@ +//旧コードは念の為コメントアウトしています。 +//package com.example.tampopo_client.views; +// +//import android.view.LayoutInflater; +//import android.view.ViewGroup; +//import android.widget.TextView; +// +//import com.example.tampopo_client.databinding.FragmentFriendListBinding; +//import com.example.tampopo_client.views.placeholder.FriendContent; +// +//import java.util.List; +// +//import androidx.recyclerview.widget.RecyclerView; +// +///** +// * {@link RecyclerView.Adapter} that can display a {@link FriendContent.FriendItem}. +// * TODO: Replace the implementation with code for your data type. +// */ +//public class MyFriendRecyclerViewAdapter extends RecyclerView.Adapter { +// +// private final List mValues; +// +// public MyFriendRecyclerViewAdapter(List items) { +// mValues = items; +// } +// +// @Override +// public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { +// +// return new ViewHolder(FragmentFriendListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); +// +// } +// +// @Override +// public void onBindViewHolder(final ViewHolder holder, int position) { +// holder.mItem = mValues.get(position); +// //holder.mIdView.setText(mValues.get(position).id); +// holder.mContentView.setText(mValues.get(position).name); +// } +// +// @Override +// public int getItemCount() { +// return mValues.size(); +// } +// +// public class ViewHolder extends RecyclerView.ViewHolder { +// //public final TextView mIdView; +// public final TextView mContentView; +// public FriendContent.FriendItem mItem; +// +// public ViewHolder(FragmentFriendListBinding binding) { +// super(binding.getRoot()); +// //mIdView = binding.itemNumber; +// mContentView = binding.content; +// } +// +// @Override +// public String toString() { +// return super.toString() + " '" + mContentView.getText() + "'"; +// } +// } +//} + package com.example.tampopo_client.views; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.TextView; -import com.example.tampopo_client.databinding.FragmentFriendListBinding; -import com.example.tampopo_client.views.placeholder.FriendContent; - -import java.util.List; - import androidx.recyclerview.widget.RecyclerView; -/** - * {@link RecyclerView.Adapter} that can display a {@link FriendContent.FriendItem}. - * TODO: Replace the implementation with code for your data type. - */ +import com.example.tampopo_client.R; +import com.example.tampopo_client.views.placeholder.FriendContent.FriendItem; + +import java.util.List; + public class MyFriendRecyclerViewAdapter extends RecyclerView.Adapter { - private final List mValues; + private final List mValues; - public MyFriendRecyclerViewAdapter(List items) { + public MyFriendRecyclerViewAdapter(List items) { mValues = items; } + //各行のビューを作成する @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - - return new ViewHolder(FragmentFriendListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); - + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.fragment_friend_list, parent, false); + return new ViewHolder(view); } + + //データをビューに紐付ける @Override public void onBindViewHolder(final ViewHolder holder, int position) { - holder.mItem = mValues.get(position); - //holder.mIdView.setText(mValues.get(position).id); - holder.mContentView.setText(mValues.get(position).name); + FriendItem item = mValues.get(position); + holder.userIdView.setText(item.id); + holder.contentView.setText(item.name); + holder.userIcon.setImageResource(R.drawable.friend); } @Override @@ -43,19 +108,15 @@ } public class ViewHolder extends RecyclerView.ViewHolder { - //public final TextView mIdView; - public final TextView mContentView; - public FriendContent.FriendItem mItem; + public final ImageView userIcon; + public final TextView contentView; + public final TextView userIdView; - public ViewHolder(FragmentFriendListBinding binding) { - super(binding.getRoot()); - //mIdView = binding.itemNumber; - mContentView = binding.content; - } - - @Override - public String toString() { - return super.toString() + " '" + mContentView.getText() + "'"; + public ViewHolder(View view) { + super(view); + userIcon = view.findViewById(R.id.usericon); + contentView = view.findViewById(R.id.content); + userIdView = view.findViewById(R.id.userId); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/tampopo_client/views/ProfileActivity.java b/app/src/main/java/com/example/tampopo_client/views/ProfileActivity.java index 8f5a336..075b7ff 100644 --- a/app/src/main/java/com/example/tampopo_client/views/ProfileActivity.java +++ b/app/src/main/java/com/example/tampopo_client/views/ProfileActivity.java @@ -48,32 +48,24 @@ //決定ボタンを押したときにidとニックネームを保存 - Button dicisionbutton = (Button) findViewById(R.id.dicisionbutton); - + Button dicisionbutton = findViewById(R.id.dicisionbutton); dicisionbutton.setOnClickListener(new View.OnClickListener() { - - private Object getApplication; - public void onClick(View v) { - //ニックネームの変更 - EditText usernicknameInput = (EditText) findViewById(R.id.nicknamebutton); + EditText usernicknameInput = findViewById(R.id.nicknamebutton); String usernickname = usernicknameInput.getText().toString(); - ((Tampopo) ProfileActivity.this.getApplication()).setNickname(usernickname); - //idの変更 - EditText useridInput = (EditText) findViewById(R.id.idbutton); + EditText useridInput = findViewById(R.id.idbutton); String userid = useridInput.getText().toString(); - ((Tampopo) ProfileActivity.this.getApplication()).setUserId(userid); - //アイコンの変更 + // Application(Tampopo)に保存(→SharedPreferencesにも自動保存される) + Tampopo app = (Tampopo) getApplication(); + app.setNickname(usernickname); + app.setUserId(userid); - - //決定を押したら設定画面に遷移 + // 設定画面に戻る Intent intent = new Intent(ProfileActivity.this, SettingActivity.class); startActivity(intent); - - } }); 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 0c1facf..b70cc11 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 @@ -1,7 +1,10 @@ package com.example.tampopo_client.views; import android.os.Bundle; +import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.ImageButton; +import android.widget.LinearLayout; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; @@ -9,19 +12,67 @@ import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; +import com.bumptech.glide.Glide; import com.example.tampopo_client.R; +import com.example.tampopo_client.Tampopo; public class TestFriendIconActivity extends AppCompatActivity { + Tampopo tampopo; @Override protected void onCreate(Bundle savedInstanceState) { + tampopo = (Tampopo) getApplication(); super.onCreate(savedInstanceState); - EdgeToEdge.enable(this); - setContentView(R.layout.activity_test_friend_icon); - FriendIconView friendIconView = findViewById(R.id.friendIconView); - friendIconView.setNickname("nitta"); - friendIconView.setImageResource(R.drawable.friend01_icon); - friendIconView.setComment(("kjrig")); +// EdgeToEdge.enable(this); +// setContentView(R.layout.activity_test_friend_icon); +// String uid = tampopo.getUserId(); + + // ルートレイアウトを FrameLayout に(自由配置できる) + FrameLayout rootLayout = new FrameLayout(this); + // 画面密度取得(dp → px 換算用) + float density = getResources().getDisplayMetrics().density; + + //FriendIconView 1個目 + FriendIconView view1 = new FriendIconView(this); + FrameLayout.LayoutParams params1 = new FrameLayout.LayoutParams( + (int) (160*density), // width in px + (int) (100*density) // height in px + ); + view1.setLayoutParams(params1); + view1.setX(100); // 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) + ); + view1.setLayoutParams(params2); + view1.setX(100); + view1.setY(150); + rootLayout.addView(view1); + + setContentView(rootLayout); +// +// FriendIconView friendIconView = new FriendIconView(this); +// uid = "test0"; + +// FriendIconView friendIconView = findViewById(R.id.friendIconView); +// friendIconView.setAccount(uid); +// +// //xmlで複製後uidを設定するとそのアカウントのニックネーム,コメント,アイコンが表示される +// uid = "test1"; +// FriendIconView friendIconView2 = findViewById(R.id.friendIconView2); +// friendIconView2.setAccount(uid); + +// friendIconView.setNickname("nitta"); +// friendIconView.setComment(("kjrig")); +// Glide.with(TestFriendIconActivity.this) +// .load("http://nitta-lab-www.is.konan-u.ac.jp/tampopo-data/icon" + uid + ".jpg") +// .into(friendIconView.getImageView()); + //friendIconView.setImageResource(Integer.parseInt("http://nitta-lab-www.is.konan-u.ac.jp/tampopo-data/icontest.jpg")); // ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { // Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); // v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); diff --git a/app/src/main/java/com/example/tampopo_client/views/placeholder/FriendContent.java b/app/src/main/java/com/example/tampopo_client/views/placeholder/FriendContent.java index 3c48f8e..f399d6f 100644 --- a/app/src/main/java/com/example/tampopo_client/views/placeholder/FriendContent.java +++ b/app/src/main/java/com/example/tampopo_client/views/placeholder/FriendContent.java @@ -40,6 +40,11 @@ ITEM_MAP.put(item.id, item); } + public static void clear() { + ITEMS.clear(); + ITEM_MAP.clear(); + } + //private static FriendItem createPlaceholderItem(int position) { // return new FriendItem(String.valueOf(position), "ユーザー名 " , makeDetails(position)); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 09733ae..d02c831 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -55,9 +55,9 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/sample_friend_icon_view.xml b/app/src/main/res/layout/sample_friend_icon_view.xml index 7270a3e..365d56c 100644 --- a/app/src/main/res/layout/sample_friend_icon_view.xml +++ b/app/src/main/res/layout/sample_friend_icon_view.xml @@ -49,11 +49,11 @@ android:layout_gravity="center_horizontal"> \ No newline at end of file