package net.orleaf.android;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.provider.Settings.Secure;
import android.widget.Toast;
public class MyLogReportService extends Service {
public static final String EXTRA_PROGRESS = "progress";
public static final String EXTRA_REPORTER_ID = "reporter_id";
public static final String EXTRA_INTENT = "intent";
public static final String EXTRA_DELAY = "delay";
public static final String EXTRA_WAIT_CONNECT = "wait_connect";
public static final int WAIT_CONNECT_DISABLE = 0;
public static final int WAIT_CONNECT_ENABLE = 1;
public static final int WAIT_CONNECT_WIFIONLY = 2;
private static final String REPORT_URL = "https://orleaf.net/mailnotify/index.php/report/submit";
private static ComponentName mService;
private PendingIntent mCallbackIntent;
private String mReporterId;
private boolean mProgress;
private int mDelay;
private int mWaitConnect;
private Handler handler = new Handler();
private BroadcastReceiver mConnectivityReceiver;
private boolean mStarted = false;
private RemoteCallbackList<IMyLogReportListener> listeners = new RemoteCallbackList<IMyLogReportListener>();
private final IMyLogReportService.Stub binder = new IMyLogReportService.Stub() {
public void addListener(IMyLogReportListener listener) {
listeners.register(listener);
}
public void removeListener(IMyLogReportListener listener) {
listeners.unregister(listener);
}
};
@Override
public void onCreate() {
super.onCreate();
}
// This is the old onStart method that will be called on the pre-2.0
// platform. On 2.0 or later we override onStartCommand() so this
// method will not be called.
@SuppressWarnings("deprecation")
@Override
public void onStart(Intent intent, int startId) {
handleCommand(intent);
}
@TargetApi(5)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleCommand(intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
handleCommand(intent);
return binder;
}
/**
* ログ送信
*/
public void handleCommand(Intent intent) {
if (intent == null) {
return;
}
mCallbackIntent = intent.getParcelableExtra(EXTRA_INTENT);
mProgress = intent.getBooleanExtra(EXTRA_PROGRESS, false);
if (!mProgress) {
mDelay = intent.getIntExtra(EXTRA_DELAY, 0);
} else {
mDelay = 0;
}
mReporterId = intent.getStringExtra(EXTRA_REPORTER_ID);
mWaitConnect = intent.getIntExtra(EXTRA_WAIT_CONNECT, WAIT_CONNECT_DISABLE);
handler.postDelayed(new Runnable() {
@Override
public void run() {
start();
}
}, mDelay * 1000);
}
/**
* レポート送信
*/
private void start() {
if (mWaitConnect != WAIT_CONNECT_DISABLE) {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo == null || !networkInfo.isConnected()) {
if (mProgress) {
Toast.makeText(MyLogReportService.this, "Pending report send...", Toast.LENGTH_SHORT).show();
}
registerConnectivityReceiver();
return;
}
if (mWaitConnect == WAIT_CONNECT_WIFIONLY &&
networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
registerConnectivityReceiver();
return;
}
}
startSendReport();
}
/**
* ログ送信実行
*/
private void startSendReport() {
if (!mStarted) {
mStarted = true;
new ReportTask().execute();
}
}
/**
* ログ送信タスク
*/
private class ReportTask extends AsyncTask<Object, Integer, String> {
private String mResult = null;
/**
* 前処理
*/
@Override
protected void onPreExecute() {
if (mProgress) {
Toast.makeText(MyLogReportService.this, "Now sending report...", Toast.LENGTH_SHORT).show();
}
}
/**
* ログ送信処理居
*/
@Override
protected String doInBackground(Object... params) {
//if (BuildConfig.DEBUG) Log.d(EmailNotify.TAG, "Executing ReportTask.");
HashMap<String, String> posts = new HashMap<String, String>();
try {
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
posts.put("app_name", getResources().getString(pi.applicationInfo.labelRes));
posts.put("app_version", pi.versionName);
} catch (NameNotFoundException ignored) {}
posts.put("reporter_id", mReporterId);
posts.put("android_id", Secure.getString(getContentResolver(), Secure.ANDROID_ID));
posts.put("build_version_release", Build.VERSION.RELEASE);
posts.put("build_brand", Build.BRAND);
posts.put("build_model", Build.MODEL);
posts.put("build_id", Build.ID);
posts.put("build_fingerprint", Build.FINGERPRINT);
//posts.put("Build.VERSION.CODENAMET", Build.VERSION.CODENAME);
//posts.put("Build.VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
//posts.put("Build.VERSION.RELEASE", Build.VERSION.RELEASE);
posts.put("log", MyLog.getLogText(MyLogReportService.this));
posts.put("preferences", getPreferencesString());
return postTo(REPORT_URL, posts);
}
/**
* POST
*
* @param urlStr 送信先URL
* @param params POSTパラメタ
* @return エラー文字列 (null:成功)
*/
private String postTo(String urlStr, HashMap<String, String> params) {
StringBuilder resultBuf = new StringBuilder();
try {
URL url = new URL(urlStr);
HttpURLConnection urlconn = (HttpURLConnection)url.openConnection();
urlconn.setDoOutput(true);
PrintStream ps = new PrintStream(urlconn.getOutputStream());
ps.print(getQueryString(params));
ps.close();
BufferedReader reader = new BufferedReader(
new InputStreamReader(urlconn.getInputStream()));
String s;
while ((s = reader.readLine()) != null) {
resultBuf.append(s);
}
reader.close();
mResult = resultBuf.toString();
urlconn.disconnect();
return null;
} catch (Exception e) {
e.printStackTrace();
return e.toString();
}
}
/**
* クエリ文字列へ変換
*
* @param params パラメタ
* @return クエリ文字列
*/
@SuppressWarnings("rawtypes")
private String getQueryString(HashMap<String, String> params) throws UnsupportedEncodingException {
StringBuilder postBuf = new StringBuilder();
for (Entry<String, String> entry : params.entrySet()) {
if (postBuf.length() > 0) {
postBuf.append("&");
}
String val = entry.getValue();
if (val != null) {
val = URLEncoder.encode(val, "UTF-8");
}
postBuf.append(entry.getKey()).append("=").append(val);
}
return postBuf.toString();
}
@Override
protected void onPostExecute(String result) {
// インテントで結果通知
if (mCallbackIntent != null) {
try {
Intent intent = new Intent();
intent.putExtra("result", result);
mCallbackIntent.send(getApplicationContext(), 0, intent);
} catch (CanceledException e) {
e.printStackTrace();
}
}
// 結果表示
if (result == null) {
if (mProgress) {
Toast.makeText(MyLogReportService.this, "Sent report successfully.", Toast.LENGTH_SHORT).show();
}
} else {
if (mProgress) {
Toast.makeText(MyLogReportService.this, "Failed to send report: " + result, Toast.LENGTH_LONG).show();
}
}
// コールバック onComplete()
int numListeners = listeners.beginBroadcast();
for (int i = 0; i < numListeners; i++) {
try {
listeners.getBroadcastItem(i).onComplete(mResult);
} catch (RemoteException e) {
e.printStackTrace();
}
}
listeners.finishBroadcast();
stopSelf();
}
}
private String getPreferencesString() {
StringBuilder strBuf = new StringBuilder();
Map<String, ?> prefs = PreferenceManager.getDefaultSharedPreferences(this).getAll();
for (Entry<String, ?> entry : prefs.entrySet()) {
if (entry.getValue() == null) {
strBuf.append(entry.getKey()).append("=(null)\n");
} else {
strBuf.append(entry.getKey()).append("=").append(entry.getValue().toString()).append("\n");
}
}
return strBuf.toString();
}
/**
* ネットワーク接続監視開始
*/
private void registerConnectivityReceiver() {
if (mConnectivityReceiver == null) {
mConnectivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
start();
}
};
registerReceiver(mConnectivityReceiver,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
}
/**
* ネットワーク接続監視停止
*/
private void unregisterConnectivityReceiver() {
if (mConnectivityReceiver != null) {
unregisterReceiver(mConnectivityReceiver);
mConnectivityReceiver = null;
}
}
public void onDestroy() {
super.onDestroy();
unregisterConnectivityReceiver();
}
/**
* サービス開始 (送信中を表示)
*
* @param ctx Context
* @param reporterId Reporter ID
* @return サービス起動成否
*/
public static boolean startServiceWithProgress(Context ctx, String reporterId) {
Intent intent = new Intent(ctx, MyLogReportService.class);
intent.putExtra(EXTRA_REPORTER_ID, reporterId);
intent.putExtra(EXTRA_PROGRESS, true);
mService = ctx.startService(intent);
return (mService != null);
}
/**
* サービス開始 (送信完了時インテントを通知)
*
* @param ctx Context
* @param reporterId Reporter ID
* @param callbackIntent 送信完了時に通知するインテント
* @return サービス起動成否
*/
public static boolean startService(Context ctx, String reporterId, PendingIntent callbackIntent, int delay, int waitconn) {
Intent intent = new Intent(ctx, MyLogReportService.class);
intent.putExtra(EXTRA_REPORTER_ID, reporterId);
intent.putExtra(EXTRA_INTENT, callbackIntent);
intent.putExtra(EXTRA_DELAY, delay);
intent.putExtra(EXTRA_WAIT_CONNECT, waitconn);
mService = ctx.startService(intent);
return (mService != null);
}
}