LinphoneActivity launcher (synchro service ready)

Due to a bug in tabhost on 1.5 devices (G1 and simulator)
it was impossible to have the synchronisation code directly
in LinphoneActivity. (NPE on touchmodechanged on mCurrentView).

Note that since the synchronisation code rewrite, no special care
is taken after a service crash. As a consequence you should always
check the root cause of a
"Caused by: java.lang.RuntimeException: Linphone Manager should be created before accessed"
This commit is contained in:
Guillaume Beraudo 2011-11-16 15:14:04 +01:00
parent c14c020053
commit ec3b407138
5 changed files with 133 additions and 130 deletions

View file

@ -8,8 +8,19 @@
<!-- Don't remove the space after android:debuggable: it prevents ndk-build to try including gdbserver and crash gcc -->
<application android:label="@string/app_name" android:debuggable ="true" android:icon="@drawable/logo_linphone_57x57">
<activity android:name="org.linphone.LinphoneActivity"
<activity android:name="org.linphone.LinphoneLauncherActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="org.linphone.LinphoneActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar"
android:launchMode="singleTask"
@ -17,7 +28,6 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
@ -86,12 +96,12 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name="ContactPickerActivityNew">
<activity android:name="ContactPickerActivityNew" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name="ContactPickerActivityOld">
<activity android:name="ContactPickerActivityOld" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>

View file

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ProgressBar android:layout_height="wrap_content" android:layout_width="wrap_content"/>
<TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@string/wait_dialog_text"/>
</LinearLayout>
<ImageView android:layout_width="fill_parent" android:layout_height="fill_parent"
android:src="@drawable/logo_linphone_57x57" android:scaleType="fitXY" />
<ProgressBar android:layout_height="wrap_content" android:layout_width="wrap_content" />
</FrameLayout>

View file

@ -35,7 +35,6 @@ import org.linphone.mediastream.Version;
import org.linphone.mediastream.video.AndroidVideoWindowImpl;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.TabActivity;
import android.content.Context;
import android.content.DialogInterface;
@ -56,7 +55,6 @@ import android.text.Html;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TabWidget;
import android.widget.TextView;
import android.widget.Toast;
@ -85,9 +83,6 @@ public class LinphoneActivity extends TabActivity implements
private Handler mHandler = new Handler();
private static final int waitDialogId = 1;
private ServiceWaitThread thread;
// Customization
private static boolean useFirstLoginActivity;
private static boolean useMenuSettings;
@ -104,15 +99,6 @@ public class LinphoneActivity extends TabActivity implements
throw new RuntimeException("LinphoneActivity not instantiated yet");
}
@Override
protected Dialog onCreateDialog(final int id) {
if (id == waitDialogId) {
View v = getLayoutInflater().inflate((R.layout.wait_service_dialog), null);
return new AlertDialog.Builder(this).setView(v).setCancelable(false).create();
}
return super.onCreateDialog(id);
}
public void onCreate(Bundle savedInstanceState) {
instance = this;
super.onCreate(savedInstanceState);
@ -127,15 +113,27 @@ public class LinphoneActivity extends TabActivity implements
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,Log.TAG+"#"+getClass().getName());
if (LinphoneService.isReady()) {
onCreateWhenServiceReady();
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
if (!useFirstLoginActivity || pref.getBoolean(getString(R.string.first_launch_suceeded_once_key), false)) {
fillTabHost();
} else {
// start linphone as background
startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class));
mDoOnCreateWhenServiceReady = true;
thread = new ServiceWaitThread();
thread.start();
startActivityForResult(new Intent().setClass(this, FirstLoginActivity.class), FIRST_LOGIN_ACTIVITY);
}
if (checkAccount && !useFirstLoginActivity) {
if (pref.getBoolean(PREF_FIRST_LAUNCH, true)) {
onFirstLaunch();
} else if (!pref.getBoolean(PREF_CHECK_CONFIG, false)
&& !checkDefined(pref, R.string.pref_username_key, R.string.pref_domain_key)) {
onBadSettings(pref);
} else {
checkAccount = false;
}
}
LinphoneManager.addListener(this);
}
@ -201,8 +199,8 @@ public class LinphoneActivity extends TabActivity implements
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.getData() == null) {
Log.e("LinphoneActivity received an intent without data, recreating GUI if needed");
if (!LinphoneService.isReady() || !LinphoneManager.getLc().isIncall()) return;
Log.i("LinphoneActivity received an intent without data, recreating GUI if needed");
if (!LinphoneManager.getLc().isIncall()) return;
if(LinphoneManager.getLc().isInComingInvitePending()) {
gotToDialer();
} else {
@ -242,7 +240,6 @@ public class LinphoneActivity extends TabActivity implements
@Override
protected void onPause() {
super.onPause();
mDoResumeWhenServiceReady = false;
if (isFinishing()) {
//restore audio settings
LinphoneManager.removeListener(this);
@ -573,36 +570,9 @@ public class LinphoneActivity extends TabActivity implements
}
private boolean mWaitDialogPosted;
@Override
protected void onResume() {
super.onResume();
if (LinphoneService.isReady()) {
onResumeWhenServiceReady();
} else {
if (!mWaitDialogPosted) {
mWaitDialogPosted = true;
mHandler.postDelayed(new Runnable() {
// Delay to avoid flicker.
// Call in onResume to make sure the view (especially the tabhost) is initialized.
@Override
public void run() {
if (!LinphoneService.isReady()) {
showDialog(waitDialogId);
}
}
}, 2000);
}
mDoResumeWhenServiceReady = true;
if (thread == null) {
thread = new ServiceWaitThread();
thread.start();
}
}
}
private void onResumeWhenServiceReady() {
LinphoneCall pendingCall = LinphoneManager.getInstance().getPendingIncomingCall();
if (pendingCall != null) {
LinphoneActivity.instance().startIncomingCallActivity(pendingCall);
@ -615,72 +585,6 @@ public class LinphoneActivity extends TabActivity implements
// removing is done directly in LinphoneActivity.onPause()
}
}
private boolean mDoOnCreateWhenServiceReady;
private void onCreateWhenServiceReady() {
mDoOnCreateWhenServiceReady = false;
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
if (!useFirstLoginActivity || pref.getBoolean(getString(R.string.first_launch_suceeded_once_key), false)) {
fillTabHost();
} else {
startActivityForResult(new Intent().setClass(this, FirstLoginActivity.class), FIRST_LOGIN_ACTIVITY);
}
if (checkAccount && !useFirstLoginActivity) {
if (pref.getBoolean(PREF_FIRST_LAUNCH, true)) {
onFirstLaunch();
} else if (!pref.getBoolean(PREF_CHECK_CONFIG, false)
&& !checkDefined(pref, R.string.pref_username_key, R.string.pref_domain_key)) {
onBadSettings(pref);
} else {
checkAccount = false;
}
}
LinphoneManager.addListener(this);
}
private boolean mDoResumeWhenServiceReady;
private class ServiceWaitThread extends Thread {
public void run() {
while (!LinphoneService.isReady()) {
try {
sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException("waiting thread sleep() has been interrupted");
}
}
mHandler.post(new Runnable() {
@Override
public void run() {
try {
dismissDialog(waitDialogId);
} catch (Throwable e) {
// Discarding exception which may be thrown if the dialog wasn't showing.
}
}
});
mHandler.post(new Runnable() {
@Override
public void run() {
if (mDoOnCreateWhenServiceReady) {
onCreateWhenServiceReady();
}
if (mDoResumeWhenServiceReady) {
onResumeWhenServiceReady();
}
}
});
thread = null;
}
}
}
interface ContactPicked {

View file

@ -0,0 +1,88 @@
/*
LinphoneLauncher.java
Copyright (C) 2011 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.linphone;
import static android.content.Intent.ACTION_MAIN;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
/**
*
* Launch Linphone main activity when Service is ready.
*
* @author Guillaume Beraudo
*
*/
public class LinphoneLauncherActivity extends Activity {
private Handler mHandler;
private ServiceWaitThread mThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.launcher);
mHandler = new Handler();
if (LinphoneService.isReady()) {
onServiceReady();
} else {
// start linphone as background
startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class));
mThread = new ServiceWaitThread();
mThread.start();
}
}
private void onServiceReady() {
startActivity(new Intent()
.setClass(this, LinphoneActivity.class)
.setData(getIntent().getData()));
finish();
}
private class ServiceWaitThread extends Thread {
public void run() {
while (!LinphoneService.isReady()) {
try {
sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException("waiting thread sleep() has been interrupted");
}
}
mHandler.post(new Runnable() {
@Override
public void run() {
onServiceReady();
}
});
mThread = null;
}
}
}

View file

@ -47,7 +47,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
/***
/**
*
* Linphone service, reacting to Incoming calls, ...<br />
*
@ -69,11 +69,11 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
private Handler mHandler = new Handler();
private static LinphoneService instance;
// private static boolean mTestDelayElapsed; // add a timer for testing
private static boolean mTestDelayElapsed = true; // no timer
// private boolean mTestDelayElapsed; // add a timer for testing
private boolean mTestDelayElapsed = true; // no timer
public static boolean isReady() {
return mTestDelayElapsed && instance!=null;
return instance!=null && instance.mTestDelayElapsed;
}
/**