package com.example.citrusclient.views;
import android.content.Intent;
import android.os.Bundle; //
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
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.citrusclient.Citrus;
import com.example.citrusclient.R;
import com.example.citrusclient.rest.AccountsRest;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.jackson.JacksonConverterFactory;
public class LoginActivity extends AppCompatActivity {
private Retrofit retrofit; // サーバーと通信するためのRetrofitオブジェクト
private AccountsRest accountsRest; // APIエンドポイント(サーバーと通信するためのURL)を呼び出すためのインターフェース
private Citrus citrus; // アプリ全体を管理するオブジェクト
// 画面起動時に呼び出されるonCreateメソッド
@Override
// Androidのアクティビティは、Activity クラス(提供されている)を継承して作成される。Activity クラスは、すべてのアクティビティに共通の基本機能を提供します。そのため、アクティビティを作成する際には、Activity クラスのメソッドをoverrideしてカスタマイズすることが一般的です。
// 具体的には、onCreate メソッドは Activity クラスの一部であり、アクティビティが作成される際に呼び出される重要なメソッドです。アクティビティが初めて起動されるときには必ずこのメソッドが呼ばれます。onCreate メソッドでは、アクティビティの初期化、レイアウトの設定、リソースの読み込み、UIの構築などの処理が行われます。
protected void onCreate(Bundle savedInstanceState) { //すべてのアクティビティで実装する必要があるコールバックメソッド OnCreateメソッド 画面表示させたかったら、必ず皆かく
super.onCreate(savedInstanceState); // 最初に親クラスの onCreate メソッドを呼び出してる。これにより、親クラスで行われる初期化処理が実行。savedInstanceState パラメータは、前回の状態を復元
EdgeToEdge.enable(this); // 画面を端から端まで表示するための設定を有効にする
setContentView(R.layout.activity_login); // setContentViewで activity_login.xmlというレイアウトを設定し、画面の初期化
// ViewCompat.setOnApplyWindowInsetsListener を使用することで、ビューがシステムバーによって隠れないように、必要な余白(パディング)を自動的に設定することができる。
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { // WindowInsetsListenerの対象となる、mainというIDを持つビューを見つけます。
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); // システムバーのインセットを取得。
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); // 取得したインセットを使ってビューのパディングを設定。
return insets;
});
// アプリケーション全体の状態を管理する Citrus オブジェクトを取得 初期化
citrus = (Citrus) this.getApplication();
// ② サーバーと通信するためのRetrofitインスタンスの作成、初期化
this.retrofit = new Retrofit.Builder() // retrofitのビルダーを作成
.baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/citrus/") // サーバーのベースURLを設定
.addConverterFactory(JacksonConverterFactory.create()) // レスポンス(通信の返信)をパース(解析、変換)するためのConverter(変換ツール)を追加します。この場合はJacksonを使用
.build();
this.accountsRest = retrofit.create(AccountsRest.class);
// ボタンをクリックすると...
// loginButtonというIDを持つボタン(私らがつけた)を取得し、
Button loginButton = (Button)findViewById(R.id.loginButton);
// そのボタンにクリックリスナーを設定
loginButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){ // View.OnClickListener インターフェースに属するメソッド
//テキスト入力を文字列で認識、取得
EditText editid = (EditText) findViewById(R.id.username);
String id = editid.getText().toString().trim();
EditText editpw = (EditText) findViewById(R.id.password);
String pw = editpw.getText().toString().trim();
// Androidでスレッド間の通信を簡単に行うために使用される Handler クラスのインスタンスを作成しています
final Handler handler = new Handler();
//入力欄が空欄の時
if(id.isEmpty() && pw.isEmpty() && id.trim().isEmpty() && pw.trim().isEmpty()){
System.out.println("不適切入力");
// textView_responseっていう名前をactivity_login.xmlでつけた
((TextView)findViewById(R.id.textView_response)).setText("適切に入力してください");
// Intent intent = new Intent(LoginActivity.this,MainActivity.class);
// startActivity(intent);
// 入力欄が適切に入力されたとき
}else if(!id.isEmpty() && !pw.isEmpty() && !id.trim().isEmpty() && !pw.trim().isEmpty() ){
// ② accountsRest.login(id,pw)は、Retrofit のインターフェースを使って定義されたメソッドで、サーバーに対してログイン情報を送信
Call<String> call = accountsRest.login(id,pw);
call.enqueue(new Callback<String>() {
// ④ 通信成功
@Override
public void onResponse(Call<String> call, Response<String> response) { // Retrofit の Response クラスで提供されているもので、HTTPリクエストの結果を表すクラス
if (response.isSuccessful()){ // リクエストが成功(ステータスコード200)し、サーバーがリクエストに対する適切なレスポンスを返した
if (!response.body().equals("null")) { // サーバー側で(AccountRest)ログイン認証が行われ、入力されたIDとパスワードが一致しない場合nullをかえすってあAccountRestに書いてる
System.out.println("通信成功"); // 通常はトークンやユーザー情報などをレスポンスとして返します。
// 端末に(Citrusに)tokenとidを登録。他の画面からもログイン情報を見れるようになる
String token = response.body(); // レスポンスのボディ部分にある、tokenデータをtoken変数に代入
citrus.setToken(token);
citrus.setAccountId(id);
// 画面遷移 handlerここで使われてる
handler.post(new Runnable() { // Runnableはrunメソッドを持っている
@Override
// ログイン成功後に MainActivity へ遷移する処理
public void run() { // Runnableはrunメソッドを持っている
// Intentは、アクティビティ間の通信を管理するためのクラス
Intent intent = new Intent(LoginActivity.this, MainActivity.class); // 画面遷移
startActivity(intent);
}
});
}else{
((TextView)findViewById(R.id.textView_response)).setText(("パスワードが間違っています"));
}
}else{
//通信可能(ただしエラーが発生した場合)
//404がPW入力されてないときだけど、前で定義しているからいらないよね..?
System.out.println("通信可能");
if(response.code()==404){
((TextView)findViewById(R.id.textView_response)).setText(("IDが間違っています"));
}
}
}
// ④ 通信失敗
@Override
public void onFailure(Call<String> call, Throwable t) {
System.out.println("通信失敗");
System.out.println(t);
}
});
}
}
});
Button newButton = (Button)findViewById(R.id.newAccount);
newButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
// Intentは、アクティビティ間の通信を管理するためのクラス
Intent intent = new Intent(LoginActivity.this,SignUpActivity.class); // 画面遷移
startActivity(intent);
}
});
}
}
// キーボード
// Insertでちかちかするやつの位置変わる
// onCreate
// すべてのアクティビティで実装する必要があるコールバックメソッド
// onClick
// View.OnClickListener インターフェースに属するメソッド
// onFailure
// Callback インターフェースのメソッド Retrofitなどのライブラリで使用される非同期通信の際に、通信が失敗した場合に呼び出されるコールバック
// Response
// Retrofit の Response クラスで提供されているもので、HTTPリクエストの結果を表すクラス
// ①requestの作成
// クライアント側でユーザーがログインフォームにログインIDとパスワードを入力し、送信ボタンを押します。
// これにより、Retrofitを使ってサーバーに対するHTTPリクエストが作成
// ②requestの送信
// ③サーバーでの処理
// ④リスポンスの受け取り
//