diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1a4e8be..7ec1c97 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -50,5 +50,5 @@ androidTestImplementation(libs.espresso.core) implementation(libs.navigation.fragment) implementation(libs.navigation.ui) - + implementation(libs.viewpager2) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ef6698f..0ff4bd5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Tampopoclient" + android:usesCleartextTraffic="true" tools:targetApi="31"> + - \ No newline at end of file + diff --git a/app/src/main/java/com/example/tampopo_client/models/User.java b/app/src/main/java/com/example/tampopo_client/models/User.java index c5092a1..1b6e7de 100644 --- a/app/src/main/java/com/example/tampopo_client/models/User.java +++ b/app/src/main/java/com/example/tampopo_client/models/User.java @@ -2,7 +2,7 @@ public class User { - private Integer id; //ユーザーID + private String userId; //ユーザーID private String name; //ニックネーム private String email; //メールアドレス private String icon; //アイコン @@ -17,11 +17,11 @@ } - public Integer getId() { - return id; + public String getId() { + return userId; } - public void setId(Integer id) { - this.id = id; + public void setId(String userId) { + this.userId = userId; } public String getName() { diff --git a/app/src/main/java/com/example/tampopo_client/resources/UserResource.java b/app/src/main/java/com/example/tampopo_client/resources/UserResource.java index 7db852b..5e9f426 100644 --- a/app/src/main/java/com/example/tampopo_client/resources/UserResource.java +++ b/app/src/main/java/com/example/tampopo_client/resources/UserResource.java @@ -20,7 +20,7 @@ @GET("users") Call> getUsers(); - // 新規アカウントを作る + // 新規アカウントを作成 @FormUrlEncoded @POST("users") Call createUser( diff --git a/app/src/main/java/com/example/tampopo_client/ui/notifications/NotificationsFragment.java b/app/src/main/java/com/example/tampopo_client/ui/notifications/NotificationsFragment.java new file mode 100644 index 0000000..16eba68 --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/ui/notifications/NotificationsFragment.java @@ -0,0 +1,37 @@ +package com.example.tampopo_client.ui.notifications; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import com.example.tampopo_client.databinding.FragmentNotificationsBinding; + +public class NotificationsFragment extends Fragment { + + private FragmentNotificationsBinding binding; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + NotificationsViewModel notificationsViewModel = + new ViewModelProvider(this).get(NotificationsViewModel.class); + + binding = FragmentNotificationsBinding.inflate(inflater, container, false); + View root = binding.getRoot(); + + final TextView textView = binding.textNotifications; + notificationsViewModel.getText().observe(getViewLifecycleOwner(), textView::setText); + return root; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/tampopo_client/ui/notifications/NotificationsViewModel.java b/app/src/main/java/com/example/tampopo_client/ui/notifications/NotificationsViewModel.java new file mode 100644 index 0000000..403564e --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/ui/notifications/NotificationsViewModel.java @@ -0,0 +1,19 @@ +package com.example.tampopo_client.ui.notifications; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class NotificationsViewModel extends ViewModel { + + private final MutableLiveData mText; + + public NotificationsViewModel() { + mText = new MutableLiveData<>(); + mText.setValue("This is notifications fragment"); + } + + public LiveData getText() { + return mText; + } +} \ No newline at end of file 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 e90bce2..596ddc4 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 @@ -8,6 +8,9 @@ import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; @@ -17,9 +20,9 @@ //APIの窓口 private final FriendRequestsResource friendRequestsResource; //自分が受け取った申請 - private final MutableLiveData> receivedRequests ; + private final MutableLiveData> receivedRequests; //通信結果の状態 - private final MutableLiveData operationResult ; + private final MutableLiveData operationResult; public FriendReceivedRequestViewModel() { @@ -31,10 +34,63 @@ this.receivedRequests = new MutableLiveData<>(); this.operationResult = new MutableLiveData<>(); } + //viewがobserve出来るように public MutableLiveData> getSentRequestsLiveData() { return receivedRequests; } + //サーバーから受け取ったFriendReceivedRequestのデータを格納してキャッシュしていくぞ + public void loadReceivedRequests(String token) { + //tokenを渡して、受信フレンド申請一覧を取得するHTTPリクエスト(Webのサーバーに対して何かをお願いするメッセージ」)を作る準備をしている + 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()); + } else { + operationResult.setValue("Error: " + response.code()); + System.out.println("response error"); + } + } + @Override + public void onFailure(Call> call, Throwable t) { + operationResult.setValue("Network error: " + t.getMessage()); + System.out.println("ネットワークエラー: " + t); + } + }); + } + // フレンドリクエスト削除メソッド + public void deleteFriendRequest(String friendRequestId, String token) { + Call call = friendRequestsResource.deleteFriendRequest(friendRequestId, token); + + call.enqueue(new Callback() { + @Override + 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); + } else { + operationResult.setValue("Error deleting request: " + response.code()); + System.out.println("Error deleting request: " + response.code()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + operationResult.setValue("Network error: " + t.getMessage()); + System.out.println("Network error: " + t); + } + }); + } } + + + diff --git a/app/src/main/java/com/example/tampopo_client/viewmodels/FriendSentRequestViewModel.java b/app/src/main/java/com/example/tampopo_client/viewmodels/FriendSentRequestViewModel.java index a4260fc..c9a99ea 100644 --- a/app/src/main/java/com/example/tampopo_client/viewmodels/FriendSentRequestViewModel.java +++ b/app/src/main/java/com/example/tampopo_client/viewmodels/FriendSentRequestViewModel.java @@ -6,28 +6,29 @@ import com.example.tampopo_client.models.FriendRequest; import com.example.tampopo_client.resources.FriendRequestsResource; - import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; public class FriendSentRequestViewModel extends ViewModel { - //サーバー(API)と通信するためのツール + // サーバー(API)と通信するためのツール private final Retrofit retrofit; + // APIの窓口 private final FriendRequestsResource friendRequestsResource; - //自分が送った申請 - private final MutableLiveData> sentRequests ; - //変数がfinalやから一回しか代入できません - //sentRequestで呼び出される、viewモデルの下のコンストラクターがアンドロイドがで呼び出される - // 処理の進行中 - private final MutableLiveData operationResult ; + // 自分が送ったフレンド申請一覧 + private final MutableLiveData> sentRequests; + // 処理の進行状況や結果を表示するためのステータスメッセージ + private final MutableLiveData operationResult; + // コンストラクタ(ViewModel生成時に呼び出される) public FriendSentRequestViewModel() { - this.retrofit = new Retrofit.Builder() .baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/tampopo/") .addConverterFactory(JacksonConverterFactory.create()) @@ -36,14 +37,76 @@ this.friendRequestsResource = retrofit.create(FriendRequestsResource.class); this.sentRequests = new MutableLiveData<>(); this.operationResult = new MutableLiveData<>(); - } - //viewがobserve出来るように - public MutableLiveData> getSentRequestsLiveData() { - return sentRequests; - } -//Mutable Livedataの宣言 + // Viewがobserveできるようにgetterを用意 + public MutableLiveData> getSentRequestsLiveData() { + return sentRequests; + } + + public MutableLiveData getOperationResult() { + return operationResult; + } + + // サーバーから送信済みフレンドリクエスト一覧を取得してキャッシュに保存 + public void loadSentRequests(String token) { + Call> call = friendRequestsResource.getFriendRequests(token); + + call.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful()) { + sentRequests.setValue(response.body()); + operationResult.setValue("Success"); + System.out.println("Success SetValue: " + response.body()); + } else { + operationResult.setValue("Error: " + response.code()); + System.out.println("Response error: " + response.code()); + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + operationResult.setValue("Network error: " + t.getMessage()); + System.out.println("Network error: " + t); + } + }); + } + + // サーバーにフレンド申請を送信するメソッド + public void sendFriendRequest(String senderId, String receiverId, String token) { + Call call = friendRequestsResource.postFriendRequest(token, senderId, receiverId); + + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + //Retrofitでサーバーにリクエストを送った後、その結果が帰ってくる。成功 + operationResult.setValue("Friend request sent successfully."); + + // 成功時に送信済みリストを更新 + loadSentRequests(token); + + System.out.println("Friend request sent: " + response.body()); + } else { + operationResult.setValue("Error sending request: " + response.code()); + System.out.println("Error sending request: " + response.code()); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + operationResult.setValue("Network error: " + t.getMessage()); + System.out.println("Network error: " + t); + } + }); + } +} + + + + + @@ -123,4 +186,4 @@ // } //} -} + 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 a4e6274..9c4005c 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 @@ -64,7 +64,7 @@ }); } - public void addFriend(String token, String user0Id, String user1Id){ + public void createFriend(String token, String user0Id, String user1Id){ Call call = friendsResource.createFriend(token, user0Id, user1Id); call.enqueue(new Callback() { diff --git a/app/src/main/java/com/example/tampopo_client/views/FriendActivity.java b/app/src/main/java/com/example/tampopo_client/views/FriendActivity.java index 4e60537..362994f 100644 --- a/app/src/main/java/com/example/tampopo_client/views/FriendActivity.java +++ b/app/src/main/java/com/example/tampopo_client/views/FriendActivity.java @@ -1,58 +1,78 @@ package com.example.tampopo_client.views; - +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.Toast; -import androidx.activity.EdgeToEdge; -import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.core.graphics.Insets; -import androidx.core.view.ViewCompat; -import androidx.core.view.WindowInsetsCompat; -import androidx.lifecycle.ViewModelProvider; -import androidx.navigation.NavController; -import androidx.navigation.Navigation; -import com.example.tampopo_client.databinding.ActivityFriendBinding; -import androidx.navigation.ui.AppBarConfiguration; -import androidx.navigation.ui.NavigationUI; +import androidx.viewpager2.widget.ViewPager2; import com.example.tampopo_client.R; -import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.example.tampopo_client.views.MyFragmentAdapter; +import com.google.android.material.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; public class FriendActivity extends AppCompatActivity { - private ActivityFriendBinding binding; + + private ViewPager2 viewPager; + private TabLayout tabLayout; + private MyFragmentAdapter adapter; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.activity_friend); -// EdgeToEdge.enable(this); -// setContentView(R.layout.activity_friend); -// 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; -// }); -// } - binding = ActivityFriendBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); + viewPager = findViewById(R.id.view_pager); + tabLayout = findViewById(R.id.tab_layout); - Toolbar toolbar = findViewById(R.id.actionbar); - setSupportActionBar(toolbar); + adapter = new MyFragmentAdapter(this); + viewPager.setAdapter(adapter); - BottomNavigationView navView = findViewById(R.id.nav_view); - // Passing each menu ID as a set of Ids because each - // menu should be considered as top level destinations. - AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( - R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications) - .build(); - NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main); - NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); - NavigationUI.setupWithNavController(binding.navView, navController); + new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { + if (position == 0) { + tab.setText("フレンド"); + } else if (position == 1) { + tab.setText("保留中"); + } + }).attach(); + + //フレンド画面からメイン画面への遷移 + ImageButton buttonReturn = (ImageButton) findViewById(R.id.buttonReturn); + buttonReturn.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + Intent intent = new Intent(FriendActivity.this, MainActivity.class); + startActivity(intent); + } + }); + ImageButton openDialogButton = findViewById(R.id.buttonAdd); + openDialogButton.setOnClickListener(v -> showAddFriendDialog(this)); + + } + public void showAddFriendDialog(Context context) { + Dialog dialog = new Dialog(context); + dialog.setContentView(R.layout.dialog_add_friend); + dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); + dialog.getWindow().setDimAmount(0.5f); + EditText editFriendId = dialog.findViewById(R.id.edit_friend_id); + Button buttonSearch = dialog.findViewById(R.id.button_search); + ImageView buttonClose = dialog.findViewById(R.id.button_close); + + buttonClose.setOnClickListener(v -> dialog.dismiss()); + + buttonSearch.setOnClickListener(v -> { + String id = editFriendId.getText().toString(); + // TODO: IDを使って検索処理を行う + Toast.makeText(context, "検索: " + id, Toast.LENGTH_SHORT).show(); + }); + + dialog.show(); } - -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/tampopo_client/views/FriendReceivedFragment.java b/app/src/main/java/com/example/tampopo_client/views/FriendReceivedFragment.java index abe19ae..a836ccc 100644 --- a/app/src/main/java/com/example/tampopo_client/views/FriendReceivedFragment.java +++ b/app/src/main/java/com/example/tampopo_client/views/FriendReceivedFragment.java @@ -56,6 +56,10 @@ Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_friend_received_list, container, false); + // Add some sample items. + for (int i = 1; i <= 30; i++) + FriendRequestContent.addItem(new FriendRequestContent.FriendRequestItem(Integer.toString(i), "ユーザ名" + i)); + // Set the adapter if (view instanceof RecyclerView) { Context context = view.getContext(); 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 new file mode 100644 index 0000000..5786821 --- /dev/null +++ b/app/src/main/java/com/example/tampopo_client/views/MyFragmentAdapter.java @@ -0,0 +1,30 @@ +package com.example.tampopo_client.views; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; + +public class MyFragmentAdapter extends FragmentStateAdapter { + + public MyFragmentAdapter(@NonNull FragmentActivity fragmentActivity) { + super(fragmentActivity); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + if (position == 0) { + // フラグメント①:申請を受けた + return new FriendListFragment(); + } else { + // フラグメント②:友達リスト + return new FriendReceivedFragment(); + } + } + + @Override + public int getItemCount() { + return 2; + } +} 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 9640389..7df07fc 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 @@ -34,7 +34,7 @@ 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).content); + holder.mContentView.setText(mValues.get(position).name); } @Override diff --git a/app/src/main/java/com/example/tampopo_client/views/placeholder/FriendRequestContent.java b/app/src/main/java/com/example/tampopo_client/views/placeholder/FriendRequestContent.java index cd141e8..7882221 100644 --- a/app/src/main/java/com/example/tampopo_client/views/placeholder/FriendRequestContent.java +++ b/app/src/main/java/com/example/tampopo_client/views/placeholder/FriendRequestContent.java @@ -13,26 +13,26 @@ //各アイテムにアクセスするためのID付きマップ*/ public static final Map ITEM_MAP = new HashMap(); - private static final int COUNT = 30; //データ30個作成 + public static final int COUNT = 30; //データ30個作成 //アイテムを初期化時に追加する - static { - // Add some sample items. - for (int i = 1; i <= COUNT; i++) { - addItem(createPlaceholderItem(i)); - } - } +// static { +// // Add some sample items. +// for (int i = 1; i <= COUNT; i++) { +// addItem(createPlaceholderItem(i)); +// } +// } //アイテムをリストとマップの両方に登録するメソッド - private static void addItem(FriendRequestItem item) { + public static void addItem(FriendRequestItem item) { ITEMS.add(item); ITEM_MAP.put(item.id, item); } //位置(1,2,...)に応じたFriendRequestItem を生成するメソッド - private static FriendRequestItem createPlaceholderItem(int position) { - return new FriendRequestItem(String.valueOf(position), "ユーザー名 ", makeDetails(position)); - } +// public static FriendRequestItem createPlaceholderItem(int position) { +// return new FriendRequestItem(String.valueOf(position), "ユーザー名 ", makeDetails(position)); +// } //詳細説明用の文字列を作るメソッド。position に応じて「詳細情報」がたくさん増える private static String makeDetails(int position) { @@ -47,18 +47,18 @@ //FriendRequestを表す1件のデータ public static class FriendRequestItem { public final String id; //アイテムのID - public final String content; //表示される名前 - public final String details; //詳細説明 + public final String name; //表示される名前 + //public final String details; //詳細説明 - public FriendRequestItem(String id, String content, String details) { + public FriendRequestItem(String id, String name) { this.id = id; - this.content = content; - this.details = details; + this.name = name; + //this.details = details; } @Override public String toString() { - return content; + return name + " " + id; } } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_friend.xml b/app/src/main/res/layout/activity_friend.xml index 880385d..5217891 100644 --- a/app/src/main/res/layout/activity_friend.xml +++ b/app/src/main/res/layout/activity_friend.xml @@ -1,86 +1,81 @@ - + android:background="#BDE6FA"> - + - - - + app:layout_constraintVertical_bias="0.016" /> - - - - - - - - - - + - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_add_friend.xml b/app/src/main/res/layout/dialog_add_friend.xml new file mode 100644 index 0000000..4ea31db --- /dev/null +++ b/app/src/main/res/layout/dialog_add_friend.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + +