/*
Utils.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.utils;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.WindowManager;
import android.widget.EditText;
import org.deviceconnect.android.app.simplebot.R;
import org.deviceconnect.android.app.simplebot.data.SettingData;
import org.deviceconnect.message.DConnectMessage;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import au.com.bytecode.opencsv.CSVReader;
/**
* ユーティリティクラス
*/
public class Utils {
//---------------------------------------------------------------------------------------
//region Etc.
/**
* 画面遷移
* @param fragment Fragment
* @param manager FragmentManager
*/
public static void transition(Fragment fragment, FragmentManager manager, boolean backStack) {
FragmentTransaction transaction = manager.beginTransaction();
String name = fragment.getClass().getName();
transaction.replace(R.id.container, fragment, name);
if (backStack){
transaction.addToBackStack(name);
}
transaction.commit();
}
/**
* JsonからMapへ変換する
* @param json Json
* @return Map
*/
public static Map<String, Object> jsonToMap(String json) {
if (json == null) {
return null;
}
try {
JSONObject jsonObj = new JSONObject(json);
Iterator<String> keys = jsonObj.keys();
// 今回は文字列限定
Map<String, Object> params = new HashMap<>();
while (keys.hasNext()) {
String key = keys.next();
String val = jsonObj.getString(key);
params.put(key, val);
}
return params;
} catch (JSONException e) {
return null;
}
}
/**
* Scopeのチェック
* @param context Context
* @param scopes Scope
* @param callback コールバック
*/
public static void checkScopes(Context context, List<String> scopes, final DConnectHelper.FinishCallback<Boolean> callback) {
// scopeチェック
final SettingData settings = SettingData.getInstance(context);
if (scopes == null || settings.scopes.containsAll(scopes)) {
if (callback != null) {
callback.onFinish(true, null);
}
} else {
// 認証されていないscopeがあった
final Set<String> currentScopes = new HashSet<>(settings.scopes);
settings.scopes.addAll(scopes);
// scopeを追加して再認証
settings.accessToken = null;
Utils.connect(context, new DConnectHelper.FinishCallback<DConnectHelper.AuthInfo>() {
@Override
public void onFinish(DConnectHelper.AuthInfo authInfo, Exception error) {
if (error == null) {
if (callback != null) {
callback.onFinish(false, null);
}
} else {
// scopeを戻す
settings.scopes = currentScopes;
settings.save();
if (callback != null) {
callback.onFinish(false, error);
}
}
}
});
}
}
/**
* 文字コードがUTF8かを判別する
* @param file File
* @return UTF8でtrue
* @throws IOException エラー
*/
public static boolean checkUTF8(File file) throws IOException {
InputStream input = new FileInputStream(file);
BufferedInputStream bstream = new BufferedInputStream(input);
byte[] buff = new byte[255];
bstream.read(buff);
try {
// UTF8で文字列にしてからByte配列に変換したものと元データを比較
byte[] tmp = new String(buff, "UTF8").getBytes("UTF8");
return Arrays.equals(tmp, buff);
}
catch(UnsupportedEncodingException e) {
return false;
}
}
/**
* CSVファイルを読み込む
* @param filepath ファイルパス
* @return Stringのリスト
*/
public static List<String[]> readCSV(String filepath) {
File csvfile = new File(filepath);
if (csvfile.exists()){
try {
// 文字コード判別
String encode = "UTF8";
if (!Utils.checkUTF8(csvfile)) {
encode = "ms932";
}
// CSV読み込み
InputStream stream = new FileInputStream(csvfile);
InputStreamReader reader = new InputStreamReader(stream, encode);
BufferedReader buffer = new BufferedReader(reader);
CSVReader csvReader = new CSVReader(buffer, ',', '"', 0);
// 各行読み込み
return csvReader.readAll();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
//endregion
//---------------------------------------------------------------------------------------
//region Dialog
/**
* プログレスダイアログを表示
* @param context Context
* @return ダイアログ
*/
public static ProgressDialog showProgressDialog(Context context) {
ProgressDialog dialog = new ProgressDialog(context);
dialog.setMessage("Please wait...");
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.show();
return dialog;
}
/**
* エラーダイアログ表示
* @param context Context
* @param e Exception
*/
public static void showErrorDialog(Context context, Exception e) {
String msg;
if (e != null) {
if (e instanceof DConnectHelper.DConnectHelperException) {
if (e.getClass().equals(DConnectHelper.DConnectInvalidResultException.class)) {
msg = context.getString(R.string.err_server_res);
} else {
DConnectMessage.ErrorCode code = DConnectMessage.ErrorCode.getInstance(((DConnectHelper.DConnectHelperException) e).errorCode);
msg = code.toString();
}
} else {
msg = e.toString();
}
} else {
msg = context.getString(R.string.err_occurred);
}
new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.err))
.setMessage(msg)
.setPositiveButton("OK", null)
.show();
}
/**
* 警告ダイアログ表示
* @param context Context
* @param msg メッセージ
*/
public static void showAlertDialog(Context context, String msg) {
new AlertDialog.Builder(context)
.setMessage(msg)
.setPositiveButton("OK", null)
.show();
}
/**
* 確認ダイアログ表示
* @param context Context
* @param title タイトル
* @param msg メッセージ
* @param listener OKボタンイベントリスナー
*/
public static void showConfirmDialog(Context context, String title, String msg, DialogInterface.OnClickListener listener) {
new AlertDialog.Builder(context)
.setTitle(title)
.setMessage(msg)
.setPositiveButton("OK", listener)
.setNegativeButton("Cancel", null)
.show();
}
/**
* 入力ダイアログ表示
* @param context Context
* @param title タイトル
* @param text 初期表示テキスト
* @param inputType InputType
* @param callback Callback
*/
public static void showInputDialog(Context context, String title, String text, int inputType, final DConnectHelper.FinishCallback<String> callback) {
final EditText editView = new EditText(context);
editView.setInputType(inputType);
editView.setText(text);
AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(title)
.setView(editView)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (callback != null) {
callback.onFinish(editView.getText().toString(), null);
}
}
})
.setNegativeButton("Cancel", null)
.create();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.show();
}
/**
* 選択ダイアログ表示
* @param context Context
* @param title タイトル
* @param items アイテム
* @param callback Callback
*/
public static void showSelectDialog(Context context, String title, String[] items, final DConnectHelper.FinishCallback<Integer> callback) {
new AlertDialog.Builder(context)
.setTitle(title)
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (callback != null) {
callback.onFinish(which, null);
}
}
})
.show();
}
//endregion
//---------------------------------------------------------------------------------------
//region Connection
/**
* 接続処理を行う.
*
* @param context context
* @param callback 終了コールバック
*/
public static void connect(final Context context, final DConnectHelper.FinishCallback<DConnectHelper.AuthInfo> callback) {
String appName = context.getString(R.string.app_name);
final SettingData setting = SettingData.getInstance(context);
String scopes[] = setting.getScopes();
// 接続先設定
DConnectHelper.INSTANCE.setHostInfo(
setting.ssl,
setting.host,
setting.port
);
if (setting.accessToken == null) {
// 新規接続
// 認証
DConnectHelper.INSTANCE.auth(appName, scopes, new DConnectHelper.FinishCallback<DConnectHelper.AuthInfo>() {
@Override
public void onFinish(DConnectHelper.AuthInfo authInfo, Exception error) {
Exception outError = error;
DConnectHelper.AuthInfo outAuthInfo = authInfo;
if (authInfo != null) {
// 設定に保存
setting.accessToken = authInfo.accessToken;
setting.clientId = authInfo.clientId;
} else {
if (error != null) {
// ErrorCode=2の場合はLocalOAuth非対応サーバーなので、正常終了とする。
DConnectHelper.DConnectHelperException e = (DConnectHelper.DConnectHelperException)error;
if (e.errorCode == 2) {
outError = null;
outAuthInfo = new DConnectHelper.AuthInfo("dummy_id", null);
}
}
// 失敗したらクリア
setting.accessToken = null;
setting.clientId = null;
}
setting.save();
callback.onFinish(outAuthInfo, outError);
}
});
} else {
// 接続済み情報あり
callback.onFinish(new DConnectHelper.AuthInfo(setting.clientId, setting.accessToken), null);
}
}
/**
* サービス一覧を取得する.
*
* @param context context
* @param callback 終了コールバック
*/
public static void fetchServices(final Context context, final DConnectHelper.FinishCallback<List<DConnectHelper.ServiceInfo>> callback) {
DConnectHelper.FinishCallback<DConnectHelper.AuthInfo> finishCallback = new DConnectHelper.FinishCallback<DConnectHelper.AuthInfo>() {
@Override
public void onFinish(DConnectHelper.AuthInfo authInfo, Exception error) {
if (error == null) {
// サービス検索
DConnectHelper.INSTANCE.serviceDiscovery(new DConnectHelper.FinishCallback<List<DConnectHelper.ServiceInfo>>() {
@Override
public void onFinish(List<DConnectHelper.ServiceInfo> serviceInfos, Exception error) {
if (retryCheck(context, error)) {
fetchServices(context, callback);
} else {
callback.onFinish(serviceInfos, error);
}
}
});
} else {
callback.onFinish(null, error);
}
}
};
Utils.connect(context, finishCallback);
}
/**
* サービス情報を取得する.
*
* @param context context
* @param callback 終了コールバック
*/
public static void fetchServiceInformation(final Context context, final String serviceId, final DConnectHelper.FinishCallback<Map<String, List<DConnectHelper.APIInfo>>> callback) {
DConnectHelper.FinishCallback<DConnectHelper.AuthInfo> finishCallback = new DConnectHelper.FinishCallback<DConnectHelper.AuthInfo>() {
@Override
public void onFinish(DConnectHelper.AuthInfo authInfo, Exception error) {
if (error == null) {
// サービス検索
DConnectHelper.INSTANCE.serviceInformation(serviceId, new DConnectHelper.FinishCallback<Map<String, List<DConnectHelper.APIInfo>>>() {
@Override
public void onFinish(Map<String, List<DConnectHelper.APIInfo>> apiInfos, Exception error) {
if (retryCheck(context, error)) {
fetchServiceInformation(context, serviceId, callback);
} else {
callback.onFinish(apiInfos, error);
}
}
});
} else {
callback.onFinish(null, error);
}
}
};
Utils.connect(context, finishCallback);
}
/**
* Device Connect Managerの生存確認を行う.
* @param context コンテキスト
* @param callback 生存確認結果を通知するコールバック
*/
public static void availability(final Context context, final DConnectHelper.FinishCallback<Void> callback) {
DConnectHelper.INSTANCE.availability(new DConnectHelper.FinishCallback<Void>() {
@Override
public void onFinish(final Void object, final Exception error) {
callback.onFinish(null, error);
}
});
}
/**
* イベントを登録する.
* @param context context
* @param callback 終了コールバック
*/
public static void registerEvent(final Context context, final DConnectHelper.FinishCallback<Void> callback) {
DConnectHelper.FinishCallback<DConnectHelper.AuthInfo> finishCallback = new DConnectHelper.FinishCallback<DConnectHelper.AuthInfo>() {
@Override
public void onFinish(final DConnectHelper.AuthInfo authInfo, Exception error) {
if (error == null) {
DConnectHelper.INSTANCE.openWebSocket();
callback.onFinish(null, null);
} else {
callback.onFinish(null, error);
}
}
};
Utils.connect(context, finishCallback);
}
/**
* イベントを解除する.
* @param context context
*/
public static void unregisterEvent(Context context) {
DConnectHelper.FinishCallback<DConnectHelper.AuthInfo> finishCallback = new DConnectHelper.FinishCallback<DConnectHelper.AuthInfo>() {
@Override
public void onFinish(final DConnectHelper.AuthInfo authInfo, Exception error) {
if (error == null) {
DConnectHelper.INSTANCE.closeWebSocket();
}
}
};
Utils.connect(context, finishCallback);
}
/**
* メッセージ送信
* @param context context
* @param callback 終了コールバック
*/
public static void sendMessage(final Context context, final String channel, final String text, final String resource, final DConnectHelper.FinishCallback<Void> callback) {
DConnectHelper.FinishCallback<DConnectHelper.AuthInfo> finishCallback = new DConnectHelper.FinishCallback<DConnectHelper.AuthInfo>() {
@Override
public void onFinish(DConnectHelper.AuthInfo authInfo, Exception error) {
if (error == null) {
// メッセージ送信
SettingData setting = SettingData.getInstance(context);
DConnectHelper.INSTANCE.sendMessage(setting.serviceId, channel, text, resource, new DConnectHelper.FinishCallback<Void>() {
@Override
public void onFinish(Void aVoid, Exception error) {
callback.onFinish(null, error);
}
});
} else {
callback.onFinish(null, error);
}
}
};
Utils.connect(context, finishCallback);
}
/**
* リクエスト送信
* @param context context
* @param method メソッド
* @param path パス
* @param params パラメータ
* @param callback 終了コールバック
*/
public static void sendRequest(final Context context, final String method, final String path, final String serviceId, final Map<String, Object> params, final DConnectHelper.FinishCallback<Map<String, Object>> callback) {
final DConnectHelper.FinishCallback<DConnectHelper.AuthInfo> finishCallback = new DConnectHelper.FinishCallback<DConnectHelper.AuthInfo>() {
@Override
public void onFinish(DConnectHelper.AuthInfo authInfo, Exception error) {
if (error == null) {
// リクエスト送信
DConnectHelper.INSTANCE.sendRequest(method, path, serviceId, params, new DConnectHelper.FinishCallback<Map<String, Object>>() {
@Override
public void onFinish(Map<String, Object> stringObjectMap, Exception error) {
callback.onFinish(stringObjectMap, error);
}
});
} else {
callback.onFinish(null, error);
}
}
};
Utils.connect(context, finishCallback);
}
/** 再接続カウンタ */
private static int retryCount = 0;
/**
* 再接続チェック
* @param context Context
* @param error Error
* @return 再接続必要ならtrue
*/
public static boolean retryCheck(Context context, Exception error) {
if (error instanceof DConnectHelper.DConnectInvalidResultException) {
// "clientId was not found"の場合はclientIdを消して再接続
if (((DConnectHelper.DConnectInvalidResultException) error).errorCode == 15) {
if (retryCount++ > 3) {
retryCount = 0;
return false;
}
SettingData setting = SettingData.getInstance(context);
setting.accessToken = null;
setting.clientId = null;
setting.save();
return true;
} else {
return false;
}
} else {
return false;
}
}
//endregion
//---------------------------------------------------------------------------------------
}