package com.dianping.loader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Locale;
import org.json.JSONObject;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.Html;
import android.text.TextUtils;
import android.view.ViewGroup;
import android.widget.TextView;
import com.dianping.app.MyActivity;
import com.dianping.app.MyApplication;
import com.dianping.loader.model.SiteSpec;
import com.dianping.util.EmbedHttpServer;
/**
* 开发调试启动入口
* <p>
*
* Intent参数:<br>
* port:int,指定服务器的监听端口号,默认为5036<br>
*
* @author yimin.tu
*
*/
public class DevLoaderActivity extends MyActivity {
private TextView text;
private MyHttpServer server;
private RepositoryManager repoManager;
private SiteSpec site;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
repoManager = ((MyApplication) getApplication()).repositoryManager();
text = new TextView(this);
text.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(text);
int port = getIntent().getIntExtra("port", 5036);
server = new MyHttpServer(port);
try {
server.start();
println("server started on port " + port);
} catch (Exception e) {
print("unable to start server on port " + port + ": ");
println(e);
}
}
@Override
protected void onDestroy() {
try {
server.stop();
} catch (Exception e) {
}
super.onDestroy();
}
private void print(final Object obj) {
if (Thread.currentThread().getId() == Looper.getMainLooper()
.getThread().getId()) {
text.append(String.valueOf(obj));
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
print(obj);
}
});
}
}
private void println(final Object obj) {
if (Thread.currentThread().getId() == Looper.getMainLooper()
.getThread().getId()) {
text.append(String.valueOf(obj));
text.append("\n");
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
println(obj);
}
});
}
}
/**
* GET /list<br>
* return list of files in repo<br>
* <p>
*
* GET /repo/...<br>
* PUT /repo/...<br>
* DELETE /repo/...<br>
* get or upload or delete file in repo<br>
* <p>
*
* PUT /site<br>
* PUT /site.txt<br>
* upload the site.txt file in this session<br>
* <p>
*
* GET /go/[host]?[params]<br>
* launch the host with params<br>
*
* GET /debug/[host]?[params]<br>
* launch and debug (Waiting For Debugger)<br>
*
* @author yimin.tu
*
*/
private class MyHttpServer extends EmbedHttpServer {
public MyHttpServer(int port) {
super(port);
}
@Override
protected void handle(String method, String path,
HashMap<String, String> headers, InputStream input,
ResponseOutputStream response) throws Exception {
final String origPath = path;
println(method + " " + origPath);
path = path.toLowerCase(Locale.US);
String query = "";
{
int i = path.indexOf('?');
if (i != -1) {
query = path.substring(i + 1);
path = path.substring(0, i);
}
i = query.indexOf('#');
if (i != -1) {
query = query.substring(0, i);
}
}
if ("GET".equalsIgnoreCase(method)
&& "/list".equalsIgnoreCase(path)) {
response.setContentTypeText();
File dir = repoManager.getDir();
dir.mkdirs();
int count = 0;
for (File id : dir.listFiles()) {
if (id.isDirectory()) {
for (File file : id.listFiles()) {
if (file.isFile()
&& file.getName().endsWith(".apk")) {
String str = id.getName() + "/"
+ file.getName() + "\n";
response.write(str.getBytes("ASCII"));
count++;
}
}
}
}
println(count + " files listed");
}
if (path.startsWith("/repo/")) {
File dir = repoManager.getDir();
String str = path.substring("/repo/".length());
str = dir.getAbsolutePath() + "/" + str;
File file = new File(str);
if ("PUT".equalsIgnoreCase(method)) {
file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[4096];
int l;
while ((l = input.read(buf)) != -1) {
fos.write(buf, 0, l);
}
fos.close();
println(file.length() + " bytes writed");
}
if ("GET".equalsIgnoreCase(method)) {
if (file.isFile()) {
response.setContentLength((int) file.length());
response.setContentTypeBinary();
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[4096];
int l;
while ((l = input.read(buf)) != -1) {
response.write(buf, 0, l);
}
fis.close();
println(file.length() + " bytes read");
} else {
response.setStatusCode(404);
println("404 not found");
}
}
if ("DELETE".equalsIgnoreCase(method)) {
int c = delete(file);
boolean fail = file.exists();
println(c + " files deleted" + (fail ? " (fail)" : ""));
}
}
if ("PUT".equalsIgnoreCase(method)
&& ("/site".equals(path) || "/site.txt".equals(path))) {
byte[] buf = new byte[input.available()];
int l, n = 0;
while ((l = input.read(buf, n, buf.length - n)) != -1) {
n += l;
}
String str = new String(buf, 0, n, Charset.forName("UTF-8"));
try {
JSONObject json = new JSONObject(str);
site = new SiteSpec(json);
File file = new File(getFilesDir(), "repo");
new File(file, "lastUrl.txt").delete();
file = new File(file, "site.txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write(buf, 0, n);
fos.close();
println("site.txt is ready (" + site + ")");
} catch (Exception e) {
println("malformed site.txt");
println(e.toString());
}
}
if ("GET".equalsIgnoreCase(method)
&& (path.startsWith("/go/") || path.startsWith("/debug/"))) {
StringBuilder sb = new StringBuilder(
MyApplication.PRIMARY_SCHEME);
sb.append("://").append(
path.substring(path.indexOf('/', 1) + 1));
if (!TextUtils.isEmpty(query)) {
sb.append('?').append(query);
}
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(sb
.toString()));
if (site != null) {
i.putExtra("_site", site);
}
if (path.startsWith("/debug/")) {
new WaitForDebugger(i).start();
} else {
startActivity(i);
}
try {
File file = new File(getFilesDir(), "repo");
file = new File(file, "lastUrl.txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write(sb.toString().getBytes("UTF-8"));
fos.close();
} catch (Exception e) {
}
}
}
private int delete(File dir) {
if (dir.isFile()) {
dir.delete();
return 1;
}
File[] list = dir.listFiles();
if (list == null)
return 0;
int c = 0;
for (File f : list) {
c += delete(f);
}
return c;
}
}
private class WaitForDebugger extends Handler {
Intent intent;
AlertDialog dialog;
public WaitForDebugger(Intent i) {
super(Looper.getMainLooper());
intent = i;
}
public void start() {
sendEmptyMessage(1);
}
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
AlertDialog.Builder b = new AlertDialog.Builder(
DevLoaderActivity.this);
b.setTitle("Waiting For Debugger..");
b.setIcon(android.R.drawable.ic_dialog_alert);
b.setMessage(Html
.fromHtml("Go to eclipse, open DDMS perspective, debug <b>"
+ getPackageName() + "</b> in Devices panel"));
b.setNegativeButton("Skip",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int button) {
sendEmptyMessage(2);
}
});
b.setCancelable(false);
dialog = b.create();
dialog.show();
new Thread(looper, "wait-for-debugger").start();
}
if (msg.what == 2) {
if (dialog != null) {
dialog.dismiss();
dialog = null;
startActivity(intent);
}
}
}
private final Runnable looper = new Runnable() {
@Override
public void run() {
while (dialog != null && !Debug.isDebuggerConnected()) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
sendEmptyMessage(2);
}
};
}
}