0

Android で実行される単純な SIP クライアントを作成しようとしています。連絡を取るために SIPDemo プロジェクトをダウンロードしてから、コードの作成を開始しましたが、いくつかの問題が発生しています。

コードを示しましょう。

package com.example.gt_sipclient;

import java.text.ParseException;

import android.net.sip.SipAudioCall;
import android.net.sip.SipException;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
import android.net.sip.SipRegistrationListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

//Activity android simple pour effectuer quelques appels VoIP
//et réaliser des tests.
public class SIPCliMainActivity extends Activity {

private TextView numText, domText, usrText, pswText, callerIDText, sipText,
        statusText;
private EditText numInput, domInput, usrInput, pswInput, callerIDInput,
        sipInput;
private Button callBtn, registerBtn, unregisterBtn;
private String number, domain, username, password, callerID, sipPort;

public SipManager mySipManager = null;
public SipProfile mySipProfile = null;
public SipAudioCall call = null;
public IncomingCallReceiver callReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sipcli_main);

    // TODO: virer les éléments qui ne seront pas utilisés au final
    // Get the layout elements
    // TextViews
    numText = (TextView) findViewById(R.id.number_title);
    domText = (TextView) findViewById(R.id.domain);
    usrText = (TextView) findViewById(R.id.username);
    pswText = (TextView) findViewById(R.id.password);
    callerIDText = (TextView) findViewById(R.id.callerid);
    sipText = (TextView) findViewById(R.id.sipport);
    statusText = (TextView) findViewById(R.id.status);

    // EditText views
    numInput = (EditText) findViewById(R.id.number_input);
    domInput = (EditText) findViewById(R.id.domain_input);
    usrInput = (EditText) findViewById(R.id.username_input);
    pswInput = (EditText) findViewById(R.id.password_input);
    callerIDInput = (EditText) findViewById(R.id.callerid_input);
    sipInput = (EditText) findViewById(R.id.sipport_input);

    // Button views
    callBtn = (Button) findViewById(R.id.callbtn);
    registerBtn = (Button) findViewById(R.id.registerbtn);
    unregisterBtn = (Button) findViewById(R.id.unregisterbtn);

    // make sure that the screen will not turn off during the app lifecycle
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

}

// handle click events without implementing listeners (see layout file
// android:onclick)
// register handling
public void registerClick(View v) {

    // Get the SIP account parameters
    domain = domInput.getText().toString().trim();
    username = usrInput.getText().toString().trim();

    // TODO: penser à stocker le psw en MD5 et à le faire aussi dans le
    // sip.conf
    password = pswInput.getText().toString().trim();
    callerID = callerIDInput.getText().toString().trim();
    sipPort = sipInput.getText().toString().trim();

    sipManagerInit();

}

public void unregisterClick(View v) {

    myProfileClose();

}

// TODO: call handling
public void callClick(View v) {

}

public void sipManagerInit() {
    if (mySipManager == null)
        mySipManager = SipManager.newInstance(this);

    myProfileInit();
}

public void myProfileInit() {
    if (mySipManager == null)
        return;

    // TODO: test si un profil est déjà présent

    // TODO: Load persistent data if they exist
    // SharedPreferences myPreferences =
    // PreferenceManager.getDefaultSharedPreferences(getBaseContext());

    if (username == null || password == null || domain == null) {
        updateStatus("Please fill the form.");
        return;
    }

    try {
        SipProfile.Builder builder = new SipProfile.Builder(username,
                domain);
        builder.setPassword(password);
        // important
        //builder.setAutoRegistration(false);
        mySipProfile = builder.build();

        // open myProfile for making and receiving calls

        Intent i = new Intent();
        i.setAction("com.example.gt_sipclient.INCOMING_CALL");
        PendingIntent pi = PendingIntent.getBroadcast(this, 0, i,
                Intent.FILL_IN_DATA);
        mySipManager.open(mySipProfile, pi, null);

        //manual register
        //mySipManager.register(mySipProfile, 60, null);

        // set SIP registration events

        mySipManager.setRegistrationListener(mySipProfile.getUriString(),
                new SipRegistrationListener() {

                    @Override
                    public void onRegistrationFailed(String arg0, int arg1,
                            String arg2) {
                        updateStatus("Registration failed");

                    }

                    @Override
                    public void onRegistrationDone(String arg0, long arg1) {
                        updateStatus("Registration OK");
                        registerBtn.setVisibility(View.INVISIBLE);
                    }

                    @Override
                    public void onRegistering(String arg0) {
                        updateStatus("Registering with SIP server...");

                    }
                });

    } catch (ParseException e) {
        // TODO getMessage juste pour le debug
        updateStatus("Connection error." + e.getMessage());
    } catch (SipException e) {
        // TODO getMessage juste pour le debug
        updateStatus("Connection error SIP." + e.getMessage());
    }

}

public void myProfileClose() {
    if (mySipManager == null)
        return;

    try {
        if (mySipProfile != null) {
            mySipManager.close(mySipProfile.getUriString());
            unregisterBtn.setVisibility(View.VISIBLE);
        }
    } catch (Exception ee) {
        Log.d("WalkieTalkieActivity/onDestroy",
                "Failed to close local profile.", ee);
    }
}

//TODO: completer cette methode pour se desinscrire du serveur manuellement
public void myProfileUnregister(){
    if(mySipManager == null)
        return;

    try {
        if(mySipProfile != null && mySipManager.isRegistered(mySipProfile.getUriString())){
            mySipManager.unregister(mySipProfile, new SipRegistrationListener(){

                @Override
                public void onRegistering(String localProfileUri) {
                    // TODO Auto-generated method stub

                }

                @Override
                public void onRegistrationDone(String localProfileUri,
                        long expiryTime) {
                    // TODO Auto-generated method stub

                }

                @Override
                public void onRegistrationFailed(String localProfileUri,
                        int errorCode, String errorMessage) {
                    // TODO Auto-generated method stub

                }

            });
        }
    } catch (SipException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.sipcli_main, menu);
    return true;
}

//Allez on va coder un thread propre pour la maj
public void updateStatus(final String status) {


    this.runOnUiThread(new Runnable() {

        @Override
        public void run() {
            statusText.setText(status);

        }

    });

}

public void updateStatus(SipAudioCall call) {
    String useName = call.getPeerProfile().getDisplayName();
    // TODO: watch it
    if (useName == null)
        useName = call.getPeerProfile().getUserName();

    updateStatus(useName + "@" + call.getPeerProfile().getSipDomain());

}

    }

さて、アプリを実行しているときに、次のエラーが発生しました。

03-06 17:38:35.931: E/JavaBinder(3754): *** Uncaught remote exception!  (Exceptions are not yet supported across processes.)
03-06 17:38:35.931: E/JavaBinder(3754): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
03-06 17:38:35.931: E/JavaBinder(3754):     at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4039)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.view.ViewRootImpl.invalidateChild(ViewRootImpl.java:722)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:771)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.view.ViewGroup.invalidateChild(ViewGroup.java:4005)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.view.View.invalidate(View.java:8581)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.view.View.setFlags(View.java:6766)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.view.View.setVisibility(View.java:4617)
03-06 17:38:35.931: E/JavaBinder(3754):     at com.example.gt_sipclient.SIPCliMainActivity$1.onRegistrationDone(SIPCliMainActivity.java:163)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.net.sip.SipManager$ListenerRelay.onRegistrationDone(SipManager.java:601)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.net.sip.ISipSessionListener$Stub.onTransact(ISipSessionListener.java:167)
03-06 17:38:35.931: E/JavaBinder(3754):     at android.os.Binder.execTransact(Binder.java:338)
03-06 17:38:35.931: E/JavaBinder(3754):     at dalvik.system.NativeStart.run(Native Method)

問題は、渡された登録ステップごとに TextView を更新しようとしているということです。runOnUiThread を使用しても問題ないと思いましたが、明らかに何かが欠けています。登録について心配する必要はありません。うまく機能し (少し遅いですが...)、SIP サーバーでデバイスを確認できました。

私の 2 番目の質問は、自動登録を回避することです。ユーザーがボタンをクリックして SIP サーバーに自分自身を登録/登録解除できるようにしたいと思います (ゾイパー クライアントの場合のように)。それについての例は見つかりませんでした。しかし、あなたがいくつか持っているなら、私はこれらを読んでうれしいです. さらに情報が必要な場合は、教えてください。

4

1 に答える 1

0

registerBtn.setVisibility(View.INVISIBLE);また、内部に移動する必要がありますrunOnUiThreadonRegistrationDoneコードを次のように変更します。

 @Override
 public void onRegistrationDone(String arg0, long arg1) {
   updateStatus("Registration OK");  
  }
///..your code here....

public void updateStatus(final String status) {
    SIPCliMainActivity.this.runOnUiThread(new Runnable() {

        @Override
        public void run() {
            statusText.setText(status);
            registerBtn.setVisibility(View.INVISIBLE);
        }

    });
}
于 2013-03-06T17:09:55.440 に答える