diff options
Diffstat (limited to 'pjsip-apps/src/swig/java')
34 files changed, 1826 insertions, 0 deletions
diff --git a/pjsip-apps/src/swig/java/Makefile b/pjsip-apps/src/swig/java/Makefile new file mode 100644 index 00000000..1946891f --- /dev/null +++ b/pjsip-apps/src/swig/java/Makefile @@ -0,0 +1,124 @@ +include ../../../../build.mak + +ifneq ($(findstring android,$(TARGET_NAME)),) + OS=android +else + ifneq ($(findstring darwin,$(TARGET_NAME)),) + OS=darwin + endif +endif + +OUT_DIR=output +ifeq ($(OS),Windows_NT) + LIBPJSUA2_SO=$(OUT_DIR)/pjsua2.dll +else + ifeq ($(OS),darwin) + LIBPJSUA2_SO=$(OUT_DIR)/libpjsua2.jnilib + else + ifeq ($(OS),android) + LIBPJSUA2_SO=android/libs/armeabi/libpjsua2.so + else + LIBPJSUA2_SO=$(OUT_DIR)/libpjsua2.so + endif + endif +endif + +# Get JDK location +ifeq ("$(JAVA_HOME)","") + # Get javac location to determine JDK location + JAVAC_PATH = $(shell which javac) + ifeq ("$(JAVAC_PATH)","") + $(error Cannot determine JDK location using 'which' command. Please define JAVA_HOME envvar) + endif + + JAVAC_PATH := $(realpath $(JAVAC_PATH)) + JAVA_BIN := $(dir $(JAVAC_PATH)) + JAVA_HOME := $(patsubst %/bin/,%,$(JAVA_BIN)) +else + ifeq (exists, $(shell test -d $(JAVA_HOME)/bin && echo exists )) + JAVA_BIN := $(JAVA_HOME)/bin + else + JAVA_BIN := $(JAVA_HOME) + endif +endif + +# OS specific +ifeq ($(OS),Windows_NT) + MY_JNI_LDFLAGS = -L$(MY_JDK)/lib -Wl,--kill-at +else + MY_JNI_CFLAGS = -fPIC + MY_JNI_LDFLAGS = -L$(MY_JDK)/lib + ifeq ($(OS),darwin) + MY_JNI_LDFLAGS := $(MY_JNI_LDFLAGS) -Wl,-soname,pjsua2.so + endif + ifeq ($(OS),android) + MY_JNI_CFLAGS := $(MY_JNI_CFLAGS) -D__ANDROID__ + endif +endif + +# Env settings, e.g: path to SWIG, JDK, java(.exe), javac(.exe) +MY_SWIG = swig +MY_JDK = $(JAVA_HOME) +ifneq ($(findstring bin,$(JAVA_BIN)),) + MY_JAVA = $(MY_JDK)/bin/java + MY_JAVAC = $(MY_JDK)/bin/javac +else + MY_JAVA = $(MY_JDK)/java + MY_JAVAC = $(MY_JDK)/javac +endif +MY_JNI_CFLAGS := $(MY_JNI_CFLAGS) -I$(MY_JDK)/include -I$(MY_JDK)/include/win32 \ + -I$(MY_JDK)/include/linux -I. + +# Build settings +MY_CFLAGS = $(PJ_CFLAGS) $(MY_JNI_CFLAGS) +MY_LDFLAGS = $(PJ_LDFLAGS) -lpjsua2-$(TARGET_NAME) $(PJ_LDLIBS) $(MY_JNI_LDFLAGS) +MY_PACKAGE_NAME = org.pjsip.pjsua2 +ifeq ($(OS),android) + MY_PACKAGE_PATH = android/src/$(subst .,/,$(MY_PACKAGE_NAME)) +else + MY_PACKAGE_PATH = $(OUT_DIR)/$(subst .,/,$(MY_PACKAGE_NAME)) +endif + +MY_APP_JAVA = android/src/$(subst .,/,$(MY_PACKAGE_NAME))/app/MyApp.java + +.PHONY: all java install uninstall + +all: $(LIBPJSUA2_SO) java + +$(LIBPJSUA2_SO): $(OUT_DIR)/pjsua2_wrap.o + $(PJ_CXX) -shared -o $(LIBPJSUA2_SO) $(OUT_DIR)/pjsua2_wrap.o $(MY_CFLAGS) $(MY_LDFLAGS) + +$(OUT_DIR)/pjsua2_wrap.o: $(OUT_DIR)/pjsua2_wrap.cpp Makefile + $(PJ_CXX) -c $(OUT_DIR)/pjsua2_wrap.cpp -o $(OUT_DIR)/pjsua2_wrap.o $(MY_CFLAGS) $(MY_LDFLAGS) + +$(OUT_DIR)/pjsua2_wrap.cpp: ../pjsua2.i ../symbols.i $(SRCS) + mkdir -p $(MY_PACKAGE_PATH) + swig $(SWIG_FLAGS) -java -package $(MY_PACKAGE_NAME) -outdir $(MY_PACKAGE_PATH) -o $(OUT_DIR)/pjsua2_wrap.cpp ../pjsua2.i + +clean distclean realclean: + rm -rf $(LIBPJSUA2_SO) $(OUT_DIR)/* $(MY_PACKAGE_PATH)/*.java $(MY_PACKAGE_PATH)/*.class + +java: $(MY_PACKAGE_PATH)/Error.class $(MY_PACKAGE_PATH)/test.class $(MY_PACKAGE_PATH)/sample.class + +$(MY_PACKAGE_PATH)/Error.class: $(MY_PACKAGE_PATH)/Error.java + $(MY_JAVAC) -d $(OUT_DIR) $(MY_PACKAGE_PATH)/*.java $(MY_APP_JAVA) + +$(MY_PACKAGE_PATH)/test.class: test.java + $(MY_JAVAC) -d $(OUT_DIR) -classpath "$(OUT_DIR)" test.java + +$(MY_PACKAGE_PATH)/sample.class: sample.java + $(MY_JAVAC) -d $(OUT_DIR) -classpath "$(OUT_DIR)" sample.java + +test: + @# Need to specify classpath and library path, alternatively, they can be set via + @# CLASSPATH and java.library.path env settings + $(MY_JAVA) -cp "$(OUT_DIR)" -Djava.library.path="$(OUT_DIR)" test + +sample: + @# Need to specify classpath and library path, alternatively, they can be set via + @# CLASSPATH and java.library.path env settings + $(MY_JAVA) -cp "$(OUT_DIR)" -Djava.library.path="$(OUT_DIR)" org.pjsip.pjsua2.app.sample + +install: +uninstall: + diff --git a/pjsip-apps/src/swig/java/android/.classpath b/pjsip-apps/src/swig/java/android/.classpath new file mode 100644 index 00000000..b76ec6cd --- /dev/null +++ b/pjsip-apps/src/swig/java/android/.classpath @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/pjsip-apps/src/swig/java/android/.project b/pjsip-apps/src/swig/java/android/.project new file mode 100644 index 00000000..434a3408 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/.project @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>Pjsua2</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/pjsip-apps/src/swig/java/android/.settings/org.eclipse.jdt.core.prefs b/pjsip-apps/src/swig/java/android/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..48ab4c6b --- /dev/null +++ b/pjsip-apps/src/swig/java/android/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/pjsip-apps/src/swig/java/android/AndroidManifest.xml b/pjsip-apps/src/swig/java/android/AndroidManifest.xml new file mode 100644 index 00000000..5a9b0aef --- /dev/null +++ b/pjsip-apps/src/swig/java/android/AndroidManifest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.pjsip.pjsua2.app" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="11" + 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" /> + <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + <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.WAKE_LOCK" /> + <uses-permission android:name="android.permission.VIBRATE" /> + <uses-permission android:name="android.permission.READ_LOGS" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name="org.pjsip.pjsua2.app.MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name="org.pjsip.pjsua2.app.CallActivity" + android:label="@string/title_activity_call" > + </activity> + </application> + +</manifest> diff --git a/pjsip-apps/src/swig/java/android/ic_launcher-web.png b/pjsip-apps/src/swig/java/android/ic_launcher-web.png Binary files differnew file mode 100644 index 00000000..a18cbb48 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/ic_launcher-web.png diff --git a/pjsip-apps/src/swig/java/android/jni/Android.mk b/pjsip-apps/src/swig/java/android/jni/Android.mk new file mode 100644 index 00000000..94912baf --- /dev/null +++ b/pjsip-apps/src/swig/java/android/jni/Android.mk @@ -0,0 +1,12 @@ +include ../../../../../build.mak + +LOCAL_PATH := $(PJDIR)/pjsip-apps/src/swig/java/android +include $(CLEAR_VARS) + +LOCAL_MODULE := libpjsua2 +LOCAL_CFLAGS := $(APP_CFLAGS) -frtti -fexceptions +LOCAL_LDFLAGS := $(APP_LDFLAGS) +LOCAL_LDLIBS := $(APP_LDLIBS) +LOCAL_SRC_FILES := ../output/pjsua2_wrap.cpp + +include $(BUILD_SHARED_LIBRARY) diff --git a/pjsip-apps/src/swig/java/android/jni/Application.mk b/pjsip-apps/src/swig/java/android/jni/Application.mk new file mode 100644 index 00000000..87124dd8 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/jni/Application.mk @@ -0,0 +1 @@ +APP_STL := gnustl_static diff --git a/pjsip-apps/src/swig/java/android/proguard-project.txt b/pjsip-apps/src/swig/java/android/proguard-project.txt new file mode 100644 index 00000000..f2fe1559 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/pjsip-apps/src/swig/java/android/project.properties b/pjsip-apps/src/swig/java/android/project.properties new file mode 100644 index 00000000..0840b4a0 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-15 diff --git a/pjsip-apps/src/swig/java/android/res/drawable-hdpi/ic_launcher.png b/pjsip-apps/src/swig/java/android/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..288b6655 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/drawable-hdpi/ic_launcher.png diff --git a/pjsip-apps/src/swig/java/android/res/drawable-mdpi/ic_launcher.png b/pjsip-apps/src/swig/java/android/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..6ae570b4 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/drawable-mdpi/ic_launcher.png diff --git a/pjsip-apps/src/swig/java/android/res/drawable-xhdpi/ic_launcher.png b/pjsip-apps/src/swig/java/android/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..d4fb7cd9 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/drawable-xhdpi/ic_launcher.png diff --git a/pjsip-apps/src/swig/java/android/res/drawable-xxhdpi/ic_launcher.png b/pjsip-apps/src/swig/java/android/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..85a60815 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/drawable-xxhdpi/ic_launcher.png diff --git a/pjsip-apps/src/swig/java/android/res/drawable/bkg.xml b/pjsip-apps/src/swig/java/android/res/drawable/bkg.xml new file mode 100644 index 00000000..f5052332 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/drawable/bkg.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8" ?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@color/pressed_color" />
+ <item android:drawable="@color/default_color" />
+</selector>
\ No newline at end of file diff --git a/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml b/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml new file mode 100644 index 00000000..3745eb39 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/textViewPeer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="Peer URI"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/textViewCallState"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="Call state" />
+
+ <Button
+ android:id="@+id/buttonAccept"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:onClick="acceptCall"
+ android:text="Accept" />
+
+ <Button
+ android:id="@+id/buttonHangup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:onClick="hangupCall"
+ android:text="Reject" />
+
+</LinearLayout>
\ No newline at end of file diff --git a/pjsip-apps/src/swig/java/android/res/layout/activity_main.xml b/pjsip-apps/src/swig/java/android/res/layout/activity_main.xml new file mode 100644 index 00000000..c63c0210 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/layout/activity_main.xml @@ -0,0 +1,65 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity" >
+
+ <ListView
+ android:id="@+id/listViewBuddy"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:listSelector="@drawable/bkg" >
+ </ListView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <ImageButton
+ android:id="@+id/buttonCall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:onClick="makeCall"
+ android:src="@android:drawable/ic_menu_call" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:text=" "/>
+
+ <ImageButton
+ android:id="@+id/buttonAddBuddy"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:onClick="addBuddy"
+ android:src="@android:drawable/ic_menu_add" />
+
+ <ImageButton
+ android:id="@+id/buttonEditBuddy"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:onClick="editBuddy"
+ android:src="@android:drawable/ic_menu_edit" />
+
+ <ImageButton
+ android:id="@+id/buttonDelBuddy"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:onClick="delBuddy"
+ android:src="@android:drawable/ic_menu_delete" />
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file diff --git a/pjsip-apps/src/swig/java/android/res/layout/dlg_account_config.xml b/pjsip-apps/src/swig/java/android/res/layout/dlg_account_config.xml new file mode 100644 index 00000000..71111f14 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/layout/dlg_account_config.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?>
+<TableLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:padding = "20dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/textViewInfo"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:paddingBottom="20dp"
+ android:textColor="#b0b0b0" >
+ </TextView>
+
+ <TableRow>
+ <TextView android:text="ID">
+ </TextView>
+
+ <EditText
+ android:id="@+id/editTextId"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textUri" >
+
+ <requestFocus />
+ </EditText>
+ </TableRow>
+ <TableRow>
+ <TextView android:text="Registrar">
+ </TextView>
+ <EditText
+ android:id="@+id/editTextRegistrar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textUri" >
+ </EditText>
+ </TableRow>
+ <TableRow>
+ <TextView android:text="Proxy">
+ </TextView>
+ <EditText
+ android:id="@+id/editTextProxy"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textUri" >
+ </EditText>
+ </TableRow>
+ <TableRow>
+ <TextView android:text="Username">
+ </TextView>
+
+ <EditText
+ android:id="@+id/editTextUsername"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="text" >
+
+ </EditText>
+ </TableRow>
+ <TableRow>
+ <TextView android:text="Password">
+ </TextView>
+
+ <EditText
+ android:id="@+id/editTextPassword"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textPassword" >
+
+ </EditText>
+ </TableRow>
+</TableLayout>
diff --git a/pjsip-apps/src/swig/java/android/res/layout/dlg_add_buddy.xml b/pjsip-apps/src/swig/java/android/res/layout/dlg_add_buddy.xml new file mode 100644 index 00000000..f13e7005 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/layout/dlg_add_buddy.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?>
+<TableLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:padding = "20dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TableRow>
+ <TextView android:text="Buddy URI">
+ </TextView>
+
+ <EditText
+ android:id="@+id/editTextUri"
+ android:layout_weight="1"
+ android:inputType="textUri" >
+ <requestFocus />
+ </EditText>
+ </TableRow>
+ <TableRow>
+ <CheckBox
+ android:id="@+id/checkBoxSubscribe"
+ android:layout_column="1"
+ android:text="Subscribe presence" />
+ </TableRow>
+</TableLayout>
diff --git a/pjsip-apps/src/swig/java/android/res/menu/call.xml b/pjsip-apps/src/swig/java/android/res/menu/call.xml new file mode 100644 index 00000000..d122a4b7 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/menu/call.xml @@ -0,0 +1,9 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"
+ android:title="@string/action_settings"/>
+
+</menu>
diff --git a/pjsip-apps/src/swig/java/android/res/menu/main.xml b/pjsip-apps/src/swig/java/android/res/menu/main.xml new file mode 100644 index 00000000..be94829a --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/menu/main.xml @@ -0,0 +1,14 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/action_acc_config"
+ android:icon="@android:drawable/ic_menu_manage"
+ android:showAsAction="ifRoom"
+ android:title="Account Config"/>
+ <item
+ android:id="@+id/action_quit"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel"
+ android:showAsAction="ifRoom"
+ android:title="Quit"/>
+
+</menu>
diff --git a/pjsip-apps/src/swig/java/android/res/values-sw600dp/dimens.xml b/pjsip-apps/src/swig/java/android/res/values-sw600dp/dimens.xml new file mode 100644 index 00000000..c876987e --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/values-sw600dp/dimens.xml @@ -0,0 +1,8 @@ +<resources>
+
+ <!--
+ Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw600dp devices (e.g. 7" tablets) here.
+ -->
+
+</resources>
diff --git a/pjsip-apps/src/swig/java/android/res/values-sw720dp-land/dimens.xml b/pjsip-apps/src/swig/java/android/res/values-sw720dp-land/dimens.xml new file mode 100644 index 00000000..0df30679 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,9 @@ +<resources>
+
+ <!--
+ Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
+ -->
+ <dimen name="activity_horizontal_margin">128dp</dimen>
+
+</resources>
diff --git a/pjsip-apps/src/swig/java/android/res/values-v11/styles.xml b/pjsip-apps/src/swig/java/android/res/values-v11/styles.xml new file mode 100644 index 00000000..e3ef53d9 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/values-v11/styles.xml @@ -0,0 +1,11 @@ +<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces + AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/pjsip-apps/src/swig/java/android/res/values-v14/styles.xml b/pjsip-apps/src/swig/java/android/res/values-v14/styles.xml new file mode 100644 index 00000000..94dd245c --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/values-v14/styles.xml @@ -0,0 +1,12 @@ +<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces + AppBaseTheme from BOTH res/values/styles.xml and + res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/pjsip-apps/src/swig/java/android/res/values/colors.xml b/pjsip-apps/src/swig/java/android/res/values/colors.xml new file mode 100644 index 00000000..9ce61cf2 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/values/colors.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="pressed_color">#B8F2F5</color>
+ <color name="default_color">#E8FEFF</color>
+</resources>
\ No newline at end of file diff --git a/pjsip-apps/src/swig/java/android/res/values/dimens.xml b/pjsip-apps/src/swig/java/android/res/values/dimens.xml new file mode 100644 index 00000000..2e0e2ae4 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/values/dimens.xml @@ -0,0 +1,7 @@ +<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/pjsip-apps/src/swig/java/android/res/values/strings.xml b/pjsip-apps/src/swig/java/android/res/values/strings.xml new file mode 100644 index 00000000..2ee52b69 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/values/strings.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">Pjsua2</string>
+ <string name="action_settings">Settings</string>
+ <string name="title_activity_call">Call</string>
+ <string name="hello_world">Hello world!</string>
+
+</resources>
diff --git a/pjsip-apps/src/swig/java/android/res/values/styles.xml b/pjsip-apps/src/swig/java/android/res/values/styles.xml new file mode 100644 index 00000000..4ea93266 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/res/values/styles.xml @@ -0,0 +1,20 @@ +<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java new file mode 100644 index 00000000..48e1bad3 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java @@ -0,0 +1,146 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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.pjsip.pjsua2.app; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.app.Activity; + +import org.pjsip.pjsua2.*; + +public class CallActivity extends Activity implements Handler.Callback { + + public static Handler handler_; + + private final Handler handler = new Handler(this); + private static CallInfo lastCallInfo; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_call); + + handler_ = handler; + if (MainActivity.currentCall != null) { + try { + lastCallInfo = MainActivity.currentCall.getInfo(); + updateCallState(lastCallInfo); + } catch (Exception e) { + System.out.println(e); + } + } else { + updateCallState(lastCallInfo); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + handler_ = null; + } + + public void acceptCall(View view) { + CallOpParam prm = new CallOpParam(); + prm.setStatusCode(pjsip_status_code.PJSIP_SC_OK); + try { + MainActivity.currentCall.answer(prm); + } catch (Exception e) { + System.out.println(e); + } + + view.setVisibility(View.GONE); + } + + public void hangupCall(View view) { + handler_ = null; + finish(); + + if (MainActivity.currentCall != null) { + CallOpParam prm = new CallOpParam(); + prm.setStatusCode(pjsip_status_code.PJSIP_SC_DECLINE); + try { + MainActivity.currentCall.hangup(prm); + } catch (Exception e) { + System.out.println(e); + } + + MainActivity.currentCall = null; + } + } + + @Override + public boolean handleMessage(Message m) { + + if (m.what == MainActivity.MSG_TYPE.CALL_STATE) { + + lastCallInfo = (CallInfo) m.obj; + updateCallState(lastCallInfo); + + } else { + + /* Message not handled */ + return false; + + } + + return true; + } + + private void updateCallState(CallInfo ci) { + TextView tvPeer = (TextView) findViewById(R.id.textViewPeer); + TextView tvState = (TextView) findViewById(R.id.textViewCallState); + Button buttonHangup = (Button) findViewById(R.id.buttonHangup); + Button buttonAccept = (Button) findViewById(R.id.buttonAccept); + String call_state = ""; + + if (ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAC) { + buttonAccept.setVisibility(View.GONE); + } + + if (ci.getState().swigValue() < pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED.swigValue()) + { + if (ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAS) { + call_state = "Incoming call.."; + /* Default button texts are already 'Accept' & 'Reject' */ + } else { + buttonHangup.setText("Cancel"); + call_state = ci.getStateText(); + } + } + else if (ci.getState().swigValue() >= pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED.swigValue()) + { + buttonAccept.setVisibility(View.GONE); + call_state = ci.getStateText(); + if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) { + buttonHangup.setText("Hangup"); + } else if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) { + buttonHangup.setText("OK"); + call_state = "Call disconnected: " + ci.getLastReason(); + MainActivity.currentCall = null; + } + } + + tvPeer.setText(ci.getRemoteUri()); + tvState.setText(call_state); + } +} diff --git a/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java new file mode 100644 index 00000000..34633f97 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java @@ -0,0 +1,497 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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.pjsip.pjsua2.app; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.SimpleAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.pjsip.pjsua2.*; + +public class MainActivity extends Activity implements Handler.Callback, MyAppObserver { + public static MyApp app = null; + public static MyCall currentCall = null; + public static MyAccount account = null; + public static AccountConfig accCfg = null; + + private ListView buddyListView; + private SimpleAdapter buddyListAdapter; + private int buddyListSelectedIdx = -1; + ArrayList<Map<String, String>> buddyList; + private String lastRegStatus = ""; + + private final Handler handler = new Handler(this); + public class MSG_TYPE { + public final static int INCOMING_CALL = 1; + public final static int CALL_STATE = 2; + public final static int REG_STATE = 3; + public final static int BUDDY_STATE = 4; + } + + private HashMap<String, String> putData(String uri, String status) { + HashMap<String, String> item = new HashMap<String, String>(); + item.put("uri", uri); + item.put("status", status); + return item; + } + + private void showCallActivity() { + Intent intent = new Intent(this, CallActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (app == null) { + app = new MyApp(); + /* Wait for GDB to init */ + if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) {} + } + + app.init(this, getFilesDir().getAbsolutePath()); + } + + if (app.accList.size() == 0) { + accCfg = new AccountConfig(); + accCfg.setIdUri("sip:localhost"); + account = app.addAcc(accCfg); + } else { + account = app.accList.get(0); + accCfg = account.cfg; + } + + buddyList = new ArrayList<Map<String, String>>(); + for (int i = 0; i < account.buddyList.size(); i++) { + buddyList.add(putData(account.buddyList.get(i).cfg.getUri(), + account.buddyList.get(i).getStatusText())); + } + + String[] from = { "uri", "status" }; + int[] to = { android.R.id.text1, android.R.id.text2 }; + buddyListAdapter = new SimpleAdapter(this, buddyList, android.R.layout.simple_list_item_2, from, to); + + buddyListView = (ListView) findViewById(R.id.listViewBuddy);; + buddyListView.setAdapter(buddyListAdapter); + buddyListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, final View view, + int position, long id) + { + view.setSelected(true); + buddyListSelectedIdx = position; + } + }); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_acc_config: + dlgAccountSetting(); + break; + + case R.id.action_quit: + Message m = Message.obtain(handler, 0); + m.sendToTarget(); + break; + + default: + break; + } + + return true; + } + + @Override + public boolean handleMessage(Message m) { + + if (m.what == 0) { + + app.deinit(); + finish(); + Runtime.getRuntime().gc(); + android.os.Process.killProcess(android.os.Process.myPid()); + + } else if (m.what == MSG_TYPE.CALL_STATE) { + + CallInfo ci = (CallInfo) m.obj; + + /* Forward the message to CallActivity */ + if (CallActivity.handler_ != null) { + Message m2 = Message.obtain(CallActivity.handler_, MSG_TYPE.CALL_STATE, ci); + m2.sendToTarget(); + } + + if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) + currentCall = null; + + } else if (m.what == MSG_TYPE.BUDDY_STATE) { + + MyBuddy buddy = (MyBuddy) m.obj; + int idx = account.buddyList.indexOf(buddy); + if (idx >= 0) { + buddyList.get(idx).put("status", buddy.getStatusText()); + buddyListAdapter.notifyDataSetChanged(); + // TODO: selection color/mark is gone after this, + // dont know how to return it back. + //buddyListView.setSelection(buddyListSelectedIdx); + //buddyListView.performItemClick(buddyListView, buddyListSelectedIdx, + // buddyListView.getItemIdAtPosition(buddyListSelectedIdx)); + + /* Return back Call activity */ + notifyCallState(currentCall); + } + + } else if (m.what == MSG_TYPE.REG_STATE) { + + String msg_str = (String) m.obj; + lastRegStatus = msg_str; + + } else if (m.what == MSG_TYPE.INCOMING_CALL) { + + /* Incoming call */ + final MyCall call = (MyCall) m.obj; + CallOpParam prm = new CallOpParam(); + + /* Only one call at anytime */ + if (currentCall != null) { + prm.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE); + try { + call.hangup(prm); + } catch (Exception e) {} + return true; + } + + /* Answer with ringing */ + prm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING); + try { + call.answer(prm); + } catch (Exception e) {} + + currentCall = call; + showCallActivity(); + + } else { + + /* Message not handled */ + return false; + + } + + return true; + } + + + private void dlgAccountSetting() { + + LayoutInflater li = LayoutInflater.from(this); + View view = li.inflate(R.layout.dlg_account_config, null); + + if (!lastRegStatus.isEmpty()) { + TextView tvInfo = (TextView)view.findViewById(R.id.textViewInfo); + tvInfo.setText("Last status: " + lastRegStatus); + } + + AlertDialog.Builder adb = new AlertDialog.Builder(this); + adb.setView(view); + adb.setTitle("Account Settings"); + + final EditText etId = (EditText)view.findViewById(R.id.editTextId); + final EditText etReg = (EditText)view.findViewById(R.id.editTextRegistrar); + final EditText etProxy = (EditText)view.findViewById(R.id.editTextProxy); + final EditText etUser = (EditText)view.findViewById(R.id.editTextUsername); + final EditText etPass = (EditText)view.findViewById(R.id.editTextPassword); + + etId. setText(accCfg.getIdUri()); + etReg. setText(accCfg.getRegConfig().getRegistrarUri()); + StringVector proxies = accCfg.getSipConfig().getProxies(); + if (proxies.size() > 0) + etProxy.setText(proxies.get(0)); + else + etProxy.setText(""); + AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds(); + if (creds.size() > 0) { + etUser. setText(creds.get(0).getUsername()); + etPass. setText(creds.get(0).getData()); + } else { + etUser. setText(""); + etPass. setText(""); + } + + adb.setCancelable(false); + adb.setPositiveButton("OK", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog,int id) { + String acc_id = etId.getText().toString(); + String registrar = etReg.getText().toString(); + String proxy = etProxy.getText().toString(); + String username = etUser.getText().toString(); + String password = etPass.getText().toString(); + + accCfg.setIdUri(acc_id); + accCfg.getRegConfig().setRegistrarUri(registrar); + AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds(); + creds.clear(); + if (!username.isEmpty()) { + creds.add(new AuthCredInfo("Digest", "*", username, 0, password)); + } + StringVector proxies = accCfg.getSipConfig().getProxies(); + proxies.clear(); + if (!proxy.isEmpty()) { + proxies.add(proxy); + } + + /* Finally */ + lastRegStatus = ""; + try { + account.modify(accCfg); + } catch (Exception e) {} + } + }); + adb.setNegativeButton("Cancel", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog,int id) { + dialog.cancel(); + } + }); + + AlertDialog ad = adb.create(); + ad.show(); + } + + + public void makeCall(View view) { + if (buddyListSelectedIdx == -1) + return; + + /* Only one call at anytime */ + if (currentCall != null) { + return; + } + + HashMap<String, String> item = (HashMap<String, String>) buddyListView.getItemAtPosition(buddyListSelectedIdx); + String buddy_uri = item.get("uri"); + + MyCall call = new MyCall(account, -1); + CallOpParam prm = new CallOpParam(); + CallSetting opt = prm.getOpt(); + opt.setAudioCount(1); + opt.setVideoCount(0); + + try { + call.makeCall(buddy_uri, prm); + } catch (Exception e) { + currentCall = null; + return; + } + + currentCall = call; + showCallActivity(); + } + + private void dlgAddEditBuddy(BuddyConfig initial) { + final BuddyConfig cfg = new BuddyConfig(); + final BuddyConfig old_cfg = initial; + final boolean is_add = initial == null; + + LayoutInflater li = LayoutInflater.from(this); + View view = li.inflate(R.layout.dlg_add_buddy, null); + + AlertDialog.Builder adb = new AlertDialog.Builder(this); + adb.setView(view); + + final EditText etUri = (EditText)view.findViewById(R.id.editTextUri); + final CheckBox cbSubs = (CheckBox)view.findViewById(R.id.checkBoxSubscribe); + + if (is_add) { + adb.setTitle("Add Buddy"); + } else { + adb.setTitle("Edit Buddy"); + etUri. setText(initial.getUri()); + cbSubs.setChecked(initial.getSubscribe()); + } + + adb.setCancelable(false); + adb.setPositiveButton("OK", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog,int id) { + cfg.setUri(etUri.getText().toString()); + cfg.setSubscribe(cbSubs.isChecked()); + + if (is_add) { + account.addBuddy(cfg); + buddyList.add(putData(cfg.getUri(), "")); + buddyListAdapter.notifyDataSetChanged(); + buddyListSelectedIdx = -1; + } else { + if (!old_cfg.getUri().equals(cfg.getUri())) { + account.delBuddy(buddyListSelectedIdx); + account.addBuddy(cfg); + buddyList.remove(buddyListSelectedIdx); + buddyList.add(putData(cfg.getUri(), "")); + buddyListAdapter.notifyDataSetChanged(); + buddyListSelectedIdx = -1; + } else if (old_cfg.getSubscribe() != cfg.getSubscribe()) { + MyBuddy bud = account.buddyList.get(buddyListSelectedIdx); + try { + bud.subscribePresence(cfg.getSubscribe()); + } catch (Exception e) {} + } + } + } + }); + adb.setNegativeButton("Cancel", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog,int id) { + dialog.cancel(); + } + }); + + AlertDialog ad = adb.create(); + ad.show(); + } + + public void addBuddy(View view) { + dlgAddEditBuddy(null); + } + + public void editBuddy(View view) { + if (buddyListSelectedIdx == -1) + return; + + BuddyConfig old_cfg = account.buddyList.get(buddyListSelectedIdx).cfg; + dlgAddEditBuddy(old_cfg); + } + + public void delBuddy(View view) { + if (buddyListSelectedIdx == -1) + return; + + final HashMap<String, String> item = (HashMap<String, String>) buddyListView.getItemAtPosition(buddyListSelectedIdx); + String buddy_uri = item.get("uri"); + + DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + account.delBuddy(buddyListSelectedIdx); + buddyList.remove(item); + buddyListAdapter.notifyDataSetChanged(); + buddyListSelectedIdx = -1; + break; + case DialogInterface.BUTTON_NEGATIVE: + break; + } + } + }; + + AlertDialog.Builder adb = new AlertDialog.Builder(this); + adb.setTitle(buddy_uri); + adb.setMessage("\nDelete this buddy?\n"); + adb.setPositiveButton("Yes", ocl); + adb.setNegativeButton("No", ocl); + adb.show(); + } + + + /* + * === MyAppObserver === + * + * As we cannot do UI from worker thread, the callbacks mostly just send + * a message to UI/main thread. + */ + + public void notifyIncomingCall(MyCall call) { + Message m = Message.obtain(handler, MSG_TYPE.INCOMING_CALL, call); + m.sendToTarget(); + } + + public void notifyRegState(pjsip_status_code code, String reason, int expiration) { + String msg_str = ""; + if (expiration == 0) + msg_str += "Unregistration"; + else + msg_str += "Registration"; + + if (code.swigValue()/100 == 2) + msg_str += " successful"; + else + msg_str += " failed: " + reason; + + Message m = Message.obtain(handler, MSG_TYPE.REG_STATE, msg_str); + m.sendToTarget(); + } + + public void notifyCallState(MyCall call) { + if (currentCall == null || call.getId() != currentCall.getId()) + return; + + CallInfo ci; + try { + ci = call.getInfo(); + } catch (Exception e) { + ci = null; + } + Message m = Message.obtain(handler, MSG_TYPE.CALL_STATE, ci); + m.sendToTarget(); + } + + public void notifyBuddyState(MyBuddy buddy) { + Message m = Message.obtain(handler, MSG_TYPE.BUDDY_STATE, buddy); + m.sendToTarget(); + } + + /* === end of MyAppObserver ==== */ + +} diff --git a/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java new file mode 100644 index 00000000..7d7ab5d4 --- /dev/null +++ b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java @@ -0,0 +1,449 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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.pjsip.pjsua2.app; + +import java.io.File; +import java.util.ArrayList; +import org.pjsip.pjsua2.*; + + +/* Interface to separate UI & engine a bit better */ +interface MyAppObserver { + abstract void notifyRegState(pjsip_status_code code, String reason, int expiration); + abstract void notifyIncomingCall(MyCall call); + abstract void notifyCallState(MyCall call); + abstract void notifyBuddyState(MyBuddy buddy); +} + + +class MyLogWriter extends LogWriter { + @Override + public void write(LogEntry entry) { + System.out.println(entry.getMsg()); + } +} + + +class MyCall extends Call { + MyCall(MyAccount acc, int call_id) { + super(acc, call_id); + } + + @Override + public void onCallState(OnCallStateParam prm) { + MyApp.observer.notifyCallState(this); + } + + @Override + public void onCallMediaState(OnCallMediaStateParam prm) { + CallInfo ci; + try { + ci = getInfo(); + } catch (Exception e) { + return; + } + + CallMediaInfoVector cmiv = ci.getMedia(); + + for (int i = 0; i < cmiv.size(); i++) { + CallMediaInfo cmi = cmiv.get(i); + if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_AUDIO && + (cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE || + cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_REMOTE_HOLD)) + { + // unfortunately, on Java too, the returned Media cannot be downcasted to AudioMedia + Media m = getMedia(i); + AudioMedia am = AudioMedia.typecastFromMedia(m); + + // connect ports + try { + MyApp.ep.audDevManager().getCaptureDevMedia().startTransmit(am); + am.startTransmit(MyApp.ep.audDevManager().getPlaybackDevMedia()); + } catch (Exception e) { + continue; + } + } + } + } +} + + +class MyAccount extends Account { + public ArrayList<MyBuddy> buddyList = new ArrayList<MyBuddy>(); + public AccountConfig cfg; + + MyAccount(AccountConfig config) { + super(); + cfg = config; + } + + public MyBuddy addBuddy(BuddyConfig bud_cfg) + { + /* Create Buddy */ + MyBuddy bud = new MyBuddy(bud_cfg); + try { + bud.create(this, bud_cfg); + } catch (Exception e) { + bud = null; + } + + if (bud != null) { + buddyList.add(bud); + if (bud_cfg.getSubscribe()) + try { + bud.subscribePresence(true); + } catch (Exception e) {} + } + + return bud; + } + + public void delBuddy(MyBuddy buddy) { + buddyList.remove(buddy); + } + + public void delBuddy(int index) { + buddyList.remove(index); + } + + @Override + public void onRegState(OnRegStateParam prm) { + MyApp.observer.notifyRegState(prm.getCode(), prm.getReason(), prm.getExpiration()); + } + + @Override + public void onIncomingCall(OnIncomingCallParam prm) { + System.out.println("======== Incoming call ======== "); + MyCall call = new MyCall(this, prm.getCallId()); + MyApp.observer.notifyIncomingCall(call); + } + + @Override + public void onInstantMessage(OnInstantMessageParam prm) { + System.out.println("======== Incoming pager ======== "); + System.out.println("From : " + prm.getFromUri()); + System.out.println("To : " + prm.getToUri()); + System.out.println("Contact : " + prm.getContactUri()); + System.out.println("Mimetype : " + prm.getContentType()); + System.out.println("Body : " + prm.getMsgBody()); + } +} + + +class MyBuddy extends Buddy { + public BuddyConfig cfg; + + MyBuddy(BuddyConfig config) { + super(); + cfg = config; + } + + String getStatusText() { + BuddyInfo bi; + + try { + bi = getInfo(); + } catch (Exception e) { + return "?"; + } + + String status = ""; + if (bi.getSubState() == pjsip_evsub_state.PJSIP_EVSUB_STATE_ACTIVE) { + if (bi.getPresStatus().getStatus() == pjsua_buddy_status.PJSUA_BUDDY_STATUS_ONLINE) { + status = bi.getPresStatus().getStatusText(); + if (status == null || status.isEmpty()) { + status = "Online"; + } + } else if (bi.getPresStatus().getStatus() == pjsua_buddy_status.PJSUA_BUDDY_STATUS_OFFLINE) { + status = "Offline"; + } else { + status = "Unknown"; + } + } + return status; + } + + @Override + public void onBuddyState() { + MyApp.observer.notifyBuddyState(this); + } + +} + + +class MyAccountConfig { + public AccountConfig accCfg = new AccountConfig(); + public ArrayList<BuddyConfig> buddyCfgs = new ArrayList<BuddyConfig>(); + + public void readObject(ContainerNode node) { + try { + ContainerNode acc_node = node.readContainer("Account"); + accCfg.readObject(acc_node); + ContainerNode buddies_node = acc_node.readArray("buddies"); + buddyCfgs.clear(); + while (buddies_node.hasUnread()) { + BuddyConfig bud_cfg = new BuddyConfig(); + bud_cfg.readObject(buddies_node); + buddyCfgs.add(bud_cfg); + } + } catch (Exception e) {} + } + + public void writeObject(ContainerNode node) { + try { + ContainerNode acc_node = node.writeNewContainer("Account"); + accCfg.writeObject(acc_node); + ContainerNode buddies_node = acc_node.writeNewArray("buddies"); + for (int j = 0; j < buddyCfgs.size(); j++) { + buddyCfgs.get(j).writeObject(buddies_node); + } + } catch (Exception e) {} + } +} + + +class MyApp { + static { + System.loadLibrary("pjsua2"); + System.out.println("Library loaded"); + } + + public static Endpoint ep = new Endpoint(); + public static MyAppObserver observer; + public ArrayList<MyAccount> accList = new ArrayList<MyAccount>(); + + private ArrayList<MyAccountConfig> accCfgs = new ArrayList<MyAccountConfig>(); + private EpConfig epConfig = new EpConfig(); + private TransportConfig sipTpConfig = new TransportConfig(); + private String appDir; + + /* Maintain reference to log writer to avoid premature cleanup by GC */ + private MyLogWriter logWriter; + + private final String configName = "pjsua2.json"; + private final int SIP_PORT = 6000; + private final int LOG_LEVEL = 4; + + public void init(MyAppObserver obs, String app_dir) { + init(obs, app_dir, false); + } + + public void init(MyAppObserver obs, String app_dir, boolean own_worker_thread) { + observer = obs; + appDir = app_dir; + + /* Create endpoint */ + try { + ep.libCreate(); + } catch (Exception e) { + return; + } + + + /* Load config */ + String configPath = appDir + "/" + configName; + File f = new File(configPath); + if (f.exists()) { + loadConfig(configPath); + } else { + /* Set 'default' values */ + sipTpConfig.setPort(SIP_PORT); + } + + /* Override log level setting */ + epConfig.getLogConfig().setLevel(LOG_LEVEL); + epConfig.getLogConfig().setConsoleLevel(LOG_LEVEL); + + /* Set log config. */ + LogConfig log_cfg = epConfig.getLogConfig(); + logWriter = new MyLogWriter(); + log_cfg.setWriter(logWriter); + log_cfg.setDecor(log_cfg.getDecor() & + ~(pj_log_decoration.PJ_LOG_HAS_CR.swigValue() | + pj_log_decoration.PJ_LOG_HAS_NEWLINE.swigValue())); + + /* Set ua config. */ + UaConfig ua_cfg = epConfig.getUaConfig(); + ua_cfg.setUserAgent("Pjsua2And" + ep.libVersion().getFull()); + if (own_worker_thread) { + ua_cfg.setThreadCnt(0); + ua_cfg.setMainThreadOnly(true); + } + + /* Init endpoint */ + try { + ep.libInit(epConfig); + } catch (Exception e) { + return; + } + + /* Create transports. */ + try { + ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, sipTpConfig); + } catch (Exception e) { + System.out.println(e); + } + + try { + ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TCP, sipTpConfig); + } catch (Exception e) { + System.out.println(e); + } + + /* Create accounts. */ + for (int i = 0; i < accCfgs.size(); i++) { + MyAccountConfig my_cfg = accCfgs.get(i); + MyAccount acc = addAcc(my_cfg.accCfg); + if (acc == null) + continue; + + /* Add Buddies */ + for (int j = 0; j < my_cfg.buddyCfgs.size(); j++) { + BuddyConfig bud_cfg = my_cfg.buddyCfgs.get(j); + acc.addBuddy(bud_cfg); + } + } + + /* Start. */ + try { + ep.libStart(); + } catch (Exception e) { + return; + } + } + + public MyAccount addAcc(AccountConfig cfg) { + MyAccount acc = new MyAccount(cfg); + try { + acc.create(cfg); + } catch (Exception e) { + acc = null; + return null; + } + + accList.add(acc); + return acc; + } + + public void delAcc(MyAccount acc) { + accList.remove(acc); + } + + private void loadConfig(String filename) { + JsonDocument json = new JsonDocument(); + + try { + /* Load file */ + json.loadFile(filename); + ContainerNode root = json.getRootContainer(); + + /* Read endpoint config */ + epConfig.readObject(root); + + /* Read transport config */ + ContainerNode tp_node = root.readContainer("SipTransport"); + sipTpConfig.readObject(tp_node); + + /* Read account configs */ + accCfgs.clear(); + ContainerNode accs_node = root.readArray("accounts"); + while (accs_node.hasUnread()) { + MyAccountConfig acc_cfg = new MyAccountConfig(); + acc_cfg.readObject(accs_node); + accCfgs.add(acc_cfg); + } + } catch (Exception e) { + System.out.println(e); + } + + /* Force delete json now, as I found that Java somehow destroys it + * after lib has been destroyed and from non-registered thread. + */ + json.delete(); + } + + private void buildAccConfigs() { + /* Sync accCfgs from accList */ + accCfgs.clear(); + for (int i = 0; i < accList.size(); i++) { + MyAccount acc = accList.get(i); + MyAccountConfig my_acc_cfg = new MyAccountConfig(); + my_acc_cfg.accCfg = acc.cfg; + + my_acc_cfg.buddyCfgs.clear(); + for (int j = 0; j < acc.buddyList.size(); j++) { + MyBuddy bud = acc.buddyList.get(j); + my_acc_cfg.buddyCfgs.add(bud.cfg); + } + + accCfgs.add(my_acc_cfg); + } + } + + private void saveConfig(String filename) { + JsonDocument json = new JsonDocument(); + + try { + /* Write endpoint config */ + json.writeObject(epConfig); + + /* Write transport config */ + ContainerNode tp_node = json.writeNewContainer("SipTransport"); + sipTpConfig.writeObject(tp_node); + + /* Write account configs */ + buildAccConfigs(); + ContainerNode accs_node = json.writeNewArray("accounts"); + for (int i = 0; i < accCfgs.size(); i++) { + accCfgs.get(i).writeObject(accs_node); + } + + /* Save file */ + json.saveFile(filename); + } catch (Exception e) {} + + /* Force delete json now, as I found that Java somehow destroys it + * after lib has been destroyed and from non-registered thread. + */ + json.delete(); + } + + public void deinit() { + String configPath = appDir + "/" + configName; + saveConfig(configPath); + + /* Try force GC to avoid late destroy of PJ objects as they should be + * deleted before lib is destroyed. + */ + Runtime.getRuntime().gc(); + + /* Shutdown pjsua. Note that Endpoint destructor will also invoke + * libDestroy(), so this will be a test of double libDestroy(). + */ + try { + ep.libDestroy(); + } catch (Exception e) {} + + /* Force delete Endpoint here, to avoid deletion from a non- + * registered thread (by GC?). + */ + ep.delete(); + ep = null; + } +} diff --git a/pjsip-apps/src/swig/java/sample.java b/pjsip-apps/src/swig/java/sample.java new file mode 100644 index 00000000..34d06b82 --- /dev/null +++ b/pjsip-apps/src/swig/java/sample.java @@ -0,0 +1,140 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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.pjsip.pjsua2.app; + +import java.io.IOException; +import org.pjsip.pjsua2.*; +import org.pjsip.pjsua2.app.*; + +class MyObserver implements MyAppObserver { + private static MyCall currentCall = null; + + @Override + public void notifyRegState(pjsip_status_code code, String reason, int expiration) {} + + @Override + public void notifyIncomingCall(MyCall call) { + /* Auto answer. */ + CallOpParam call_param = new CallOpParam(); + call_param.setStatusCode(pjsip_status_code.PJSIP_SC_OK); + try { + currentCall = call; + currentCall.answer(call_param); + } catch (Exception e) { + System.out.println(e); + return; + } + } + + @Override + public void notifyCallState(MyCall call) { + if (currentCall == null || call.getId() != currentCall.getId()) + return; + + CallInfo ci; + try { + ci = call.getInfo(); + } catch (Exception e) { + ci = null; + } + if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) + currentCall = null; + } + + @Override + public void notifyBuddyState(MyBuddy buddy) {} +} + +class MyShutdownHook extends Thread { + Thread thread; + MyShutdownHook(Thread thr) { + thread = thr; + } + public void run() { + thread.interrupt(); + try { + thread.join(); + } catch (Exception e) { + ; + } + } +} + +public class sample { + private static MyApp app = new MyApp(); + private static MyAppObserver observer = new MyObserver(); + private static MyAccount account = null; + private static AccountConfig accCfg = null; + + private static void runWorker() { + try { + app.init(observer, ".", true); + } catch (Exception e) { + System.out.println(e); + app.deinit(); + System.exit(-1); + } + + if (app.accList.size() == 0) { + accCfg = new AccountConfig(); + accCfg.setIdUri("sip:localhost"); + account = app.addAcc(accCfg); + + accCfg.setIdUri("sip:301@pjsip.org"); + AccountSipConfig sipCfg = accCfg.getSipConfig(); + AuthCredInfoVector ciVec = sipCfg.getAuthCreds(); + ciVec.add(new AuthCredInfo("Digest", + "*", + "301", + 0, + "pw301")); + + StringVector proxy = sipCfg.getProxies(); + proxy.add("sip:pjsip.org;transport=tcp"); + + AccountRegConfig regCfg = accCfg.getRegConfig(); + regCfg.setRegistrarUri("sip:pjsip.org"); + account = app.addAcc(accCfg); + } else { + account = app.accList.get(0); + accCfg = account.cfg; + } + + try { + account.modify(accCfg); + } catch (Exception e) {} + + while (!Thread.currentThread().isInterrupted()) { + MyApp.ep.libHandleEvents(10); + try { + Thread.currentThread().sleep(50); + } catch (InterruptedException ie) { + break; + } + } + app.deinit(); + } + + public static void main(String argv[]) { + Runtime.getRuntime().addShutdownHook(new MyShutdownHook(Thread.currentThread())); + + runWorker(); + } +} diff --git a/pjsip-apps/src/swig/java/test.java b/pjsip-apps/src/swig/java/test.java new file mode 100644 index 00000000..f616460d --- /dev/null +++ b/pjsip-apps/src/swig/java/test.java @@ -0,0 +1,17 @@ +import org.pjsip.pjsua2.*; + +public class test { + static { + System.loadLibrary("pjsua2"); + System.out.println("Library loaded"); + } + + public static void main(String argv[]) { + + AuthCredInfo cred = new AuthCredInfo(); + + cred.setRealm("Hello world"); + + System.out.println(cred.getRealm()); + } +} |