package com.example.wordbook; import java.io.Serializable; 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.app.SearchManager; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ListView; import android.widget.WrapperListAdapter; import com.example.wordbook.common.Common; import com.example.wordbook.common.DBAccess; import com.example.wordbook.common.Data; /** * 検索結果画面クラス */ public class SearchActivity extends Activity { /** TAG */ private static final String TAG = SearchActivity.class.getSimpleName(); /** フラグ選択ダイアログ */ private static final int DIALOG_FLAG = 1; /** メモ編集ダイアログ */ private static final int DIALOG_EDIT = 2; /** コマンド選択ダイアログ */ private static final int DIALOG_COMM = 7; /** インスタンス状態用データ先頭キー */ private static final String IS_TOP = "top"; /** インスタンス状態用データ位置キー */ private static final String IS_POS = "pos"; /** インスタンス状態用データリストキー */ private static final String IS_DATA = "data"; /** 短縮コマンド */ private int mComm; /** 検索データ位置 */ private int mPos = 0; /** 検索データリスト */ private List mData = new ArrayList(); /** ListView */ private ListView mListView; /** ListView footer */ private View mFooter; /** ExecutorService */ private ExecutorService mES; /* * (非 Javadoc) * * @see android.app.Activity#onCreate(android.os.Bundle) */ @SuppressWarnings("unchecked") @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate()"); // 状態復帰 int top = 0; if (savedInstanceState != null) { top = savedInstanceState.getInt(IS_TOP); mPos = savedInstanceState.getInt(IS_POS); mData = (List) savedInstanceState.getSerializable(IS_DATA); } Log.d(TAG, "top=" + top + "/pos=" + mPos + "/data=" + mData.size()); // 設定取得 mComm = Common.getComm(getApplicationContext()); // レイアウト設定 setContentView(R.layout.search); // タイトル設定 // searchableとmanifestで指定するandroid:labelは一致させておくべき setTitle(getString(R.string.title_search)); // ListView設定 mFooter = getLayoutInflater().inflate(R.layout.list_footer, null); mListView = (ListView) findViewById(R.id.list); mListView.addFooterView(mFooter); // アダプタ設定 WBListAdapter adp = new WBListAdapter(this, R.layout.list_wblist, mData); mListView.setAdapter(adp); // イベントリスナ設定 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { doItemClick(parent, position, id); } }); mListView.setFastScrollEnabled(true); mListView.setSelectionFromTop(top, 0); if (mData.size() == 0) { // 検索実行 doSearch(getIntent()); } else { // まだレイアウトが生成されていないため最大値を設定 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); // setVisibility()のみでは表示領域が残るためsetPadding()で調整 mFooter.setVisibility(View.GONE); mFooter.setPadding(0, -dm.heightPixels, 0, 0); } } /* * (非 Javadoc) * * @see android.app.Activity#onNewIntent(android.content.Intent) */ @Override protected void onNewIntent(Intent intent) { Log.d(TAG, "onNewIntent()"); // まだアクティビティが再起動されていないため設定が必要 setIntent(intent); // 検索実行 doSearch(intent); } /* * (非 Javadoc) * * @see android.app.Activity#onResume() */ @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume()"); // 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.putInt(IS_TOP, mListView.getFirstVisiblePosition()); outState.putInt(IS_POS, mPos); outState.putSerializable(IS_DATA, (Serializable) mData); } /* * (非 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_COMM: // コマンド選択ダイアログ生成 return getDialogComm(); 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_COMM: // コマンド選択ダイアログ準備 // 処理無し 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.search, menu); return true; } /* * (非 Javadoc) * * @see android.app.Activity#onMenuItemSelected(int, android.view.MenuItem) */ @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { int id = item.getItemId(); switch (id) { case R.id.menu_search: onSearchRequested(); break; default: Log.w(TAG, "onMenuItemSelected() id=" + id); break; } return true; } /** * フラグ選択ダイアログ生成 * * @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) { // 選択フラグ更新 mData.get(mPos).flag = which; // 更新通知 notifyUpdate(); // データ更新タスク呼び出し saveData(getIndex(mPos), mData.get(mPos)); } }); 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) { // 選択メモ更新 mData.get(mPos).data2 = edit.getText().toString(); // 更新通知 notifyUpdate(); // データ更新タスク呼び出し saveData(getIndex(mPos), mData.get(mPos)); } }); builder.setNegativeButton(android.R.string.cancel, null); builder.setView(view); return builder.create(); } /** * コマンド選択ダイアログ生成 * * @return コマンド選択ダイアログ */ private AlertDialog getDialogComm() { // ダイアログ生成 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(false); builder.setTitle(getString(R.string.title_commdialog)); // イベントリスナ設定 builder.setNegativeButton(android.R.string.cancel, null); // アイテム設定 builder.setItems(R.array.comm, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // クリック&コマンド識別 doCommClick(which, mPos); } }); return builder.create(); } /** * メモ編集ダイアログ準備 * * @param dialog * ダイアログ */ private void setDialogEdit(Dialog dialog) { // 現状メモ設定 EditText edit = (EditText) dialog.findViewById(R.id.editText); edit.setText(mData.get(mPos).data2); } /** * アイテムクリック処理 * * @param parent * AdapterView * @param position * Position * @param id * ID */ private void doItemClick(AdapterView parent, int position, long id) { Log.d(TAG, "doItemClick() id=" + id); // クリック領域識別(アイコン) if (id < 0) { // アダプタからロングクリックイベントを送信できないためIDで識別 if (id == Common.ITEM_CLICK) { // クリック&コマンド識別 doCommClick(mComm, position); } else if (id == Common.ITEM_LONG_CLICK) { // ロングクリック doCommLongClick(mComm, position); } else { Log.w(TAG, "doItemClick() id=" + id); } } } /** * アイコンクリック処理 * * @param command * Command * @param position * Position */ @SuppressWarnings("deprecation") private void doCommClick(int command, int position) { // 選択アイテム位置保存 mPos = position; // コマンド識別 switch (command) { case 0: // フラグ選択ダイアログ表示 showDialog(DIALOG_FLAG); break; case 1: // メモ編集ダイアログ表示 showDialog(DIALOG_EDIT); break; case 2: // TODO Log.d(TAG, "SOUND"); break; case 3: // TODO Log.d(TAG, "LINK"); break; default: Log.w(TAG, "doCommClick() command=" + command); break; } } /** * アイコンロングクリック処理 * * @param command * Command * @param position * Position */ @SuppressWarnings("deprecation") private void doCommLongClick(int command, int position) { // 選択アイテム位置保存 mPos = position; // コマンド選択ダイアログ表示 showDialog(DIALOG_COMM); } /** * 検索実行 * * @param intent * インテント */ private void doSearch(Intent intent) { String action = intent.getAction(); if (Intent.ACTION_SEARCH.equals(action)) { Log.d(TAG, "ACTION_SEARCH"); // QUERYには検索欄の値が設定されている String data = intent.getStringExtra(SearchManager.QUERY); if (!TextUtils.isEmpty(data)) { searchData(data, ""); } } else if (Intent.ACTION_VIEW.equals(action)) { Log.d(TAG, "ACTION_VIEW"); // DATAには選択候補の質問と解答が設定されている String[] data = intent.getData().toString().split(","); if (data.length == 2) { searchData(data[0], data[1]); } } else { Log.w(TAG, "doSearch() action=" + action); } } /** * インデックス取得 * * @param pos * 選択位置 * @return インデックス */ private int getIndex(int pos) { int value = 0; // アイテム用DataにはFILE値が含まれないためタイトル用Dataから取得する for (int i = pos - 1; i >= 0; i--) { if (mData.get(i).num == 0) { value = mData.get(i).stat; break; } } return value; } /** * 更新通知 */ private void notifyUpdate() { // フッタ設定時対応更新通知 if (mListView.getAdapter() instanceof WrapperListAdapter) { ((BaseAdapter) ((WrapperListAdapter) mListView.getAdapter()) .getWrappedAdapter()).notifyDataSetChanged(); } else { ((BaseAdapter) mListView.getAdapter()).notifyDataSetChanged(); } } /** * データ更新タスク呼び出し * * @param index * インデックス * @param data * 更新データ */ private void saveData(int index, Data data) { // データ更新タスク依頼 try { if (mES.submit(new DBAccess.SaveDataTask(getApplicationContext(), index, data)) == null) { Log.w(TAG, "saveData() Future==null"); } } catch (RejectedExecutionException e) { Log.e(TAG, "saveData()=RejectedExecutionException"); e.printStackTrace(); } } /** 検索結果表示タスク */ private AsyncTask> mTask = null; /** * 検索結果表示タスク呼び出し * * @param data1 * 質問/検索欄の値 * @param data2 * 解答 */ private void searchData(final String data1, final String data2) { // 検索結果表示タスク動作時は処理無し 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()"); // setVisibility()のみでは表示領域が残るためsetPadding()で調整 mFooter.setVisibility(View.VISIBLE); mFooter.setPadding(0, 0, 0, 0); // onNewIntent()対応 mListView.setVisibility(View.VISIBLE); // 検索データ削除 mData.clear(); notifyUpdate(); } @Override protected void onPostExecute(List result) { // setVisibility()のみでは表示領域が残るためsetPadding()で調整 mFooter.setVisibility(View.GONE); mFooter.setPadding(0, -mFooter.getHeight(), 0, 0); if (result.size() > 0) { // 検索データ追加 mData.addAll(result); notifyUpdate(); // 表示位置設定 mListView.setSelectionFromTop(0, 0); } else { // フッタ設定時はsetEmptyView()が無効のため手動で可視化 findViewById(R.id.empty).setVisibility(View.VISIBLE); mListView.setVisibility(View.GONE); } Log.d(TAG, "onPostExecute()=" + result.size()); super.onPostExecute(result); } @Override protected List doInBackground(Void... params) { Log.d(TAG, "doInBackground()=" + data1 + "/" + data2); // 単語帳DB検索結果データリスト取得 return DBAccess.getDBSearchResultList(getApplicationContext(), data1, data2); }; }.execute(); } }