package com.example.wordbook; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.text.Html; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.RatingBar; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import com.example.wordbook.common.Common; import com.example.wordbook.common.DBAccess; import com.example.wordbook.common.Data; import com.example.wordbook.common.DataManager; /** * 詳細画面クラス */ public class DetailActivity extends Activity { /** TAG */ private static final String TAG = DetailActivity.class.getSimpleName(); /** フラグ選択ダイアログ */ private static final int DIALOG_FLAG = 1; /** メモ編集ダイアログ */ private static final int DIALOG_EDIT = 2; /** 試験結果ダイアログ */ private static final int DIALOG_RESULT = 5; /** 位置指定ダイアログ */ private static final int DIALOG_SEEK = 6; /** インスタンス状態用キー */ private static final String IS_DATA = "data"; /** 単語帳DBデータインデックス */ private int mIndex; /** 単語帳DBデータ項目数 */ private int mCount; /** 出題方法 */ private int mMode; /** 出題順序 */ private int mSort; /** 試験単位 */ private int mSuite; /** アイコン位置 */ private int mIcon; /** データ管理クラス */ private DataManager mDM = new DataManager(); /** Level rating bar */ private RatingBar mRateLv; /** Number text field */ private TextView mTxtNo; /** Question text field */ private TextView mTxtQ; /** Answer text field */ private TextView mTxtA; /** Data text field */ private TextView mTxtD; /** Before view */ private View mViewBefore; /** After view */ private View mViewAfter; /** ExecutorService */ private ExecutorService mES; /* * (非 Javadoc) * * @see android.app.Activity#onCreate(android.os.Bundle) */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate()"); // 単語帳DBデータ情報取得 int base = 0; String title = ""; Bundle ext = getIntent().getExtras(); if (ext == null) { Log.w(TAG, "onCreate() Intent==null"); finish(); } else { mIndex = ext.getInt(Common.INTENT_INDEX, 0); mCount = ext.getInt(Common.INTENT_COUNT, 0); base = ext.getInt(Common.INTENT_BASE, 0); title = ext.getString(Common.INTENT_TITLE); } Log.d(TAG, "index=" + mIndex + "/count=" + mCount + "/base=" + base + "/title=" + title); // 設定取得 mMode = Common.getMode(getApplicationContext()); mSort = Common.getSort(getApplicationContext()); mSuite = Common.getSuite(getApplicationContext()); mIcon = Common.getIcon(getApplicationContext()); // 状態復帰 if (savedInstanceState != null) { mDM = (DataManager) savedInstanceState.getSerializable(IS_DATA); } else { mDM.init(base); } Log.d(TAG, "size=" + mDM.getDataList().size()); // レイアウト設定 setContentView(R.layout.detail); // タイトル設定 setTitle(title); // アイコン用イベントリスナ設定 final ImageView imgFlag = (ImageView) findViewById(R.id.imageViewFlag); imgFlag.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doClickFlag(view); } }); final ImageView imgEdit = (ImageView) findViewById(R.id.imageViewEdit); imgEdit.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doClickEdit(view); } }); final ImageView imgSound = (ImageView) findViewById(R.id.imageViewSound); imgSound.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doClickSound(view); } }); final ImageView imgLink = (ImageView) findViewById(R.id.imageViewLink); imgLink.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doClickLink(view); } }); // ボタン用イベントリスナ設定 final Button btnChk = (Button) findViewById(R.id.buttonCheck); btnChk.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doClickCheck(); } }); final Button btnNG = (Button) findViewById(R.id.buttonNG); btnNG.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doClickNG(); } }); final Button btnOK = (Button) findViewById(R.id.buttonOK); btnOK.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doClickOK(); } }); // 表示用ビュー取得 mRateLv = (RatingBar) findViewById(R.id.ratingBarLevel); mTxtNo = (TextView) findViewById(R.id.textViewID); mTxtQ = (TextView) findViewById(R.id.textViewQusetion); mTxtA = (TextView) findViewById(R.id.textViewAnswer); mTxtD = (TextView) findViewById(R.id.textViewData); mViewBefore = findViewById(R.id.layoutBefore); mViewAfter = findViewById(R.id.layoutAfter); // アイコン位置設定 if (mIcon == 1) { // 右側に再設定 int chk = R.drawable.btn_check; int ng = R.drawable.btn_ng; int ok = R.drawable.btn_ok; btnChk.setCompoundDrawablesWithIntrinsicBounds(0, 0, chk, 0); btnNG.setCompoundDrawablesWithIntrinsicBounds(0, 0, ng, 0); btnOK.setCompoundDrawablesWithIntrinsicBounds(0, 0, ok, 0); } // 結果選択欄表示設定 switch (mMode) { case Data.STAT_QUESTION: // break; case Data.STAT_ANSWER: // Check欄表示 showResultField(false); break; case Data.STAT_SHOW: // OK/NG欄表示 showResultField(true); break; default: Log.w(TAG, "onCreate() mode=" + mMode); break; } // 初期値データ設定 if (mDM.getDataList().size() == 0) { // UI制御用情報 List list = new ArrayList(); list.add(imgFlag); list.add(imgEdit); list.add(imgSound); list.add(imgLink); list.add(btnChk); list.add(btnNG); list.add(btnOK); // 試験データ追加タスク呼び出し loadData(list, true); } } /* * (非 Javadoc) * * @see android.app.Activity#onResume() */ @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume()"); // 試験データ表示 showData(); // ExecutorService開始 mES = Executors.newSingleThreadExecutor(); } /* * (非 Javadoc) * * @see android.app.Activity#onPause() */ @Override protected void onPause() { super.onPause(); Log.d(TAG, "onPause()"); // タスク動作時はキャンセル if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) { Log.w(TAG, "onPause()=RUNNING"); // キャンセル mTask.cancel(true); } // ExecutorService終了 final long TIMEOUT = 100; mES.shutdown(); try { // タスク終了待ち if (!mES.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS)) { mES.shutdownNow(); Log.w(TAG, "onPause()=TIMEOUT"); } } catch (InterruptedException e) { mES.shutdownNow(); Thread.currentThread().interrupt(); Log.e(TAG, "onPause()=InterruptedException"); e.printStackTrace(); } } /* * (非 Javadoc) * * @see android.app.Activity#onSaveInstanceState(android.os.Bundle) */ @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, "onSaveInstanceState()"); // 状態保存 outState.putSerializable(IS_DATA, mDM); } /* * (非 Javadoc) * * @see android.app.Activity#onCreateDialog(int) */ @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_FLAG: // フラグ選択ダイアログ生成 return getDialogFlag(); case DIALOG_EDIT: // メモ編集ダイアログ生成 return getDialogEdit(); case DIALOG_RESULT: // 試験結果ダイアログ生成 return getDialogResult(); case DIALOG_SEEK: // 位置指定ダイアログ生成 return getDialogSeek(); default: Log.w(TAG, "onCreateDialog() id=" + id); return null; } } /* * (非 Javadoc) * * @see android.app.Activity#onPrepareDialog(int, android.app.Dialog) */ @Override protected void onPrepareDialog(int id, Dialog dialog) { switch (id) { case DIALOG_FLAG: // フラグ選択ダイアログ準備 // 処理無し break; case DIALOG_EDIT: // メモ編集ダイアログ準備 setDialogEdit(dialog); break; case DIALOG_RESULT: // 試験結果ダイアログ準備 setDialogResult(dialog); break; case DIALOG_SEEK: // 位置指定ダイアログ準備 setDialogSeek(dialog); break; default: Log.w(TAG, "onPrepareDialog() id=" + id); break; } } /* * (非 Javadoc) * * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.sub, menu); return true; } /* * (非 Javadoc) * * @see android.app.Activity#onMenuItemSelected(int, android.view.MenuItem) */ @SuppressWarnings("deprecation") @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { int id = item.getItemId(); switch (id) { case R.id.menu_seek: // 位置指定ダイアログ表示 showDialog(DIALOG_SEEK); break; default: Log.w(TAG, "onMenuItemSelected() id=" + id); break; } return true; } /* * (非 Javadoc) * * @see android.app.Activity#finish() */ @Override public void finish() { // 応答設定 Intent intent = new Intent(); Bundle ext = new Bundle(); ext.putInt(Common.INTENT_INDEX, mIndex); ext.putInt(Common.INTENT_BASE, mDM.getBasePos()); intent.putExtras(ext); setResult(RESULT_OK, intent); super.finish(); // overridePendingTransition(0, 0); } /** * フラグ選択ダイアログ生成 * * @return フラグ選択ダイアログ */ private AlertDialog getDialogFlag() { // ダイアログ生成 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(false); builder.setTitle(getString(R.string.item_flag)); // イベントリスナ設定 builder.setNegativeButton(android.R.string.cancel, null); // アダプタ設定 FlagAdapter adapter = new FlagAdapter(this, Common.getFlagItemList(getApplicationContext()), R.layout.dlg_flag, new String[] { Common.FLAG_TEXT }, new int[] { R.id.textView }); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 選択フラグ更新 mDM.setFlag(which); // 試験データ表示 // showData();ではなく直接変更 Common.setBackgroundBorder(mTxtQ, which); // 試験データ更新タスク呼び出し saveData(); } }); return builder.create(); } /** * メモ編集ダイアログ生成 * * @return メモ編集ダイアログ */ private AlertDialog getDialogEdit() { // ダイアログ生成 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(false); builder.setTitle(getString(R.string.item_edit)); // インフレート対象にダイアログ用テーマを反映 LayoutInflater inflater = builder.create().getLayoutInflater(); View view = inflater.inflate(R.layout.dlg_edit, null); // ビュー取得 final EditText edit = (EditText) view.findViewById(R.id.editText); // イベントリスナ設定 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 選択メモ更新 mDM.setData2(edit.getText().toString()); // 試験データ表示 showData(); // 試験データ更新タスク呼び出し saveData(); } }); builder.setNegativeButton(android.R.string.cancel, null); builder.setView(view); return builder.create(); } /** * 試験結果ダイアログ生成 * * @return 試験結果ダイアログ */ private AlertDialog getDialogResult() { // ダイアログ生成 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(false); builder.setTitle(getString(R.string.title_resultdialog)); // インフレート対象にダイアログ用テーマを反映 LayoutInflater inflater = builder.create().getLayoutInflater(); View view = inflater.inflate(R.layout.dlg_result, null); // イベントリスナ設定 builder.setPositiveButton(R.string.label_next, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { doClickResultSelect(which); } }); builder.setNegativeButton(R.string.label_retry, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { doClickResultSelect(which); } }); builder.setView(view); return builder.create(); } /** * 位置指定ダイアログ生成 * * @return 位置指定ダイアログ */ private AlertDialog getDialogSeek() { // ダイアログ生成 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(false); builder.setTitle(getString(R.string.menu_seek)); // インフレート対象にダイアログ用テーマを反映 LayoutInflater inflater = builder.create().getLayoutInflater(); View view = inflater.inflate(R.layout.dlg_seek, null); // ビュー取得 final TextView text = (TextView) view.findViewById(R.id.textView); final SeekBar seek = (SeekBar) view.findViewById(R.id.seekBar); // 最大値&移動単位設定 seek.setMax((mCount - 1) / mSuite); seek.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // 表示は1から text.setText(progress * mSuite + 1 + " / " + mCount); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); // イベントリスナ設定 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // データ先頭更新 mDM.setBasePos(seek.getProgress() * mSuite); } }); builder.setNegativeButton(android.R.string.cancel, null); builder.setView(view); return builder.create(); } /** * メモ編集ダイアログ準備 * * @param dialog * ダイアログ */ private void setDialogEdit(Dialog dialog) { // 現状メモ設定 EditText edit = (EditText) dialog.findViewById(R.id.editText); edit.setText(mDM.getData().data2); } /** * 試験結果ダイアログ準備 * * @param dialog * ダイアログ */ private void setDialogResult(Dialog dialog) { // 試験データ準備状態確認 if (mDM.getTempList().size() == 0) { // 試験データ準備完了まで次へボタン無効化 if (dialog instanceof AlertDialog) { // UI制御用情報 List list = new ArrayList(); list.add(((AlertDialog) dialog) .getButton(DialogInterface.BUTTON_POSITIVE)); // 試験データ追加タスク呼び出し loadData(list, false); } } // OK結果設定 TextView ok = (TextView) dialog.findViewById(R.id.textViewOK); ok.setText(Integer.toString(mDM.getOKCount())); // NG結果設定 TextView ng = (TextView) dialog.findViewById(R.id.textViewNG); ng.setText(Integer.toString(mDM.getNGCount())); } /** * 位置指定ダイアログ準備 * * @param dialog * ダイアログ */ private void setDialogSeek(Dialog dialog) { // 現状データ先頭設定 TextView text = (TextView) dialog.findViewById(R.id.textView); SeekBar seek = (SeekBar) dialog.findViewById(R.id.seekBar); int pos = mDM.getBasePos(); // 表示は1から text.setText(pos + 1 + " / " + mCount); // 一旦クリアしないとバーの色が更新されない seek.setProgress(0); seek.setProgress(pos / mSuite); } /** * Flagアイコン処理 * * @param view * View */ @SuppressWarnings("deprecation") private void doClickFlag(View view) { // フラグ選択ダイアログ表示 showDialog(DIALOG_FLAG); } /** * Editアイコン処理 * * @param view * View */ @SuppressWarnings("deprecation") private void doClickEdit(View view) { // メモ編集ダイアログ表示 showDialog(DIALOG_EDIT); } /** * Soundアイコン処理 * * @param view * View */ private void doClickSound(View view) { // TODO Log.d(TAG, "SOUND"); } /** * Linkアイコン処理 * * @param view * View */ private void doClickLink(View view) { // TODO Log.d(TAG, "LINK"); } /** * Checkボタン処理 */ private void doClickCheck() { switch (mMode) { case Data.STAT_QUESTION: // break; case Data.STAT_ANSWER: // 表示状態更新 mDM.setStat(Data.STAT_SHOW); // 試験データ表示 showData(); // OK/NG欄表示 showResultField(true); break; default: Log.w(TAG, "doClickCheck() mode=" + mMode); break; } } /** * NGボタン選択処理 */ @SuppressWarnings("deprecation") private void doClickNG() { switch (mMode) { case Data.STAT_QUESTION: // break; case Data.STAT_ANSWER: // Check欄表示 showResultField(false); // 表示状態更新 mDM.setStat(mMode); // break; //$FALL-THROUGH$ case Data.STAT_SHOW: // NG結果更新 mDM.addNG(); // 試験データ位置更新 if (mDM.setNextPos()) { // 試験データ表示 showData(); } else { // 試験データリスト更新タスク呼び出し saveDataList(); // 試験結果ダイアログ表示 showDialog(DIALOG_RESULT); } break; default: Log.w(TAG, "doClickNG() mode=" + mMode); break; } } /** * OKボタン選択処理 */ @SuppressWarnings("deprecation") private void doClickOK() { switch (mMode) { case Data.STAT_QUESTION: // break; case Data.STAT_ANSWER: // Check欄表示 showResultField(false); // 表示状態更新 mDM.setStat(mMode); // break; //$FALL-THROUGH$ case Data.STAT_SHOW: // OK結果更新 mDM.addOK(); // 試験データ位置更新 if (mDM.setNextPos()) { // 試験データ表示 showData(); } else { // 試験データリスト更新タスク呼び出し saveDataList(); // 試験結果ダイアログ表示 showDialog(DIALOG_RESULT); } break; default: Log.w(TAG, "doClickOK() mode=" + mMode); break; } } /** * Result選択処理 * * @param which * 選択結果 */ private void doClickResultSelect(int which) { // この時点では試験データ準備完了 switch (which) { case DialogInterface.BUTTON_POSITIVE: // 次回試験データ先頭=試験データ先頭+試験単位 int base = mDM.getBasePos() + mSuite; // 項目数超過時は初期化 if (base >= mCount) { base = 0; } // データ先頭更新 mDM.setBasePos(base); // 次へ mDM.setNextPage(); // 試験データ表示 showData(); break; case DialogInterface.BUTTON_NEGATIVE: // 再試験 mDM.setZeroPos(); // 試験データ表示 showData(); break; default: Log.w(TAG, "doClickResultSelect() which=" + which); break; } } /** * 結果選択欄表示 * * @param flag * 表示状態 */ private void showResultField(boolean flag) { if (flag) { // OK/NG欄表示 mViewBefore.setVisibility(View.GONE); mViewAfter.setVisibility(View.VISIBLE); } else { // Check欄表示 mViewBefore.setVisibility(View.VISIBLE); mViewAfter.setVisibility(View.GONE); } } /** * 試験データ表示 */ private void showData() { // 表示データ取得 Data data = mDM.getData(); // 取得失敗 if (data.num == 0) { return; } // レーティング mRateLv.setRating(data.level); // 試験中データ位置 mTxtNo.setText(mDM.getDataPos() + 1 + " / " + mDM.getDataList().size()); // 質問欄背景色 Common.setBackgroundBorder(mTxtQ, data.flag); // 質問欄設定 String q = data.question; if (data.data1.length() > 0) { /* TODO 発音記号追加 */ q += " / " + data.data1 + ""; } CharSequence cs = Html.fromHtml(q); // 文字列設定 switch (data.stat & Data.STAT_SHOW) { case Data.STAT_QUESTION: mTxtQ.setText(cs); mTxtA.setText(""); mTxtD.setText(data.data2); break; case Data.STAT_ANSWER: mTxtQ.setText(""); mTxtA.setText(data.answer); mTxtD.setText(data.data2); break; case Data.STAT_SHOW: mTxtQ.setText(cs); mTxtA.setText(data.answer); mTxtD.setText(data.data2); break; default: Log.w(TAG, "showData() stat=" + data.stat); break; } } /** * 試験データ更新タスク呼び出し */ private void saveData() { Data data = mDM.getData(); // 試験データ更新タスク依頼 try { if (mES.submit(new DBAccess.SaveDataTask(getApplicationContext(), mIndex, data)) == null) { Log.w(TAG, "saveData() Future==null"); } } catch (RejectedExecutionException e) { Log.e(TAG, "saveData()=RejectedExecutionException"); e.printStackTrace(); } } /** * 試験データリスト更新タスク呼び出し */ private void saveDataList() { List list = mDM.getDataList(); // 試験データリスト更新タスク依頼 try { if (mES.submit(new DBAccess.SaveDataListTask( getApplicationContext(), mIndex, list)) == null) { Log.w(TAG, "saveDataList() Future==null"); } } catch (RejectedExecutionException e) { Log.e(TAG, "saveDataList()=RejectedExecutionException"); e.printStackTrace(); } } /** 試験データ追加タスク */ private AsyncTask mTask = null; /** * 試験データ追加タスク呼び出し * * @param list * 無効化ビューリスト * @param init * true:初期値データ追加/false:その他データ追加 */ private void loadData(final List list, final boolean init) { // 試験データ追加タスク動作時は処理無し if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) { Log.w(TAG, "loadData()=RUNNING"); return; } // 試験データ追加タスク実行 mTask = new AsyncTask() { @Override protected void onPreExecute() { super.onPreExecute(); Log.d(TAG, "onPreExecute()"); // 無効化 for (View view : list) { view.setEnabled(false); } } @Override protected void onPostExecute(Integer result) { if (result > 0) { // 試験データ表示 showData(); // 有効化 for (View view : list) { view.setEnabled(true); } } else { // データ無しメッセージ設定 findViewById(R.id.empty).setVisibility(View.VISIBLE); // 消去 View[] view = { findViewById(R.id.footer), findViewById(R.id.v0), findViewById(R.id.v1), findViewById(R.id.layoutBefore), findViewById(R.id.layoutAfter) }; for (View v : view) { v.setVisibility(View.GONE); } } Log.d(TAG, "onPostExecute()=" + result); super.onPostExecute(result); } @Override protected Integer doInBackground(Void... params) { Log.d(TAG, "doInBackground()"); // 次回試験データ先頭=試験データ先頭+試験単位 int base = mDM.getBasePos(); // 初期値データ以外の場合のみ試験単位を加算 if (!init) { base += mSuite; } // 項目数超過時は初期化 if (base >= mCount) { base = 0; } // 単語帳DBデータリスト取得 List temp = DBAccess.getDBDataList( getApplicationContext(), mIndex, base, mSuite, mSort, null); // 表示状態設定 for (int i = 0; i < temp.size(); i++) { temp.get(i).stat = mMode; } // 一時保存データリスト追加 mDM.addTempList(temp); // 初期値データの場合は初期設定が必要 if (init) { mDM.setNextPage(); } return temp.size(); }; }.execute(); } }