Re #1546: apjloader updates: handle activity destroy-recreate, e.g: on orientation change or device sleep, and minors (cleaner activity handler, copy log to logcat, etc).

git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/android@4292 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Nanang Izzuddin 2012-11-01 06:14:15 +00:00
parent 2ff094467a
commit e01ef25ab9
2 changed files with 157 additions and 69 deletions

View File

@ -6,7 +6,7 @@
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
@ -15,32 +15,24 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.USE_SIP" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-feature android:name="android.hardware.sip.voip" android:required="true" />
<uses-feature android:name="android.hardware.microphone" android:required="true" />
<uses-feature
android:name="android.hardware.microphone"
android:required="true" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar" >
<activity
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation|screenLayout"
android:name=".MainActivity"
android:windowSoftInputMode="stateHidden"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -2,6 +2,8 @@
package org.pjsip.apjloader;
import java.lang.ref.WeakReference;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@ -9,6 +11,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
@ -21,7 +24,8 @@ class CONST {
public static final String LIB_FILENAME = "apjloader";
public static final String TAG = "apjloader";
public static final String NEWLINE = "\r\n";
public static final Boolean AUTOKILL_ON_FINISHED = true;
public static final Boolean AUTOKILL_ON_FINISH = false;
public static final int MAX_LOG_CHARS = 20000; // set to zero to disable limit
public enum MSG_TYPE {
STR_INFO,
STR_ERROR,
@ -41,10 +45,14 @@ class LOG {
}
class LoaderThread extends Thread {
private Handler ui_handler;
private WeakReference<Handler> ui_handler;
public LoaderThread(Handler ui_handler_) {
ui_handler = ui_handler_;
set_ui_handler(ui_handler_);
}
public void set_ui_handler(Handler ui_handler_) {
ui_handler = new WeakReference<Handler>(ui_handler_);
}
public void run() {
@ -55,27 +63,35 @@ class LoaderThread extends Thread {
String argv[] = {"",
"--clock-rate=8000",
"--auto-answer=200",
};
};
LOG.INFO(ui_handler, "Starting module.." + CONST.NEWLINE);
Handler ui = ui_handler.get();
LOG.INFO(ui, "Starting module.." + CONST.NEWLINE);
int rc = apjloader.main(argv.length, argv);
LOG.INFO(ui_handler, "Module finished with return code: " +
ui = ui_handler.get();
LOG.INFO(ui, "Module finished with return code: " +
Integer.toString(rc) + CONST.NEWLINE);
apjloader.destroy_stdio_pipe();
if (CONST.AUTOKILL_ON_FINISHED) {
Message msg = Message.obtain(ui_handler, CONST.MSG_TYPE.QUIT.ordinal());
ui_handler.sendMessageDelayed(msg, 2000);
if (CONST.AUTOKILL_ON_FINISH) {
ui = ui_handler.get();
Message msg = Message.obtain(ui, CONST.MSG_TYPE.QUIT.ordinal());
ui.sendMessageDelayed(msg, 2000);
}
}
}
class OutputThread extends Thread {
private Handler ui_handler;
private WeakReference<Handler> ui_handler;
public OutputThread(Handler ui_handler_) {
ui_handler = ui_handler_;
set_ui_handler(ui_handler_);
}
public void set_ui_handler(Handler ui_handler_) {
ui_handler = new WeakReference<Handler>(ui_handler_);
}
public void run() {
@ -86,18 +102,19 @@ class OutputThread extends Thread {
{
byte ch[] = new byte[1];
int rc = apjloader.read_from_stdout(ch);
Handler ui = ui_handler.get();
if (rc == 0) {
if (ch[0]=='\r' || ch[0]== '\n') {
if (sb.length() > 0) {
LOG.INFO(ui_handler, sb.toString()+CONST.NEWLINE);
LOG.INFO(ui, sb.toString()+CONST.NEWLINE);
sb.delete(0, sb.length());
}
} else {
sb.append((char)ch[0]);
}
} else {
LOG.INFO(ui_handler, "Stdout pipe stopped, rc="+Integer.toString(rc)+CONST.NEWLINE);
LOG.INFO(ui, "Stdout pipe stopped, rc="+Integer.toString(rc)+CONST.NEWLINE);
break;
}
}
@ -108,22 +125,109 @@ class OutputThread extends Thread {
public class MainActivity extends Activity {
private TextView log_view;
private ScrollView log_scroll_view;
EditText input_cmd;
/** Called when the activity is first created. */
private EditText input_cmd;
private MyHandler ui_handler = new MyHandler(this);
private static OutputThread ot;
private static LoaderThread lt;
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> mTarget;
public MyHandler(MainActivity target) {
mTarget = new WeakReference<MainActivity>(target);
}
@Override
public void handleMessage(Message m) {
MainActivity target = mTarget.get();
if (target == null)
return;
if (m.what == CONST.MSG_TYPE.STR_INFO.ordinal() ||
m.what == CONST.MSG_TYPE.STR_ERROR.ordinal())
{
target.print_log((String)m.obj);
} else if (m.what == CONST.MSG_TYPE.QUIT.ordinal()) {
target.finish();
System.gc();
android.os.Process.killProcess(android.os.Process.myPid());
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
protected void onCreate(Bundle savedInstanceState) {
Log.d(CONST.TAG, "=== Activity::onCreate() ===");
super.onCreate(savedInstanceState);
init_view();
init_view();
print_log("Loading module.." + CONST.NEWLINE);
int rc = init_lib();
if (rc != 0)
int rc = init_lib();
if (rc != 0) {
print_log("Failed loading module: " + Integer.toString(rc) + CONST.NEWLINE);
return;
}
ot.set_ui_handler(ui_handler);
lt.set_ui_handler(ui_handler);
}
@Override
protected void onStart() {
Log.d(CONST.TAG, "=== Activity::onStart() ===");
super.onStart();
}
@Override
protected void onRestart() {
Log.d(CONST.TAG, "=== Activity::onRestart() ===");
super.onRestart();
}
@Override
protected void onResume() {
Log.d(CONST.TAG, "=== Activity::onResume() ===");
super.onResume();
}
@Override
protected void onPause() {
Log.d(CONST.TAG, "=== Activity::onPause() ===");
super.onPause();
}
@Override
protected void onStop() {
Log.d(CONST.TAG, "=== Activity::onStop() ===");
super.onStop();
}
@Override
protected void onDestroy() {
Log.d(CONST.TAG, "=== Activity::onDestroy() ===");
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("INPUT_CMD", input_cmd.getText().toString());
outState.putString("LOG_VIEW", log_view.getText().toString());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
input_cmd.setText(savedInstanceState.getString("INPUT_CMD"));
log_view.setText(savedInstanceState.getString("LOG_VIEW"));
log_view.post(new Runnable() {
public void run() {
log_scroll_view.fullScroll(View.FOCUS_DOWN);
}
});
}
private View.OnClickListener on_send_cmd_click = new View.OnClickListener() {
public void onClick(View v) {
String cmd = input_cmd.getText().toString().trim();
@ -160,8 +264,17 @@ public class MainActivity extends Activity {
private void print_log(String st) {
log_view.append(st);
//Log.i(CONST.TAG, st);
Log.d(CONST.TAG, st);
// Limit log view text length
if (CONST.MAX_LOG_CHARS > 0 && log_view.length() > CONST.MAX_LOG_CHARS) {
int to_del = log_view.length() - CONST.MAX_LOG_CHARS + 100;
if (to_del > log_view.length())
to_del = log_view.length();
log_view.getEditableText().delete(0, to_del);
}
// Scroll to bottom
log_view.post(new Runnable() {
public void run() {
log_scroll_view.fullScroll(View.FOCUS_DOWN);
@ -169,28 +282,15 @@ public class MainActivity extends Activity {
});
}
private Handler ui_handler = new Handler() {
@Override
public void handleMessage(Message m) {
if (m.what == CONST.MSG_TYPE.STR_INFO.ordinal() ||
m.what == CONST.MSG_TYPE.STR_ERROR.ordinal())
{
print_log((String)m.obj);
} else if (m.what == CONST.MSG_TYPE.QUIT.ordinal()) {
finish();
System.gc();
android.os.Process.killProcess(android.os.Process.myPid());
}
}
};
private void hide_soft_keyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(input_cmd.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
private void init_view() {
Button send_cmd_button = (Button)findViewById(R.id.send_cmd_button);
setContentView(R.layout.activity_main);
Button send_cmd_button = (Button)findViewById(R.id.send_cmd_button);
send_cmd_button.setOnClickListener(on_send_cmd_click);
Button quit_button = (Button)findViewById(R.id.quit_button);
@ -198,23 +298,19 @@ public class MainActivity extends Activity {
input_cmd = (EditText)findViewById(R.id.input_cmd);
input_cmd.setOnKeyListener(on_input_cmd_key);
log_view = (TextView)findViewById(R.id.output);
log_view.setMovementMethod(new ScrollingMovementMethod());
log_view.setText("");
log_scroll_view = (ScrollView)findViewById(R.id.output_scroller);
}
private int init_stdio_pipes() {
int rc = apjloader.init_stdio_pipe();
if (rc != 0)
return rc;
return 0;
}
private int init_lib() {
if (ot != null || lt != null)
return 0;
print_log("Loading module.." + CONST.NEWLINE);
try {
System.loadLibrary(CONST.LIB_FILENAME);
} catch (UnsatisfiedLinkError e) {
@ -231,13 +327,13 @@ public class MainActivity extends Activity {
}
}
int rc = init_stdio_pipes();
int rc = apjloader.init_stdio_pipe();
print_log("Stdio pipes inited: " + Integer.toString(rc) + CONST.NEWLINE);
OutputThread ot = new OutputThread(ui_handler);
ot = new OutputThread(ui_handler);
ot.start();
LoaderThread lt = new LoaderThread(ui_handler);
lt = new LoaderThread(ui_handler);
lt.start();
return 0;