diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0ff4bd5..7bee9cc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ - + + - - - - - + + + - - - + + - - - - - + + + - + \ No newline at end of file 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 31b884f..2c455f2 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,7 +9,6 @@ import com.example.tampopo_client.resources.ActivitiesResource; import com.example.tampopo_client.resources.UserResource; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -19,8 +18,6 @@ import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; -// NOTE: MainActivityでLiveDataをobserveし表示する - /** * アクティビティを扱うためのViewModel * @@ -31,8 +28,9 @@ private final ActivitiesResource activitiesResource; private final UserResource userResource; - private final MutableLiveData> activitiesLiveData; // key=userId, value=activity - private final MutableLiveData> friendUserIdsLiveData; + private final Map>> friendToActivitiesLiveData; // key=userId + private final MutableLiveData> friendUserIdsLiveData; // フレンドのユーザーIDのリスト + private final MutableLiveData myLatestActivityLiveData; // 自分の最新のアクティビティ private final String userId; private final String token; @@ -42,29 +40,26 @@ this.token = token; // Retrofitの初期化 - final Retrofit retrofit = new Retrofit.Builder() - .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/tampopo/") - .addConverterFactory(JacksonConverterFactory.create()) - .build(); + 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); - // LiveDataの初期化 - activitiesLiveData = new MutableLiveData<>(Map.of()); + friendToActivitiesLiveData = Map.of(); friendUserIdsLiveData = new MutableLiveData<>(List.of()); + myLatestActivityLiveData = new MutableLiveData<>(); } @Override public Runnable onUpdate() { return () -> { - if (activitiesLiveData == null || friendUserIdsLiveData == null) { + if (friendToActivitiesLiveData == null || friendUserIdsLiveData == null || myLatestActivityLiveData == null) { return; } // 最新のアクティビティを取得して更新する if (friendUserIdsLiveData.isInitialized() && friendUserIdsLiveData.getValue() != null) { for (String userId : friendUserIdsLiveData.getValue()) { - pullLatestActivity(userId); + pullLatestActivity(userId, activitiesResource, friendToActivitiesLiveData); } } @@ -79,6 +74,10 @@ } public void createActivity(String userId, String token, String newActivity) { + if (!myLatestActivityLiveData.isInitialized()) { + return; + } + Call createActivityCall = activitiesResource.addActivity(userId, token, newActivity); createActivityCall.enqueue(new Callback() { @Override @@ -86,24 +85,16 @@ if (response.isSuccessful()) { String createdActivityId = response.body(); - // TODO: 仮作成なので改善必須 + // TODO: 仮作成なので改善したい Call getActivityCall = activitiesResource.getActivity(userId, createdActivityId); getActivityCall.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { - Map activities = activitiesLiveData.getValue(); - if (activities == null) { - activities = new HashMap<>(); - } - Activity createdActivity = response.body(); - if (activities.containsKey(userId)) { - activities.replace(userId, createdActivity); - } else { - activities.put(userId, createdActivity); + if (createdActivity == null) { + return; } - - activitiesLiveData.postValue(activities); + myLatestActivityLiveData.postValue(createdActivity); } @Override @@ -126,28 +117,42 @@ /** * 最新のユーザーのアクティビティを取得・更新する * - * @param userId 取得対象のユーザーのID + * @param userId 取得対象のユーザーのID + * @param resource アクティビティのリソース + * @param friendToActivitiesLiveData フレンドのユーザーIDからアクティビティへの写像のライブデータ */ - private void pullLatestActivity(String userId) { - Call> fetchActivityCall = activitiesResource.getActivities(userId, "LATEST"); + private void pullLatestActivity(String userId, ActivitiesResource resource, Map>> friendToActivitiesLiveData) { + 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) { - 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); - } + List fetchedActivities = response.body(); // アクティビティが存在しない場合は空のリスト + if (fetchedActivities == null) { + return; + } - activitiesLiveData.postValue(activities); + 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); } } } @@ -182,8 +187,12 @@ }); } - public MutableLiveData> getActivitiesLiveData() { - return activitiesLiveData; + public MutableLiveData getMyLatestActivityLiveData() { + return myLatestActivityLiveData; + } + + public MutableLiveData> getActivitiesLiveDataFromUserId(String userId) { + return friendToActivitiesLiveData.get(userId); } public MutableLiveData> getFriendUserIdsLiveData() { 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 new file mode 100644 index 0000000..c038a2d --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/views/FriendIconView.java @@ -0,0 +1,279 @@ +package com.example.tampopo_client.views; + +import static androidx.core.content.ContextCompat.startActivity; + +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.FrameLayout; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import com.example.tampopo_client.R; +import com.google.android.material.imageview.ShapeableImageView; + +/** + * TODO: document your custom view class. + */ +public class FriendIconView extends FrameLayout { + 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; + private ShapeableImageView mFriendIcon; + private ImageView mFriendCommentImage; + private TextView mFriendComment; + private TextView mFriendNickname; + private ImageView mFriendChatNotification; + private boolean chatNotification = false; + + public FriendIconView(Context context) { + super(context); + init(null, 0); + } + + public FriendIconView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public FriendIconView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs, defStyle); + } + + private void init(AttributeSet attrs, int defStyle) { + 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); + mFriendIcon = findViewById(R.id.friend_icon); + mFriendChatNotification = findViewById(R.id.chat_notification); + + //true(チャット通知が来た時)なら表示 + if(chatNotification){ + mFriendChatNotification.setVisibility(View.VISIBLE); + } + //false(チャット通知が来ていない)なら非表示 + 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, FriendActivity. class); + ctx.startActivity(intent); + } + }); +// 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); +// } + +// // 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(); +// + } + + public void setNickname(String nickname) { + if (mFriendNickname != null) { + mFriendNickname.setText(nickname); + } + } + + public void setImageResource(int resId) { + if (mFriendIcon != null) { + mFriendIcon.setImageResource(resId); + } + } + + public void setComment(String comment){ + if(mFriendComment != null){ + int comment_length = comment.length(); + if (comment_length > 20) { + mFriendComment.setTextSize(7); + } else if (comment_length > 10) { + mFriendComment.setTextSize(10); + } else { + mFriendComment.setTextSize(12); + } + mFriendComment.setText(comment); + } + } + + public void setChatNotification(boolean chat){ + //true(チャット通知が来た時)なら表示 + if(chat){ + mFriendChatNotification.setVisibility(View.VISIBLE); + } + //false(チャット通知が来ていない)なら非表示 + 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; + } +} \ 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 e851900..0d44df8 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 @@ -138,7 +138,7 @@ // ImageButton notificationButton = (ImageButton)findViewById(R.id.notification); // notificationButton.setOnClickListener(new View.OnClickListener() { // public void onClick(View v) { -// Intent intent = new Intent(MainActivity.this,notificationActivity. class); +// Intent intent = new Intent(MainActivity.this,NotificationActivity. class); // startActivity(intent); // } // }); diff --git a/app/src/main/java/com/example/tampopo_client/views/MyFriendRequestRecyclerViewAdapter.java b/app/src/main/java/com/example/tampopo_client/views/MyFriendRequestRecyclerViewAdapter.java index eeff46e..400d9f1 100644 --- a/app/src/main/java/com/example/tampopo_client/views/MyFriendRequestRecyclerViewAdapter.java +++ b/app/src/main/java/com/example/tampopo_client/views/MyFriendRequestRecyclerViewAdapter.java @@ -61,7 +61,7 @@ // 「許可」ボタンを押した時の処理 holder.allowButton.setOnClickListener(v -> { // フレンド許可APIを叩く - String senderId = holder.mItem.getId().toString(); // または holder.mItem.senderId + String senderId = holder.mItem.getSenderId(); //許可を押して相手(senderID)のIDを取得 // 例:ViewModelにフレンド登録処理を依頼する friendViewModel.createFriend(token, senderId, receiverId); 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 new file mode 100644 index 0000000..0c1facf --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/views/TestFriendIconActivity.java @@ -0,0 +1,32 @@ +package com.example.tampopo_client.views; + +import android.os.Bundle; +import android.widget.ImageButton; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.example.tampopo_client.R; + +public class TestFriendIconActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + 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")); +// 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); +// return insets; +// }); + + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/chat_notification.png b/app/src/main/res/drawable/chat_notification.png new file mode 100644 index 0000000..47a4549 --- /dev/null +++ b/app/src/main/res/drawable/chat_notification.png Binary files differ diff --git a/app/src/main/res/drawable/circle_button.xml b/app/src/main/res/drawable/circle_button.xml new file mode 100644 index 0000000..12aed7c --- /dev/null +++ b/app/src/main/res/drawable/circle_button.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/friend02_icon.png b/app/src/main/res/drawable/friend02_icon.png new file mode 100644 index 0000000..5e82c4b --- /dev/null +++ b/app/src/main/res/drawable/friend02_icon.png Binary files differ diff --git a/app/src/main/res/drawable/friend_icon.png b/app/src/main/res/drawable/friend_icon.png new file mode 100644 index 0000000..1240e45 --- /dev/null +++ b/app/src/main/res/drawable/friend_icon.png Binary files differ diff --git a/app/src/main/res/drawable/new_png.png b/app/src/main/res/drawable/new_png.png new file mode 100644 index 0000000..1fe66f3 --- /dev/null +++ b/app/src/main/res/drawable/new_png.png Binary files differ diff --git a/app/src/main/res/layout/activity_test_friend_icon.xml b/app/src/main/res/layout/activity_test_friend_icon.xml new file mode 100644 index 0000000..fc0f295 --- /dev/null +++ b/app/src/main/res/layout/activity_test_friend_icon.xml @@ -0,0 +1,21 @@ + + + + + \ 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 new file mode 100644 index 0000000..7270a3e --- /dev/null +++ b/app/src/main/res/layout/sample_friend_icon_view.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..8ed710d --- /dev/null +++ b/app/src/main/res/values-night/styles.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs_friend_icon_view.xml b/app/src/main/res/values/attrs_friend_icon_view.xml new file mode 100644 index 0000000..b9f829d --- /dev/null +++ b/app/src/main/res/values/attrs_friend_icon_view.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5ec3a93..c8b0394 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,4 +3,8 @@ #FF000000 #FFFFFFFF #E53935 + #FF29B6F6 + #FF039BE5 + #FFBDBDBD + #FF757575 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d2bcbac..6b0a488 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,11 +1,11 @@ - tampopo-client - ホーム画面からフレンド一覧への画面遷移ボタン - ホーム画面から通知一覧への画面遷移ボタン - ホーム画面から設定画面への画面遷移ボタン - フレンド - 申請中 - 保留中 - 友達リスト + tampopo-client + ホーム画面からフレンド一覧への画面遷移ボタン + ホーム画面から通知一覧への画面遷移ボタン + ホーム画面から設定画面への画面遷移ボタン + フレンド + 申請中 + 保留中 + 友達リスト \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..7e2e6c3 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 0afc5a0..b4bacf5 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -6,8 +6,4 @@ \ No newline at end of file