package org.ntlab.irisclient; import static android.os.Looper.getMainLooper; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.RotateAnimation; import android.view.animation.ScaleAnimation; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import androidx.core.os.HandlerCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import org.ntlab.irisclient.models.Drawing; import org.ntlab.irisclient.viewmodels.GameViewModel; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; public class DrawingCardFragment extends Fragment { private ImageButton[] imageButtons; private ImageView[] backColors; private Resources resources; private GameViewModel gameViewModel; private Boolean isMaster; private String myTeam; private String nowTurn; private String rid; // ロングタップで表示させるために必要。非同期で値が格納され、順番通りに保存されないからHashMapにしている private Map<Integer, Bitmap> bmImages = new HashMap<>(); // 下はテストが動くようになったら削除する private Map<Integer, Drawing> drawingList; //<dno, drawingのURL> private List<Integer> map; //cno順にdnoを管理(要するに絵の並び) private List<String> colorList; //cno順にr,g,b,dを管理:カードごとの色 private List<Boolean> nowOpenList; // コンストラクタ public static DrawingCardFragment newInstance(String str){ // インスタンス生成 DrawingCardFragment fragment = new DrawingCardFragment(); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); Iris iris = (Iris) this.getActivity().getApplication(); rid = iris.getRid(); gameViewModel = new ViewModelProvider(this).get(GameViewModel.class); gameViewModel.start(500, iris); gameViewModel.setRid(rid); System.out.println(gameViewModel.getImageLiveData()); resources = getResources(); View view = inflater.inflate(R.layout.fragment_card_drawing, container, false); // 現在のopenListを記録 nowOpenList = new ArrayList<Boolean>(){ { add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); add(false); } }; // ImageButton16個の型を使いまわしやすいように配列で使用 imageButtons = new ImageButton[] { (ImageButton) view.findViewById(R.id.imageButton0), (ImageButton) view.findViewById(R.id.imageButton1), (ImageButton) view.findViewById(R.id.imageButton2), (ImageButton) view.findViewById(R.id.imageButton3), (ImageButton) view.findViewById(R.id.imageButton4), (ImageButton) view.findViewById(R.id.imageButton5), (ImageButton) view.findViewById(R.id.imageButton6), (ImageButton) view.findViewById(R.id.imageButton7), (ImageButton) view.findViewById(R.id.imageButton8), (ImageButton) view.findViewById(R.id.imageButton9), (ImageButton) view.findViewById(R.id.imageButton10), (ImageButton) view.findViewById(R.id.imageButton11), (ImageButton) view.findViewById(R.id.imageButton12), (ImageButton) view.findViewById(R.id.imageButton13), (ImageButton) view.findViewById(R.id.imageButton14), (ImageButton) view.findViewById(R.id.imageButton15) }; backColors = new ImageView[]{ (ImageView) view.findViewById(R.id.backColour0), (ImageView) view.findViewById(R.id.backColour1), (ImageView) view.findViewById(R.id.backColour2), (ImageView) view.findViewById(R.id.backColour3), (ImageView) view.findViewById(R.id.backColour4), (ImageView) view.findViewById(R.id.backColour5), (ImageView) view.findViewById(R.id.backColour6), (ImageView) view.findViewById(R.id.backColour7), (ImageView) view.findViewById(R.id.backColour8), (ImageView) view.findViewById(R.id.backColour9), (ImageView) view.findViewById(R.id.backColour10), (ImageView) view.findViewById(R.id.backColour11), (ImageView) view.findViewById(R.id.backColour12), (ImageView) view.findViewById(R.id.backColour13), (ImageView) view.findViewById(R.id.backColour14), (ImageView) view.findViewById(R.id.backColour15) }; gameViewModel.getImageLiveData().observe( getViewLifecycleOwner(), gameJsonObserver -> { System.out.println("kota: getImageLiveDataが"); setupWithViewModel(); } ); // Openされた画像ををObserveして、Viewに反映 gameViewModel.getOpenLiveData().observe ( getViewLifecycleOwner(), openListObserver -> { System.out.println("kota: オープンされた。オープンの配列:" + openListObserver); setOpen(openListObserver); } ); gameViewModel.getEndStateLiveData().observe ( getViewLifecycleOwner(), endStateObserver -> { System.out.println("kota: " + endStateObserver); if(endStateObserver == 0) { System.out.println("kota: ゲームが終了!!:" + endStateObserver); finishGameAlertMake(myTeam, nowTurn); } } ); gameViewModel.getTurnsLiveData().observe ( getViewLifecycleOwner(), turnsObserver -> { System.out.println("kota: ターンが変更。今のターン:" + turnsObserver); nowTurn = turnsObserver; } ); System.out.println("コンストラクタでのログ確認"); System.out.println("kota: isMasterかどうか:" + isMaster); System.out.println("kota: getrid:" + iris.getRid()); System.out.println("kota: getteame:" + iris.getTeam()); System.out.println("kota: getMemberList:" + iris.getMemberList()); System.out.println("kota: getNickName:" + iris.getNickname()); System.out.println("kota: getImageLiveData:" + gameViewModel.getImageLiveData().getValue()); return view; } public void onClick(View v) { for(int i=0; i< imageButtons.length; i++) { if(v.getId() == imageButtons[i].getId()) { System.out.println( "kota: タップされたボタンの配列番号:" + i); if(nowTurn != myTeam && isMaster == true) { return; } confirmAlertMake(i); } } } public boolean onLongTap(View v) { for(int i=0; i< imageButtons.length; i++) { if(v.getId() == imageButtons[i].getId()) { System.out.println( "kota: ロングタップされたボタンの配列番号:" + i); // 自分のターンのとき、疑い機能のあるアラート表示 // 相手のターンのとき、画像だけ確認できるアラート表示 if(nowTurn == myTeam) { doubtAlertMake(i); } else { lookImageAlertMake(i); } } // めくられた画像を長押でも確認できるようにする if(v.getId() == backColors[i].getId()) { System.out.println( "kota: タップされたボタンの配列番号:" + i); lookImageAlertMake(i); } } return false; } private void setupWithViewModel() { System.out.println("kota: setupWithViewModel呼ばれた"); Iris iris = (Iris) this.getActivity().getApplication(); myTeam = iris.getTeam(); rid = iris.getRid(); isMaster = iris.isMaster(); System.out.println("kota: isMasterですかどうか:" + isMaster); System.out.println(iris.getRid()); System.out.println(iris.getTeam()); System.out.println(iris.getMemberList()); System.out.println(iris.getNickname()); drawingList = gameViewModel.getGame().getDrawingList(); map = gameViewModel.getGame().getMap(); colorList = gameViewModel.getGame().getColorList(); Context context = getContext(); for (int i = 0; i < drawingList.size(); i++) { // タップとロングタップのセット imageButtons[i].setOnClickListener(this::onClick); imageButtons[i].setOnLongClickListener(this::onLongTap); backColors[i].setOnLongClickListener(this::onLongTap); // 画像のセット String urlSt = drawingList.get(map.get(i)).getDrawing(); setImage(i, urlSt, context); // 初回だけセットする外枠の、メンバーとマスターで表示分け // メンバー:全て肌色、マスター:赤・青・黒・グレーの表示 if(isMaster == true) { if (colorList.get(i).contains("r")) { backColors[i].setBackground(resources.getDrawable(R.drawable.red_image)); } else if (colorList.get(i).contains("b")) { backColors[i].setBackground(resources.getDrawable(R.drawable.blue_image)); } else if (colorList.get(i).contains("g")) { backColors[i].setBackground(resources.getDrawable(R.drawable.gray_image)); } else if (colorList.get(i).contains("d")) { backColors[i].setBackground(resources.getDrawable(R.drawable.black_image)); } } else { backColors[i].setBackground(resources.getDrawable(R.drawable.skin_image)); } } } /** * URLから画像を取得 * Androidのルール的にメインスレッドで書くとクラッシュする * 非同期で書かなければならない **/ private void setImage(int cno, String urlSt, Context context) { System.out.println("kota: setImage呼ばれた"); // Singleの別スレッドを立ち上げる Executors.newSingleThreadExecutor().execute(() -> { try { // TODO: Drawingのsetが使われるまで、無理やり指定。 URL url = new URL(urlSt); System.out.println("kota: 表示させるURL文字列 " + url); // String urlString = "http://nitta-lab-www.is.konan-u.ac.jp/irisdata/image/" + rid + "-" + cno + ".png"; //URL url = new URL(urlString); HttpURLConnection urlCon = (HttpURLConnection) url.openConnection(); // タイムアウト設定 urlCon.setReadTimeout(10000); urlCon.setConnectTimeout(20000); // リクエストメソッド urlCon.setRequestMethod("GET"); // リダイレクトを自動で許可しない設定 urlCon.setInstanceFollowRedirects(false); InputStream is = urlCon.getInputStream(); Bitmap originalBMP = BitmapFactory.decodeStream(is); Bitmap resizedMiniBMP = resize(originalBMP, (int) convertDp2Px(105, context),(int) convertDp2Px(60, context)); Bitmap resizedBigBMP = resize(originalBMP, (int) convertDp2Px(420, context),(int) convertDp2Px(240, context)); bmImages.put(cno, resizedBigBMP); HandlerCompat.createAsync(getMainLooper()).post(() -> // Mainスレッドに返す imageButtons[cno].setImageBitmap(resizedMiniBMP) ); } catch (IOException e) { e.printStackTrace(); } }); } /** * dpとpxを変換 * BitMapでImageを描画するためにどうしても必要だった **/ private static float convertDp2Px(float dp, Context context) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return dp * metrics.density; } /** * 表示させる画像のサイズ調整 **/ private static Bitmap resize(Bitmap image, int maxWidth, int maxHeight) { if (maxHeight > 0 && maxWidth > 0) { int width = image.getWidth(); int height = image.getHeight(); float ratioBitmap = (float) width / (float) height; float ratioMax = (float) maxWidth / (float) maxHeight; int finalWidth = maxWidth; int finalHeight = maxHeight; if (ratioMax > 1) { finalWidth = (int) ((float)maxHeight * ratioBitmap); } else { finalHeight = (int) ((float)maxWidth / ratioBitmap); } image = Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true); return image; } else { return image; } } /** * 開けられたImageをViewに反映させる * Observeして変更加わった度に呼ばれる **/ private void setOpen(List<Boolean> openList) { for(int i=0; i< openList.size(); i++) { if (openList.get(i) == true && nowOpenList.get(i) == false) { // オープンだったときの描画 if (colorList.get(i).contains("r")) { backColors[i].setBackground(resources.getDrawable(R.drawable.red_image)); imageButtons[i].setVisibility(View.INVISIBLE); } else if (colorList.get(i).contains("b")) { backColors[i].setBackground(resources.getDrawable(R.drawable.blue_image)); imageButtons[i].setVisibility(View.INVISIBLE); } else if (colorList.get(i).contains("g")) { backColors[i].setBackground(resources.getDrawable(R.drawable.gray_image)); imageButtons[i].setVisibility(View.INVISIBLE); } else if (colorList.get(i).contains("d")) { backColors[i].setBackground(resources.getDrawable(R.drawable.black_image)); imageButtons[i].setVisibility(View.INVISIBLE); gameViewModel.sendEndState(); //finishGameAlertMake(myTeam, nowTurn); } setAnime(i); nowOpenList.set(i, true); } } } //----------------------------------------------------------------------------- // アラート関係の関数 /** * 本当に裏返してよいか確認させるアラート */ private void confirmAlertMake(int cno){ String strTitle = "注意"; String strMessage = "本当にカードをめくってよろしいですか?"; AlertDialog.Builder builder; builder = new AlertDialog.Builder(getContext()); builder.setMessage(strMessage); builder.setTitle(strTitle); builder.setPositiveButton("ok", (dialog, id) -> setOpenRealTime(cno, true)); builder.setNegativeButton("キャンセル", (dialog, id) -> setOpenRealTime(cno, false)); builder.create(); builder.show(); } /** * アラートから呼ばれる、裏返す通信処理 **/ private void setOpenRealTime(int cno, boolean isOK) { gameViewModel.sendOpenList(cno); } /** * 自分のターンのときに、 * 疑うか疑わないかのアラートを表示させるコード */ private void doubtAlertMake(int cno){ String strTitle = "疑いますか?"; AlertDialog.Builder builder; builder = new AlertDialog.Builder(getContext()); builder.setTitle(strTitle); ImageView iv = new ImageView(getContext()); iv.setImageBitmap(bmImages.get(cno)); iv.setAdjustViewBounds(true); builder.setView(iv); builder.setPositiveButton("疑う", (dialog, id) -> setDoubt(cno, true)); builder.setNegativeButton("キャンセル", (dialog, id) -> setDoubt(cno, false)); builder.create(); builder.show(); } /** * アラートから呼ばれる、疑いをかける処理 **/ private void setDoubt(int cno, boolean isOK) { // 疑う処理 // 実はまだViewは書けてない gameViewModel.sendQ(cno); } /** * 相手のターンのときに、 * 画像だけ確認できるアラートを表示させるコード */ private void lookImageAlertMake(int cno){ String strTitle = ""; AlertDialog.Builder builder; builder = new AlertDialog.Builder(getContext()); builder.setTitle(strTitle); ImageView iv = new ImageView(getContext()); iv.setImageBitmap(bmImages.get(cno)); iv.setAdjustViewBounds(true); builder.setView(iv); builder.setPositiveButton("完了", (dialog, id) -> setNone(cno, true)); builder.create(); builder.show(); } /** * アラートからOKを呼ばれたときにする処理 * まだ空 **/ private void setNone(int cno, boolean isOK) { } private void setAnime(int cno){ ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); // animation時間 msec scaleAnimation.setDuration(2000); RotateAnimation rotate = new RotateAnimation(0.0f, 120.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.5f); // animation時間 msec rotate.setDuration(2000); AnimationSet animationSet = new AnimationSet( true ); // animationSetにそれぞれ追加する animationSet.addAnimation( scaleAnimation ); animationSet.addAnimation( rotate ); imageButtons[cno].startAnimation(animationSet); } /** * 黒いカードがひかれて、ゲームが終わったときに呼ばれるアラート */ private void finishGameAlertMake(String myTeam, String nowTurn) { String strTitle; String strMessage; System.out.println("ゲームが終わった時のmyTeam:" + myTeam); System.out.println("ゲームが終わった時のmyTurn:" + nowTurn); if (myTeam == nowTurn) { strTitle = "Your Lose..."; strMessage = "お疲れ様でした。\n残念ながら、あなたチームは負けました..."; } else { strTitle = "Your Win!!"; strMessage = "お疲れ様でした。\nおめでとうございます、あなたチームの勝利です!"; } AlertDialog.Builder builder; builder = new AlertDialog.Builder(getContext()); builder.setTitle(strTitle); builder.setMessage(strMessage); builder.setPositiveButton("完了", (dialog, id) -> setFinish()); builder.create(); builder.show(); } // TODO: 未完成の関数 /** * ゲームが終了したときに呼ばれる関数 * 画面をトップに戻る動作でも書く? **/ private void setFinish() { Intent intent = new Intent(this.getActivity().getApplication(), MainActivity.class); startActivity(intent); } }