/*
CommandDetailsFragment.java
Copyright (c) 2016 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.app.simplebot.fragment;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.text.InputType;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import org.deviceconnect.android.app.simplebot.R;
import org.deviceconnect.android.app.simplebot.data.DataManager;
import org.deviceconnect.android.app.simplebot.data.DataManager.Data;
import org.deviceconnect.android.app.simplebot.utils.DConnectHelper;
import org.deviceconnect.android.app.simplebot.utils.Utils;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* コマンド詳細画面
*/
public class CommandDetailsFragment extends Fragment implements View.OnClickListener, ShowMenuFragment {
/** メニュー */
private Menu mainMenu;
/** キーワードEditText */
private EditText editTextKeyword;
/** ServiceIDボタン */
private Button buttonServiceId;
/** APIボタン */
private Button buttonApi;
/** データボタン */
private Button buttonBody;
/** 受付レスポンスボタン */
private Button buttonAccept;
/** 受付レスポンスURIボタン */
private Button buttonAcceptUri;
/** 成功レスポンスボタン */
private Button buttonSuccess;
/** 成功レスポンスURIボタン */
private Button buttonSuccessUri;
/** 失敗レスポンスボタン */
private Button buttonError;
/** 失敗レスポンスURIボタン */
private Button buttonErrorUri;
/** コマンドデータ */
private Data commandData = new Data();
/** サービス一覧 */
private List<DConnectHelper.ServiceInfo> services;
/** 選択されたサービス*/
private DConnectHelper.ServiceInfo selectedService = null;
/** API一覧 */
private List<DConnectHelper.APIInfo> apiInfoList;
/** 選択されたAPI */
private DConnectHelper.APIInfo selectedApi;
/** 画面を戻すフラグ */
private boolean popBackFlg = false;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setHasOptionsMenu(true);
View view = inflater.inflate(R.layout.fragment_command_details, container, false);
final Context context = view.getContext();
Bundle bundle = getArguments();
if (bundle == null) {
return view;
}
Button buttonDelete = (Button)view.findViewById(R.id.buttonDelete);
Button buttonUpdate = (Button)view.findViewById(R.id.buttonUpdate);
Button buttonAdd = (Button)view.findViewById(R.id.buttonAdd);
editTextKeyword = (EditText)view.findViewById(R.id.editTextKeyword);
buttonServiceId = (Button)view.findViewById(R.id.buttonServiceId);
buttonApi = (Button)view.findViewById(R.id.buttonApi);
buttonBody = (Button)view.findViewById(R.id.buttonBody);
buttonAccept = (Button)view.findViewById(R.id.buttonAccept);
buttonAcceptUri = (Button)view.findViewById(R.id.buttonAcceptUri);
buttonSuccess = (Button)view.findViewById(R.id.buttonSuccess);
buttonSuccessUri = (Button)view.findViewById(R.id.buttonSuccessUri);
buttonError = (Button)view.findViewById(R.id.buttonError);
buttonErrorUri = (Button)view.findViewById(R.id.buttonErrorUri);
// ボタン表示切り替え
if ("add".equals(bundle.getString("mode"))) {
buttonDelete.setVisibility(View.GONE);
buttonUpdate.setVisibility(View.GONE);
} else {
buttonAdd.setVisibility(View.GONE);
}
// データ処理
final DataManager dm = new DataManager(context);
long id = bundle.getLong("id");
if (id > 0) {
// データ反映
Data d = dm.getData(id);
if (d == null) {
commandData = new Data();
} else {
commandData = d;
}
updateViews();
// KeywordはupdateViewsで更新しないので、ここで更新
editTextKeyword.setText(commandData.keyword);
}
// リスナー登録
buttonAdd.setOnClickListener(this);
buttonUpdate.setOnClickListener(this);
buttonDelete.setOnClickListener(this);
buttonServiceId.setOnClickListener(this);
buttonApi.setOnClickListener(this);
buttonBody.setOnClickListener(this);
buttonAccept.setOnClickListener(this);
buttonAcceptUri.setOnClickListener(this);
buttonSuccess.setOnClickListener(this);
buttonSuccessUri.setOnClickListener(this);
buttonError.setOnClickListener(this);
buttonErrorUri.setOnClickListener(this);
// プログレスダイアログを表示
final ProgressDialog dialog = Utils.showProgressDialog(context);
// サービス一覧を取得
Utils.fetchServices(context, new DConnectHelper.FinishCallback<List<DConnectHelper.ServiceInfo>>() {
@Override
public void onFinish(List<DConnectHelper.ServiceInfo> serviceInfos, Exception error) {
// エラーチェック
if (error != null) {
Utils.showErrorDialog(getActivity(), error);
// プログレスダイアログを閉じる
dialog.dismiss();
return;
}
services = serviceInfos;
if (services != null) {
// 選択中のサービスを再設定
for(DConnectHelper.ServiceInfo info: services) {
if (info.id.equals(commandData.serviceId)) {
selectedService = info;
// プログレスダイアログを表示
fetchServiceInformation(context, dialog);
break;
}
}
}
if (selectedService == null) {
// プログレスダイアログを閉じる
dialog.dismiss();
}
}
});
return view;
}
@Override
public void onResume() {
super.onResume();
// 画面を戻す
if (popBackFlg) {
getFragmentManager().popBackStack();
} else {
getActivity().setTitle(getString(R.string.app_name) + " [コマンド詳細]");
}
}
/**
* 画面更新
*/
private void updateViews() {
buttonServiceId.setText(checkUnset(commandData.serviceName));
buttonApi.setText(checkUnset(commandData.api));
buttonBody.setText(checkUnset(commandData.body));
buttonAccept.setText(checkUnset(commandData.accept));
buttonAcceptUri.setText(checkUnset(commandData.acceptUri));
buttonSuccess.setText(checkUnset(commandData.success));
buttonSuccessUri.setText(checkUnset(commandData.successUri));
buttonError.setText(checkUnset(commandData.error));
buttonErrorUri.setText(checkUnset(commandData.errorUri));
}
/**
* 未設定チェック
* @param str 文字列
* @return 文字列がnullの場合に未設定の文字列を返す
*/
private String checkUnset(String str) {
if (str == null || str.length() == 0) {
return getString(R.string.unset);
}
return str;
}
/**
* ServiceInformationを取得
* @param context Context
*/
private void fetchServiceInformation(final Context context, final ProgressDialog dialog) {
// 選択状態初期化
apiInfoList = null;
selectedApi = null;
Utils.fetchServiceInformation(context, selectedService.id, new DConnectHelper.FinishCallback<Map<String, List<DConnectHelper.APIInfo>>>() {
@Override
public void onFinish(Map<String, List<DConnectHelper.APIInfo>> apiInfos, Exception error) {
// プログレスダイアログを閉じる
dialog.dismiss();
// エラーチェック
if (error != null) {
Utils.showErrorDialog(context, error);
} else if (apiInfos == null) {
Utils.showAlertDialog(context, context.getString(R.string.service_not_supported));
} else {
// Profile単位を除外して一律のリストに格納
apiInfoList = new ArrayList<>();
for (Map.Entry<String, List<DConnectHelper.APIInfo>> apis: apiInfos.entrySet()) {
apiInfoList.addAll(apis.getValue());
}
// 選択中のAPIを再設定
if (commandData.path != null && commandData.method != null) {
for (DConnectHelper.APIInfo info: apiInfoList) {
String path = "/gotapi/" + info.path;
if (path.equals(commandData.path) && info.method.equals(commandData.method)) {
selectedApi = info;
break;
}
}
}
}
}
});
}
//---------------------------------------------------------------------------------------
//region Click Events
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.buttonServiceId:
onClickServiceId();
break;
case R.id.buttonAdd:
case R.id.buttonUpdate:
onClickUpdate();
break;
case R.id.buttonDelete:
onClickDelete();
break;
case R.id.buttonApi:
onClickApi();
break;
case R.id.buttonBody:
onClickData();
break;
case R.id.buttonAccept:
onClickTextButton("受付レスポンス入力", id);
break;
case R.id.buttonAcceptUri:
onClickTextButton("受付レスポンスリソースURI入力", id);
break;
case R.id.buttonSuccess:
onClickTextButton("成功レスポンス入力", id);
break;
case R.id.buttonSuccessUri:
onClickTextButton("成功レスポンスリソースURI入力", id);
break;
case R.id.buttonError:
onClickTextButton("失敗レスポンス入力", id);
break;
case R.id.buttonErrorUri:
onClickTextButton("失敗レスポンスリソースURI入力", id);
break;
}
}
/**
* サービスID選択ボタンイベント
*/
private void onClickServiceId() {
final Context context = getActivity();
// サービス存在チェック
if (services == null || services.size() == 0) {
Utils.showAlertDialog(context, getString(R.string.service_not_found));
return;
}
// 選択ダイアログ表示
String items[] = new String[services.size()];
for (int i = 0; i < services.size(); i++) {
items[i] = services.get(i).name;
}
new AlertDialog.Builder(context)
.setTitle(getString(R.string.select_service))
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 情報取得
selectedService = services.get(which);
commandData.serviceId = selectedService.id;
commandData.serviceName = selectedService.name;
commandData.api = null;
commandData.method = null;
commandData.path = null;
commandData.body = null;
// ServiceInformationを取得
// プログレスダイアログを表示
final ProgressDialog progressDialog = Utils.showProgressDialog(context);
fetchServiceInformation(context, progressDialog);
// View更新
updateViews();
}
})
.show();
}
/**
* API選択ボタンイベント
*/
private void onClickApi() {
// サービス選択チェック
if (selectedService == null) {
Utils.showAlertDialog(getActivity(), getString(R.string.service_not_selected));
return;
}
// サービス対応チェック
if (apiInfoList == null || apiInfoList.size() == 0) {
Utils.showAlertDialog(getActivity(), getString(R.string.service_not_supported));
return;
}
// 選択ダイアログ表示
String items[] = new String[apiInfoList.size()];
for (int i = 0; i < apiInfoList.size(); i++) {
items[i] = apiInfoList.get(i).name;
}
AlertDialog dialog = new AlertDialog.Builder(getActivity())
.setTitle(getString(R.string.select_api))
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DConnectHelper.APIInfo info = apiInfoList.get(which);
commandData.api = info.name;
commandData.path = "/gotapi/" + info.path;
commandData.method = info.method;
commandData.body = null;
// 選択中のAPI
selectedApi = info;
// View更新
updateViews();
}
}).create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialogInterface) {
ListView listView = ((AlertDialog) dialogInterface).getListView();
final ListAdapter originalAdapter = listView.getAdapter();
listView.setAdapter(new ListAdapter() {
@Override
public boolean areAllItemsEnabled() {
return originalAdapter.areAllItemsEnabled();
}
@Override
public boolean isEnabled(int position) {
return originalAdapter.isEnabled(position);
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
originalAdapter.registerDataSetObserver(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
originalAdapter.unregisterDataSetObserver(observer);
}
@Override
public int getCount() {
return originalAdapter.getCount();
}
@Override
public Object getItem(int position) {
return originalAdapter.getItem(position);
}
@Override
public long getItemId(int position) {
return originalAdapter.getItemId(position);
}
@Override
public boolean hasStableIds() {
return originalAdapter.hasStableIds();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = originalAdapter.getView(position, convertView, parent);
TextView textView = (TextView) view;
textView.setTextSize(16);
return textView;
}
@Override
public int getItemViewType(int position) {
return originalAdapter.getItemViewType(position);
}
@Override
public int getViewTypeCount() {
return originalAdapter.getViewTypeCount();
}
@Override
public boolean isEmpty() {
return originalAdapter.isEmpty();
}
});
}
});
dialog.show();
}
/**
* Data選択ボタンイベント
*/
private void onClickData() {
// API選択チェック
if (selectedApi == null) {
Utils.showAlertDialog(getActivity(), getString(R.string.api_not_selected));
return;
}
// 設定済みのBodyデータをパース
Map<String, Object> bodyJson = Utils.jsonToMap(commandData.body);
// 入力ダイアログ作成
final Context context = getActivity();
float dp = context.getResources().getDisplayMetrics().density;
ScrollView scrollView = new ScrollView(context);
LinearLayout rootLayout = new LinearLayout(context);
int p = (int) (10 * dp);
rootLayout.setPadding(p, 0, p, 0);
rootLayout.setOrientation(LinearLayout.VERTICAL);
scrollView.addView(rootLayout);
final Map<String, EditText> editMap = new HashMap<>();
for (DConnectHelper.APIParam param: selectedApi.params) {
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.HORIZONTAL);
// サービスIDは自動入力なので無視
if (param.name.equals(DataManager.COLUMN_SERVICE_ID)) {
continue;
}
// 説明テキスト
TextView textView = new TextView(context);
if (param.required) {
textView.setText(param.name + "*");
} else {
textView.setText(param.name);
}
textView.setLayoutParams(new LinearLayout.LayoutParams(
(int) (100 * dp),
LinearLayout.LayoutParams.WRAP_CONTENT));
layout.addView(textView);
// 入力エリア
EditText editView = new EditText(context);
editView.setHint(param.name);
// 入力制限
if ("number".endsWith(param.type) || "integer".endsWith(param.type)) {
editView.setInputType(InputType.TYPE_CLASS_NUMBER);
}
editView.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
layout.addView(editView);
editMap.put(param.name, editView);
// 設定済みのBodyデータを反映
if (bodyJson != null && bodyJson.containsKey(param.name)) {
editView.setText((String) bodyJson.get(param.name));
}
rootLayout.addView(layout);
}
// データなし
if (editMap.size() == 0) {
Utils.showAlertDialog(context, context.getString(R.string.no_data_to_set));
return;
}
// ダイアログ表示
final AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.data_set))
.setView(scrollView)
.setPositiveButton("OK", null)
.setNegativeButton("Cancel", null)
.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(final DialogInterface dialogInterface) {
Button okButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
okButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
JSONObject jsonObject = new JSONObject();
for (DConnectHelper.APIParam param: selectedApi.params) {
EditText editText = editMap.get(param.name);
if (editText == null) {
continue;
}
if (editText.getText().length() > 0) {
try {
jsonObject.put(param.name, editText.getText().toString());
} catch (JSONException e) {
e.printStackTrace();
}
} else {
// 入力必須チェック
if (param.required) {
Toast ts = Toast.makeText(context, getString(R.string.not_input_all), Toast.LENGTH_SHORT);
ts.setGravity(Gravity.CENTER, 0, 0);
ts.show();
return;
}
}
}
commandData.body = jsonObject.toString();
// View更新
updateViews();
// 閉じる
dialogInterface.dismiss();
}
});
}
});
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.show();
}
/**
* テキスト入力ボタンイベント
*/
private void onClickTextButton(String title, final int button) {
int inputType = InputType.TYPE_CLASS_TEXT;
String text = null;
switch (button) {
case R.id.buttonAccept:
text = commandData.accept;
break;
case R.id.buttonAcceptUri:
text = commandData.acceptUri;
break;
case R.id.buttonSuccess:
text = commandData.success;
break;
case R.id.buttonSuccessUri:
text = commandData.successUri;
break;
case R.id.buttonError:
text = commandData.error;
break;
case R.id.buttonErrorUri:
text = commandData.errorUri;
break;
}
Utils.showInputDialog(getActivity(), title, text, inputType, new DConnectHelper.FinishCallback<String>() {
@Override
public void onFinish(String s, Exception error) {
switch (button) {
case R.id.buttonAccept:
commandData.accept = s;
break;
case R.id.buttonAcceptUri:
commandData.acceptUri = s;
break;
case R.id.buttonSuccess:
commandData.success = s;
break;
case R.id.buttonSuccessUri:
commandData.successUri = s;
break;
case R.id.buttonError:
commandData.error = s;
break;
case R.id.buttonErrorUri:
commandData.errorUri = s;
break;
}
// View更新
updateViews();
}
});
}
/**
* 追加/更新ボタン選択イベント
*/
private void onClickUpdate() {
final Context context = getActivity();
// Keywordは変更イベントで更新していないので、ここで更新
commandData.keyword = editTextKeyword.getText().toString();
// 入力チェック
if (commandData.keyword.length() == 0 ||
commandData.path == null) {
Utils.showAlertDialog(context, getString(R.string.not_input_all));
return;
}
// scopeチェック
List<String> scopes = null;
if (selectedService != null) {
scopes = selectedService.scopes;
}
Utils.checkScopes(context, scopes, new DConnectHelper.FinishCallback<Boolean>() {
@Override
public void onFinish(Boolean aBoolean, Exception error) {
if (error == null) {
// データ保存
final DataManager dm = new DataManager(context);
if (dm.upsert(commandData)) {
// 画面を戻す
if (isResumed()) {
getFragmentManager().popBackStack();
} else {
// 画面が表示されていないのでonResumeで戻す。
popBackFlg = true;
}
} else {
Utils.showAlertDialog(context, getString(R.string.err_add_data));
}
} else {
// 認証に失敗したらエラー
Utils.showAlertDialog(context, getString(R.string.err_add_data));
}
}
});
}
/**
* 削除ボタン選択イベント
*/
private void onClickDelete() {
Utils.showConfirmDialog(getActivity(), getString(R.string.del_command), getString(R.string.confirm_del_command), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final DataManager dm = new DataManager(getActivity());
if (dm.delete(commandData.id)) {
getFragmentManager().popBackStack();
} else {
Utils.showAlertDialog(getActivity(), getString(R.string.err_del_data));
}
}
});
}
//endregion
//---------------------------------------------------------------------------------------
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.help_menu, menu);
mainMenu = menu;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_help:
Fragment fragment = new CommandDetailsHelpFragment();
Utils.transition(fragment, getFragmentManager(), true);
break;
}
return super.onOptionsItemSelected(item);
}
/**
* メニューを表示
*/
public void showMenu() {
mainMenu.performIdentifierAction(R.id.overflow_options, 0);
}
}