summaryrefslogtreecommitdiff
path: root/pjsip-apps/src/swig
diff options
context:
space:
mode:
authorLiong Sauw Ming <ming@teluu.com>2014-01-16 05:30:46 +0000
committerLiong Sauw Ming <ming@teluu.com>2014-01-16 05:30:46 +0000
commite56ea14ab8531ee3cec375460577d1b89bf62e26 (patch)
treedf77c3acb961514b2022ee9e030071b691145920 /pjsip-apps/src/swig
parentbd1c47e995a3a844868f1d4dcc8f77f163ae721b (diff)
Closed #1723: Merging pjsua2 branch into trunk
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4704 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip-apps/src/swig')
-rw-r--r--pjsip-apps/src/swig/Makefile32
-rw-r--r--pjsip-apps/src/swig/importsym.py193
-rw-r--r--pjsip-apps/src/swig/java/Makefile124
-rw-r--r--pjsip-apps/src/swig/java/android/.classpath9
-rw-r--r--pjsip-apps/src/swig/java/android/.project33
-rw-r--r--pjsip-apps/src/swig/java/android/.settings/org.eclipse.jdt.core.prefs4
-rw-r--r--pjsip-apps/src/swig/java/android/AndroidManifest.xml44
-rw-r--r--pjsip-apps/src/swig/java/android/ic_launcher-web.pngbin0 -> 51394 bytes
-rw-r--r--pjsip-apps/src/swig/java/android/jni/Android.mk12
-rw-r--r--pjsip-apps/src/swig/java/android/jni/Application.mk1
-rw-r--r--pjsip-apps/src/swig/java/android/proguard-project.txt20
-rw-r--r--pjsip-apps/src/swig/java/android/project.properties14
-rw-r--r--pjsip-apps/src/swig/java/android/res/drawable-hdpi/ic_launcher.pngbin0 -> 7658 bytes
-rw-r--r--pjsip-apps/src/swig/java/android/res/drawable-mdpi/ic_launcher.pngbin0 -> 3777 bytes
-rw-r--r--pjsip-apps/src/swig/java/android/res/drawable-xhdpi/ic_launcher.pngbin0 -> 12516 bytes
-rw-r--r--pjsip-apps/src/swig/java/android/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 24777 bytes
-rw-r--r--pjsip-apps/src/swig/java/android/res/drawable/bkg.xml5
-rw-r--r--pjsip-apps/src/swig/java/android/res/layout/activity_call.xml41
-rw-r--r--pjsip-apps/src/swig/java/android/res/layout/activity_main.xml65
-rw-r--r--pjsip-apps/src/swig/java/android/res/layout/dlg_account_config.xml77
-rw-r--r--pjsip-apps/src/swig/java/android/res/layout/dlg_add_buddy.xml24
-rw-r--r--pjsip-apps/src/swig/java/android/res/menu/call.xml9
-rw-r--r--pjsip-apps/src/swig/java/android/res/menu/main.xml14
-rw-r--r--pjsip-apps/src/swig/java/android/res/values-sw600dp/dimens.xml8
-rw-r--r--pjsip-apps/src/swig/java/android/res/values-sw720dp-land/dimens.xml9
-rw-r--r--pjsip-apps/src/swig/java/android/res/values-v11/styles.xml11
-rw-r--r--pjsip-apps/src/swig/java/android/res/values-v14/styles.xml12
-rw-r--r--pjsip-apps/src/swig/java/android/res/values/colors.xml5
-rw-r--r--pjsip-apps/src/swig/java/android/res/values/dimens.xml7
-rw-r--r--pjsip-apps/src/swig/java/android/res/values/strings.xml9
-rw-r--r--pjsip-apps/src/swig/java/android/res/values/styles.xml20
-rw-r--r--pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java146
-rw-r--r--pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java497
-rw-r--r--pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java449
-rw-r--r--pjsip-apps/src/swig/java/sample.java140
-rw-r--r--pjsip-apps/src/swig/java/test.java17
-rw-r--r--pjsip-apps/src/swig/pjsua2.i111
-rw-r--r--pjsip-apps/src/swig/python/Makefile29
-rw-r--r--pjsip-apps/src/swig/python/helper.mak20
-rw-r--r--pjsip-apps/src/swig/python/setup.py118
-rw-r--r--pjsip-apps/src/swig/python/test.py112
-rw-r--r--pjsip-apps/src/swig/symbols.i130
-rw-r--r--pjsip-apps/src/swig/symbols.lst34
43 files changed, 2605 insertions, 0 deletions
diff --git a/pjsip-apps/src/swig/Makefile b/pjsip-apps/src/swig/Makefile
new file mode 100644
index 00000000..815e0e18
--- /dev/null
+++ b/pjsip-apps/src/swig/Makefile
@@ -0,0 +1,32 @@
+include ../../../build.mak
+
+ifneq ($(findstring android,$(TARGET_NAME)),)
+ # no python for android
+ DIRS = java
+else
+ DIRS = python java
+endif
+
+export SWIG_FLAGS=-I../../../../pjlib/include \
+ -I../../../../pjlib-util/include \
+ -I../../../../pjmedia/include \
+ -I../../../../pjsip/include \
+ -I../../../../pjnath/include -c++
+export SRC_DIR=../../../../pjsip/include
+export SRCS=$(SRC_DIR)/pjsua2/endpoint.hpp $(SRC_DIR)/pjsua2/types.hpp
+
+.PHONY: all clean dep depend distclean print realclean install uninstall
+
+all: symbols.i
+
+all clean dep depend distclean print realclean install uninstall:
+ for dir in $(DIRS); do \
+ if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \
+ true; \
+ else \
+ exit 1; \
+ fi; \
+ done
+
+symbols.i: symbols.lst
+ python importsym.py
diff --git a/pjsip-apps/src/swig/importsym.py b/pjsip-apps/src/swig/importsym.py
new file mode 100644
index 00000000..32faf2cc
--- /dev/null
+++ b/pjsip-apps/src/swig/importsym.py
@@ -0,0 +1,193 @@
+# $Id$
+#
+# importsym.py: Import C symbol decls (structs, enums, etc) and write them
+# to another file
+#
+# 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
+#
+import pycparser
+from pycparser import c_generator
+import sys
+import os
+
+def which(program):
+ import os
+ def is_exe(fpath):
+ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+ if sys.platform == 'win32' and not program.endswith(".exe"):
+ program += ".exe"
+
+ fpath, fname = os.path.split(program)
+ if fpath:
+ if is_exe(program):
+ return program
+ else:
+ for path in os.environ["PATH"].split(os.pathsep):
+ path = path.strip('"')
+ exe_file = os.path.join(path, program)
+ if is_exe(exe_file):
+ return exe_file
+ return None
+
+#
+PJ_ROOT_PATH = "../../../"
+
+# CPP is needed by pycparser.
+CPP_PATH = which("cpp")
+if not CPP_PATH:
+ print 'Error: need to have cpp in PATH'
+ sys.exit(1)
+
+# Hardcoded!
+if sys.platform == 'win32':
+ PYCPARSER_DIR="C:/devs/tools/pycparser"
+elif sys.platform == "linux2":
+ PYCPARSER_DIR="/home/bennylp/Desktop/opt/src/pycparser-master"
+else:
+ PYCPARSER_DIR="/Library/Python/2.7/site-packages/pycparser"
+
+if not os.path.exists(PYCPARSER_DIR + '/utils/fake_libc_include'):
+ print "Error: couldn't find pycparser utils in '%s'" % PYPARSER_DIR
+ sys.exit(1)
+
+# Heading, to be placed before the source files
+C_HEADING_SECTION = """
+#define PJ_AUTOCONF 1
+#define jmp_buf int
+#define __attribute__(x)
+"""
+
+# CPP (C preprocessor) settings
+CPP_CFLAGS = [
+ '-I' + PYCPARSER_DIR + '/utils/fake_libc_include',
+ "-I" + PJ_ROOT_PATH + "pjlib/include",
+ "-I" + PJ_ROOT_PATH + "pjlib-util/include",
+ "-I" + PJ_ROOT_PATH + "pjnath/include",
+ "-I" + PJ_ROOT_PATH + "pjmedia/include",
+ "-I" + PJ_ROOT_PATH + "pjsip/include"
+ ]
+
+
+class SymbolVisitor(pycparser.c_ast.NodeVisitor):
+ def __init__(self, names):
+ self.nodeDict = {}
+ for name in names:
+ self.nodeDict[name] = None
+
+ def _add(self, node):
+ if self.nodeDict.has_key(node.name):
+ self.nodeDict[node.name] = node
+
+ def visit_Struct(self, node):
+ self._add(node)
+
+ def visit_Enum(self, node):
+ self._add(node)
+
+ def visit_Typename(self, node):
+ self._add(node)
+
+ def visit_Typedef(self, node):
+ self._add(node)
+
+
+TEMP_FILE="tmpsrc.h"
+
+class SymbolImporter:
+ """
+ Import C selected declarations from C source file and move it
+ to another file.
+
+ Parameters:
+ - listfile Path of file containing list of C source file
+ and identifier names to be imported. The format
+ of the listfile is:
+
+ filename name1 name2 name3
+
+ for example:
+
+ pj/sock_qos.h pj_qos_type pj_qos_flag
+ pj/types.h pj_status_t PJ_SUCCESS
+ """
+ def __init__(self):
+ pass
+
+ def process(self, listfile, outfile):
+
+ # Read listfile
+ f = open(listfile)
+ lines = f.readlines()
+ f.close()
+
+ # Process each line in list file, while generating the
+ # temporary C file to be processed by pycparser
+ f = open(TEMP_FILE, "w")
+ f.write(C_HEADING_SECTION)
+ names = []
+ fcnt = 0
+ for line in lines:
+ spec = line.split()
+ if len(spec) < 2:
+ continue
+ fcnt += 1
+ f.write("#include <%s>\n" % spec[0])
+ names.extend(spec[1:])
+ f.close()
+ print 'Parsing %d symbols from %d files..' % (len(names), fcnt)
+
+ # Parse the temporary C file
+ ast = pycparser.parse_file(TEMP_FILE, use_cpp=True, cpp_path=CPP_PATH, cpp_args=CPP_CFLAGS)
+ os.remove(TEMP_FILE)
+
+ # Filter the declarations that we wanted
+ print 'Filtering..'
+ visitor = SymbolVisitor(names)
+ visitor.visit(ast)
+
+ # Print symbol declarations to outfile
+ print 'Writing declarations..'
+ f = open(outfile, 'w')
+ f.write("// This file is autogenerated by importsym script, do not modify!\n\n")
+ gen = pycparser.c_generator.CGenerator()
+ for name in names:
+ node = visitor.nodeDict[name]
+ if not node:
+ print " ** Warning: declaration for '%s' is not found **" % k
+ else:
+ print " writing '%s'.." % name
+ output = gen.visit(node) + ";\n\n"
+ f.write(output)
+ f.close()
+ print "Done."
+
+
+if __name__ == "__main__":
+ print "Importing symbols: 'symbols.lst' --> 'symbols.i'"
+ si = SymbolImporter()
+ si.process("symbols.lst", "symbols.i")
+ try:
+ os.remove("lextab.py")
+ except OSError:
+ pass
+ try:
+ os.remove("yacctab.py")
+ except OSError:
+ pass
+
+ \ No newline at end of file
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
new file mode 100644
index 00000000..a18cbb48
--- /dev/null
+++ b/pjsip-apps/src/swig/java/android/ic_launcher-web.png
Binary files differ
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
new file mode 100644
index 00000000..288b6655
--- /dev/null
+++ b/pjsip-apps/src/swig/java/android/res/drawable-hdpi/ic_launcher.png
Binary files differ
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
new file mode 100644
index 00000000..6ae570b4
--- /dev/null
+++ b/pjsip-apps/src/swig/java/android/res/drawable-mdpi/ic_launcher.png
Binary files differ
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
new file mode 100644
index 00000000..d4fb7cd9
--- /dev/null
+++ b/pjsip-apps/src/swig/java/android/res/drawable-xhdpi/ic_launcher.png
Binary files differ
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
new file mode 100644
index 00000000..85a60815
--- /dev/null
+++ b/pjsip-apps/src/swig/java/android/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
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());
+ }
+}
diff --git a/pjsip-apps/src/swig/pjsua2.i b/pjsip-apps/src/swig/pjsua2.i
new file mode 100644
index 00000000..2cc41d4a
--- /dev/null
+++ b/pjsip-apps/src/swig/pjsua2.i
@@ -0,0 +1,111 @@
+%module(directors="1") pjsua2
+
+//
+// Suppress few warnings
+//
+#pragma SWIG nowarn=312 // 312: nested struct (in types.h, sip_auth.h)
+
+//
+// Header section
+//
+%{
+#include "pjsua2.hpp"
+using namespace std;
+using namespace pj;
+%}
+
+#ifdef SWIGPYTHON
+ %feature("director:except") {
+ if( $error != NULL ) {
+ PyObject *ptype, *pvalue, *ptraceback;
+ PyErr_Fetch( &ptype, &pvalue, &ptraceback );
+ PyErr_Restore( ptype, pvalue, ptraceback );
+ PyErr_Print();
+ //Py_Exit(1);
+ }
+ }
+#endif
+
+// Allow C++ exceptions to be handled in Java
+#ifdef SWIGJAVA
+ %typemap(throws, throws="java.lang.Exception") pj::Error {
+ jclass excep = jenv->FindClass("java/lang/Exception");
+ if (excep)
+ jenv->ThrowNew(excep, $1.info(true).c_str());
+ return $null;
+}
+
+ // Force the Error Java class to extend java.lang.Exception
+ %typemap(javabase) pj::Error "java.lang.Exception";
+
+ // Override getMessage()
+ %typemap(javacode) pj::Error %{
+ public String getMessage() {
+ return getTitle();
+ }
+%}
+#endif
+
+
+// Constants from PJSIP libraries
+%include "symbols.i"
+
+
+//
+// Classes that can be extended in the target language
+//
+%feature("director") LogWriter;
+%feature("director") Endpoint;
+%feature("director") Account;
+%feature("director") Call;
+%feature("director") Buddy;
+%feature("director") FindBuddyMatch;
+
+//
+// STL stuff.
+//
+%include "std_string.i"
+%include "std_vector.i"
+
+%template(StringVector) std::vector<std::string>;
+%template(IntVector) std::vector<int>;
+
+//
+// Ignore stuffs in pjsua2
+//
+%ignore fromPj;
+%ignore toPj;
+
+//
+// Now include the API itself.
+//
+%include "pjsua2/types.hpp"
+
+%ignore pj::ContainerNode::op;
+%ignore pj::ContainerNode::data;
+%ignore container_node_op;
+%ignore container_node_internal_data;
+%include "pjsua2/persistent.hpp"
+
+%include "pjsua2/siptypes.hpp"
+
+%template(SipHeaderVector) std::vector<pj::SipHeader>;
+%template(AuthCredInfoVector) std::vector<pj::AuthCredInfo>;
+%template(SipMultipartPartVector) std::vector<pj::SipMultipartPart>;
+%template(BuddyVector) std::vector<pj::Buddy*>;
+%template(AudioMediaVector) std::vector<pj::AudioMedia*>;
+%template(MediaFormatVector) std::vector<pj::MediaFormat*>;
+%template(AudioDevInfoVector) std::vector<pj::AudioDevInfo*>;
+%template(CodecInfoVector) std::vector<pj::CodecInfo*>;
+
+%include "pjsua2/media.hpp"
+%include "pjsua2/endpoint.hpp"
+%include "pjsua2/presence.hpp"
+%include "pjsua2/account.hpp"
+%include "pjsua2/call.hpp"
+
+%template(CallMediaInfoVector) std::vector<pj::CallMediaInfo>;
+
+%ignore pj::JsonDocument::allocElement;
+%ignore pj::JsonDocument::getPool;
+%include "pjsua2/json.hpp"
diff --git a/pjsip-apps/src/swig/python/Makefile b/pjsip-apps/src/swig/python/Makefile
new file mode 100644
index 00000000..80af9bf9
--- /dev/null
+++ b/pjsip-apps/src/swig/python/Makefile
@@ -0,0 +1,29 @@
+PYTHON_SO=_pjsua2.so
+
+#PYTHON_SETUP_FLAGS = --inplace
+ifeq ($(OS),Windows_NT)
+ PYTHON_SETUP_FLAGS += --compiler=mingw32
+endif
+
+SWIG_FLAGS += -w312
+
+.PHONY: all install uninstall
+
+all: $(PYTHON_SO)
+
+$(PYTHON_SO): pjsua2_wrap.cpp setup.py
+ python setup.py build $(PYTHON_SETUP_FLAGS)
+
+pjsua2_wrap.cpp: ../pjsua2.i ../symbols.i Makefile $(SRCS)
+ swig $(SWIG_FLAGS) -python -o pjsua2_wrap.cpp ../pjsua2.i
+
+clean distclean realclean:
+ rm -rf $(PYTHON_SO) pjsua2_wrap.cpp pjsua2_wrap.h pjsua2.py build *.pyc
+
+install:
+ python setup.py install --user
+
+uninstall:
+ rm -f $(HOME)/.local/lib/python2.7/site-packages/pjsua2*
+ rm -f $(HOME)/.local/lib/python2.7/site-packages/_pjsua2*
+
diff --git a/pjsip-apps/src/swig/python/helper.mak b/pjsip-apps/src/swig/python/helper.mak
new file mode 100644
index 00000000..41baf2b2
--- /dev/null
+++ b/pjsip-apps/src/swig/python/helper.mak
@@ -0,0 +1,20 @@
+include ../../../../build.mak
+
+lib_dir:
+ @for token in `echo $(APP_LDFLAGS)`; do \
+ echo $$token | grep L | sed 's/-L//'; \
+ done
+
+inc_dir:
+ @for token in `echo $(APP_CFLAGS)`; do \
+ echo $$token | grep I | sed 's/-I//'; \
+ done
+
+libs:
+ @for token in `echo $(APP_LDLIBS)`; do \
+ echo $$token | grep \\-l | sed 's/-l//'; \
+ done
+
+target_name:
+ @echo $(TARGET_NAME)
+
diff --git a/pjsip-apps/src/swig/python/setup.py b/pjsip-apps/src/swig/python/setup.py
new file mode 100644
index 00000000..16842e51
--- /dev/null
+++ b/pjsip-apps/src/swig/python/setup.py
@@ -0,0 +1,118 @@
+# $Id$
+#
+# pjsua2 Setup script.
+#
+# Copyright (C)2012 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
+#
+from distutils.core import setup, Extension
+import os
+import sys
+import platform
+
+# find pjsip version
+pj_version=""
+pj_version_major=""
+pj_version_minor=""
+pj_version_rev=""
+pj_version_suffix=""
+f = open('../../../../version.mak', 'r')
+for line in f:
+ if line.find("export PJ_VERSION_MAJOR") != -1:
+ tokens=line.split("=")
+ if len(tokens)>1:
+ pj_version_major= tokens[1].strip()
+ elif line.find("export PJ_VERSION_MINOR") != -1:
+ tokens=line.split("=")
+ if len(tokens)>1:
+ pj_version_minor= line.split("=")[1].strip()
+ elif line.find("export PJ_VERSION_REV") != -1:
+ tokens=line.split("=")
+ if len(tokens)>1:
+ pj_version_rev= line.split("=")[1].strip()
+ elif line.find("export PJ_VERSION_SUFFIX") != -1:
+ tokens=line.split("=")
+ if len(tokens)>1:
+ pj_version_suffix= line.split("=")[1].strip()
+
+f.close()
+if not pj_version_major:
+ print 'Unable to get PJ_VERSION_MAJOR'
+ sys.exit(1)
+
+pj_version = pj_version_major + "." + pj_version_minor
+if pj_version_rev:
+ pj_version += "." + pj_version_rev
+if pj_version_suffix:
+ pj_version += "-" + pj_version_suffix
+
+#print 'PJ_VERSION = "'+ pj_version + '"'
+
+# Get targetname
+f = os.popen("make --no-print-directory -f helper.mak target_name")
+pj_target_name = f.read().rstrip("\r\n")
+f.close()
+
+# Fill in pj_inc_dirs
+pj_inc_dirs = []
+f = os.popen("make --no-print-directory -f helper.mak inc_dir")
+for line in f:
+ pj_inc_dirs.append(line.rstrip("\r\n"))
+f.close()
+
+# Fill in pj_lib_dirs
+pj_lib_dirs = []
+f = os.popen("make --no-print-directory -f helper.mak lib_dir")
+for line in f:
+ pj_lib_dirs.append(line.rstrip("\r\n"))
+f.close()
+
+# Fill in pj_libs
+pj_libs = ['pjsua2-' + pj_target_name]
+f = os.popen("make --no-print-directory -f helper.mak libs")
+for line in f:
+ pj_libs.append(line.rstrip("\r\n"))
+f.close()
+
+# Fill in extra link args
+extra_link_args = ['-static-libstdc++']
+if platform.system() == 'Darwin':
+ # Mac OS X depedencies
+ extra_link_args += ["-framework", "CoreFoundation",
+ "-framework", "AudioToolbox",
+ "-framework", "QTKit"]
+ # OS X Lion support
+ if platform.mac_ver()[0].startswith("10.7"):
+ extra_link_args += ["-framework", "AudioUnit"]
+
+
+setup(name="pjsua2",
+ version=pj_version,
+ description='SIP User Agent Library based on PJSIP',
+ url='http://www.pjsip.org',
+ ext_modules = [Extension("_pjsua2",
+ ["pjsua2_wrap.cpp"],
+ define_macros=[('PJ_AUTOCONF', '1'),],
+ include_dirs=pj_inc_dirs,
+ library_dirs=pj_lib_dirs,
+ libraries=pj_libs,
+ extra_link_args=extra_link_args
+ )
+ ],
+ py_modules=["pjsua2"]
+ )
+
+
diff --git a/pjsip-apps/src/swig/python/test.py b/pjsip-apps/src/swig/python/test.py
new file mode 100644
index 00000000..dc805c77
--- /dev/null
+++ b/pjsip-apps/src/swig/python/test.py
@@ -0,0 +1,112 @@
+import pjsua2 as pj
+import sys
+
+#
+# Basic data structure test, to make sure basic struct
+# and array operations work
+#
+def ua_data_test():
+ #
+ # AuthCredInfo
+ #
+ print "UA data types test.."
+ the_realm = "pjsip.org"
+ ci = pj.AuthCredInfo()
+ ci.realm = the_realm
+ ci.dataType = 20
+
+ ci2 = ci
+ assert ci.dataType == 20
+ assert ci2.realm == the_realm
+
+ #
+ # UaConfig
+ # See here how we manipulate std::vector
+ #
+ uc = pj.UaConfig()
+ uc.maxCalls = 10
+ uc.userAgent = "Python"
+ uc.nameserver = pj.StringVector(["10.0.0.1", "10.0.0.2"])
+ uc.nameserver.append("NS1")
+
+ uc2 = uc
+ assert uc2.maxCalls == 10
+ assert uc2.userAgent == "Python"
+ assert len(uc2.nameserver) == 3
+ assert uc2.nameserver[0] == "10.0.0.1"
+ assert uc2.nameserver[1] == "10.0.0.2"
+ assert uc2.nameserver[2] == "NS1"
+
+ print " Dumping nameservers: ",
+ for s in uc2.nameserver:
+ print s,
+ print ""
+
+#
+# Exception test
+#
+def ua_run_test_exception():
+ print "Exception test.."
+ ep = pj.Endpoint()
+ ep.libCreate()
+ got_exception = False
+ try:
+ ep.natDetectType()
+ except pj.Error, e:
+ got_exception = True
+ print " Got exception: status=%u, reason=%s,\n title=%s,\n srcFile=%s, srcLine=%d" % \
+ (e.status, e.reason, e.title, e.srcFile, e.srcLine)
+ assert e.status == 370050
+ assert e.reason.find("PJNATH_ESTUNINSERVER") >= 0
+ assert e.title == "pjsua_detect_nat_type()"
+ assert got_exception
+
+#
+# Custom log writer
+#
+class MyLogWriter(pj.LogWriter):
+ def write(self, entry):
+ print "This is Python:", entry.msg
+
+#
+# Testing log writer callback
+#
+def ua_run_log_test():
+ print "Logging test.."
+ ep_cfg = pj.EpConfig()
+
+ lw = MyLogWriter()
+ ep_cfg.logConfig.writer = lw
+ ep_cfg.logConfig.decor = ep_cfg.logConfig.decor & ~(pj.PJ_LOG_HAS_CR | pj.PJ_LOG_HAS_NEWLINE)
+
+ ep = pj.Endpoint()
+ ep.libCreate()
+ ep.libInit(ep_cfg)
+ ep.libDestroy()
+
+#
+# Simple create, init, start, and destroy sequence
+#
+def ua_run_ua_test():
+ print "UA test run.."
+ ep_cfg = pj.EpConfig()
+
+ ep = pj.Endpoint()
+ ep.libCreate()
+ ep.libInit(ep_cfg)
+ ep.libStart()
+
+ print "************* Endpoint started ok, now shutting down... *************"
+ ep.libDestroy()
+
+#
+# main()
+#
+if __name__ == "__main__":
+ ua_data_test()
+ ua_run_test_exception()
+ ua_run_log_test()
+ ua_run_ua_test()
+ sys.exit(0)
+
+ \ No newline at end of file
diff --git a/pjsip-apps/src/swig/symbols.i b/pjsip-apps/src/swig/symbols.i
new file mode 100644
index 00000000..e101e774
--- /dev/null
+++ b/pjsip-apps/src/swig/symbols.i
@@ -0,0 +1,130 @@
+// This file is autogenerated by importsym script, do not modify!
+
+typedef int pj_status_t;
+
+enum pj_constants_ {PJ_SUCCESS = 0, PJ_TRUE = 1, PJ_FALSE = 0};
+
+typedef unsigned char pj_uint8_t;
+
+typedef int pj_int32_t;
+
+typedef unsigned int pj_uint32_t;
+
+typedef unsigned short pj_uint16_t;
+
+enum pj_file_access {PJ_O_RDONLY = 0x1101, PJ_O_WRONLY = 0x1102, PJ_O_RDWR = 0x1103, PJ_O_APPEND = 0x1108};
+
+enum pj_log_decoration {PJ_LOG_HAS_DAY_NAME = 1, PJ_LOG_HAS_YEAR = 2, PJ_LOG_HAS_MONTH = 4, PJ_LOG_HAS_DAY_OF_MON = 8, PJ_LOG_HAS_TIME = 16, PJ_LOG_HAS_MICRO_SEC = 32, PJ_LOG_HAS_SENDER = 64, PJ_LOG_HAS_NEWLINE = 128, PJ_LOG_HAS_CR = 256, PJ_LOG_HAS_SPACE = 512, PJ_LOG_HAS_COLOR = 1024, PJ_LOG_HAS_LEVEL_TEXT = 2048, PJ_LOG_HAS_THREAD_ID = 4096, PJ_LOG_HAS_THREAD_SWC = 8192, PJ_LOG_HAS_INDENT = 16384};
+
+typedef enum pj_qos_type {PJ_QOS_TYPE_BEST_EFFORT, PJ_QOS_TYPE_BACKGROUND, PJ_QOS_TYPE_VIDEO, PJ_QOS_TYPE_VOICE, PJ_QOS_TYPE_CONTROL} pj_qos_type;
+
+typedef enum pj_qos_flag {PJ_QOS_PARAM_HAS_DSCP = 1, PJ_QOS_PARAM_HAS_SO_PRIO = 2, PJ_QOS_PARAM_HAS_WMM = 4} pj_qos_flag;
+
+typedef enum pj_qos_wmm_prio {PJ_QOS_WMM_PRIO_BULK_EFFORT, PJ_QOS_WMM_PRIO_BULK, PJ_QOS_WMM_PRIO_VIDEO, PJ_QOS_WMM_PRIO_VOICE} pj_qos_wmm_prio;
+
+typedef struct pj_qos_params
+{
+ pj_uint8_t flags;
+ pj_uint8_t dscp_val;
+ pj_uint8_t so_prio;
+ pj_qos_wmm_prio wmm_prio;
+} pj_qos_params;
+
+typedef enum pj_ssl_cipher {PJ_TLS_NULL_WITH_NULL_NULL = 0x00000000, PJ_TLS_RSA_WITH_NULL_MD5 = 0x00000001, PJ_TLS_RSA_WITH_NULL_SHA = 0x00000002, PJ_TLS_RSA_WITH_NULL_SHA256 = 0x0000003B, PJ_TLS_RSA_WITH_RC4_128_MD5 = 0x00000004, PJ_TLS_RSA_WITH_RC4_128_SHA = 0x00000005, PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x0000000A, PJ_TLS_RSA_WITH_AES_128_CBC_SHA = 0x0000002F, PJ_TLS_RSA_WITH_AES_256_CBC_SHA = 0x00000035, PJ_TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003C, PJ_TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x0000003D, PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x0000000D, PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000010, PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x00000013, PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000016, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x00000030, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x00000031, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x00000032, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x00000033, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x00000036, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x00000037, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x00000038, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x00000039, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x0000003E, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003F, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x00000040, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x00000067, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x00000068, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x00000069, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x0000006A, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x0000006B, PJ_TLS_DH_anon_WITH_RC4_128_MD5 = 0x00000018, PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x0000001B, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x00000034, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x0000003A, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D, PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003, PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006, PJ_TLS_RSA_WITH_IDEA_CBC_SHA = 0x00000007, PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000008, PJ_TLS_RSA_WITH_DES_CBC_SHA = 0x00000009, PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0000000B, PJ_TLS_DH_DSS_WITH_DES_CBC_SHA = 0x0000000C, PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0000000E, PJ_TLS_DH_RSA_WITH_DES_CBC_SHA = 0x0000000F, PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x00000011, PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x00000012, PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000014, PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x00000015, PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x00000017, PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x00000019, PJ_TLS_DH_anon_WITH_DES_CBC_SHA = 0x0000001A, PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x0000001C, PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA = 0x0000001D, PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x0000001E, PJ_SSL_CK_RC4_128_WITH_MD5 = 0x00010080, PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x00020080, PJ_SSL_CK_RC2_128_CBC_WITH_MD5 = 0x00030080, PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x00040080, PJ_SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x00050080, PJ_SSL_CK_DES_64_CBC_WITH_MD5 = 0x00060040, PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x000700C0} pj_ssl_cipher;
+
+typedef enum pj_stun_nat_type {PJ_STUN_NAT_TYPE_UNKNOWN, PJ_STUN_NAT_TYPE_ERR_UNKNOWN, PJ_STUN_NAT_TYPE_OPEN, PJ_STUN_NAT_TYPE_BLOCKED, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP, PJ_STUN_NAT_TYPE_FULL_CONE, PJ_STUN_NAT_TYPE_SYMMETRIC, PJ_STUN_NAT_TYPE_RESTRICTED, PJ_STUN_NAT_TYPE_PORT_RESTRICTED} pj_stun_nat_type;
+
+typedef enum pj_turn_tp_type {PJ_TURN_TP_UDP = 17, PJ_TURN_TP_TCP = 6, PJ_TURN_TP_TLS = 255} pj_turn_tp_type;
+
+typedef enum pjmedia_event_type {PJMEDIA_EVENT_NONE, PJMEDIA_EVENT_FMT_CHANGED = ((('H' << 24) | ('C' << 16)) | ('M' << 8)) | 'F', PJMEDIA_EVENT_WND_CLOSING = ((('L' << 24) | ('C' << 16)) | ('N' << 8)) | 'W', PJMEDIA_EVENT_WND_CLOSED = ((('O' << 24) | ('C' << 16)) | ('N' << 8)) | 'W', PJMEDIA_EVENT_WND_RESIZED = ((('Z' << 24) | ('R' << 16)) | ('N' << 8)) | 'W', PJMEDIA_EVENT_MOUSE_BTN_DOWN = ((('N' << 24) | ('D' << 16)) | ('S' << 8)) | 'M', PJMEDIA_EVENT_KEYFRAME_FOUND = ((('F' << 24) | ('R' << 16)) | ('F' << 8)) | 'I', PJMEDIA_EVENT_KEYFRAME_MISSING = ((('M' << 24) | ('R' << 16)) | ('F' << 8)) | 'I', PJMEDIA_EVENT_ORIENT_CHANGED = ((('T' << 24) | ('N' << 16)) | ('R' << 8)) | 'O'} pjmedia_event_type;
+
+typedef enum pjmedia_srtp_use {PJMEDIA_SRTP_DISABLED, PJMEDIA_SRTP_OPTIONAL, PJMEDIA_SRTP_MANDATORY} pjmedia_srtp_use;
+
+typedef enum pjmedia_vid_stream_rc_method {PJMEDIA_VID_STREAM_RC_NONE = 0, PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1} pjmedia_vid_stream_rc_method;
+
+typedef pj_int32_t pjmedia_vid_dev_index;
+
+enum pjmedia_vid_dev_std_index {PJMEDIA_VID_DEFAULT_CAPTURE_DEV = -1, PJMEDIA_VID_DEFAULT_RENDER_DEV = -2, PJMEDIA_VID_INVALID_DEV = -3};
+
+typedef enum pjmedia_aud_dev_route {PJMEDIA_AUD_DEV_ROUTE_DEFAULT = 0, PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER = 1, PJMEDIA_AUD_DEV_ROUTE_EARPIECE = 2, PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH = 4} pjmedia_aud_dev_route;
+
+typedef enum pjmedia_aud_dev_cap {PJMEDIA_AUD_DEV_CAP_EXT_FORMAT = 1, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY = 2, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY = 4, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING = 8, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING = 16, PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER = 32, PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER = 64, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE = 128, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE = 256, PJMEDIA_AUD_DEV_CAP_EC = 512, PJMEDIA_AUD_DEV_CAP_EC_TAIL = 1024, PJMEDIA_AUD_DEV_CAP_VAD = 2048, PJMEDIA_AUD_DEV_CAP_CNG = 4096, PJMEDIA_AUD_DEV_CAP_PLC = 8192, PJMEDIA_AUD_DEV_CAP_MAX = 16384} pjmedia_aud_dev_cap;
+
+enum pjmedia_file_writer_option {PJMEDIA_FILE_WRITE_PCM = 0, PJMEDIA_FILE_WRITE_ALAW = 1, PJMEDIA_FILE_WRITE_ULAW = 2};
+
+enum pjmedia_file_player_option {PJMEDIA_FILE_NO_LOOP = 1};
+
+typedef enum pjmedia_type {PJMEDIA_TYPE_NONE, PJMEDIA_TYPE_AUDIO, PJMEDIA_TYPE_VIDEO, PJMEDIA_TYPE_APPLICATION, PJMEDIA_TYPE_UNKNOWN} pjmedia_type;
+
+typedef enum pjmedia_dir {PJMEDIA_DIR_NONE = 0, PJMEDIA_DIR_ENCODING = 1, PJMEDIA_DIR_CAPTURE = PJMEDIA_DIR_ENCODING, PJMEDIA_DIR_DECODING = 2, PJMEDIA_DIR_PLAYBACK = PJMEDIA_DIR_DECODING, PJMEDIA_DIR_RENDER = PJMEDIA_DIR_DECODING, PJMEDIA_DIR_ENCODING_DECODING = 3, PJMEDIA_DIR_CAPTURE_PLAYBACK = PJMEDIA_DIR_ENCODING_DECODING, PJMEDIA_DIR_CAPTURE_RENDER = PJMEDIA_DIR_ENCODING_DECODING} pjmedia_dir;
+
+typedef enum pjmedia_tp_proto {PJMEDIA_TP_PROTO_NONE = 0, PJMEDIA_TP_PROTO_RTP_AVP, PJMEDIA_TP_PROTO_RTP_SAVP, PJMEDIA_TP_PROTO_UNKNOWN} pjmedia_tp_proto;
+
+typedef enum pjmedia_format_id {PJMEDIA_FORMAT_L16 = 0, PJMEDIA_FORMAT_PCM = PJMEDIA_FORMAT_L16, PJMEDIA_FORMAT_PCMA = ((('W' << 24) | ('A' << 16)) | ('L' << 8)) | 'A', PJMEDIA_FORMAT_ALAW = PJMEDIA_FORMAT_PCMA, PJMEDIA_FORMAT_PCMU = ((('W' << 24) | ('A' << 16)) | ('L' << 8)) | 'u', PJMEDIA_FORMAT_ULAW = PJMEDIA_FORMAT_PCMU, PJMEDIA_FORMAT_AMR = ((('R' << 24) | ('M' << 16)) | ('A' << 8)) | ' ', PJMEDIA_FORMAT_G729 = ((('9' << 24) | ('2' << 16)) | ('7' << 8)) | 'G', PJMEDIA_FORMAT_ILBC = ((('C' << 24) | ('B' << 16)) | ('L' << 8)) | 'I', PJMEDIA_FORMAT_RGB24 = ((('3' << 24) | ('B' << 16)) | ('G' << 8)) | 'R', PJMEDIA_FORMAT_RGBA = ((('A' << 24) | ('B' << 16)) | ('G' << 8)) | 'R', PJMEDIA_FORMAT_BGRA = ((('A' << 24) | ('R' << 16)) | ('G' << 8)) | 'B', PJMEDIA_FORMAT_RGB32 = PJMEDIA_FORMAT_RGBA, PJMEDIA_FORMAT_DIB = (((' ' << 24) | ('B' << 16)) | ('I' << 8)) | 'D', PJMEDIA_FORMAT_GBRP = ((('P' << 24) | ('R' << 16)) | ('B' << 8)) | 'G', PJMEDIA_FORMAT_AYUV = ((('V' << 24) | ('U' << 16)) | ('Y' << 8)) | 'A', PJMEDIA_FORMAT_YUY2 = ((('2' << 24) | ('Y' << 16)) | ('U' << 8)) | 'Y', PJMEDIA_FORMAT_UYVY = ((('Y' << 24) | ('V' << 16)) | ('Y' << 8)) | 'U', PJMEDIA_FORMAT_YVYU = ((('U' << 24) | ('Y' << 16)) | ('V' << 8)) | 'Y', PJMEDIA_FORMAT_I420 = ((('0' << 24) | ('2' << 16)) | ('4' << 8)) | 'I', PJMEDIA_FORMAT_IYUV = PJMEDIA_FORMAT_I420, PJMEDIA_FORMAT_YV12 = ((('2' << 24) | ('1' << 16)) | ('V' << 8)) | 'Y', PJMEDIA_FORMAT_I422 = ((('2' << 24) | ('2' << 16)) | ('4' << 8)) | 'I', PJMEDIA_FORMAT_I420JPEG = ((('0' << 24) | ('2' << 16)) | ('4' << 8)) | 'J', PJMEDIA_FORMAT_I422JPEG = ((('2' << 24) | ('2' << 16)) | ('4' << 8)) | 'J', PJMEDIA_FORMAT_H261 = ((('1' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', PJMEDIA_FORMAT_H263 = ((('3' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', PJMEDIA_FORMAT_H263P = ((('3' << 24) | ('6' << 16)) | ('2' << 8)) | 'P', PJMEDIA_FORMAT_H264 = ((('4' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', PJMEDIA_FORMAT_MJPEG = ((('G' << 24) | ('P' << 16)) | ('J' << 8)) | 'M', PJMEDIA_FORMAT_MPEG1VIDEO = ((('V' << 24) | ('1' << 16)) | ('P' << 8)) | 'M', PJMEDIA_FORMAT_MPEG2VIDEO = ((('V' << 24) | ('2' << 16)) | ('P' << 8)) | 'M', PJMEDIA_FORMAT_MPEG4 = ((('4' << 24) | ('G' << 16)) | ('P' << 8)) | 'M'} pjmedia_format_id;
+
+typedef enum pjsip_cred_data_type {PJSIP_CRED_DATA_PLAIN_PASSWD = 0, PJSIP_CRED_DATA_DIGEST = 1, PJSIP_CRED_DATA_EXT_AKA = 16} pjsip_cred_data_type;
+
+typedef enum pjsip_dialog_cap_status {PJSIP_DIALOG_CAP_UNSUPPORTED = 0, PJSIP_DIALOG_CAP_SUPPORTED = 1, PJSIP_DIALOG_CAP_UNKNOWN = 2} pjsip_dialog_cap_status;
+
+typedef enum pjsip_event_id_e {PJSIP_EVENT_UNKNOWN, PJSIP_EVENT_TIMER, PJSIP_EVENT_TX_MSG, PJSIP_EVENT_RX_MSG, PJSIP_EVENT_TRANSPORT_ERROR, PJSIP_EVENT_TSX_STATE, PJSIP_EVENT_USER} pjsip_event_id_e;
+
+typedef enum pjsip_status_code {PJSIP_SC_TRYING = 100, PJSIP_SC_RINGING = 180, PJSIP_SC_CALL_BEING_FORWARDED = 181, PJSIP_SC_QUEUED = 182, PJSIP_SC_PROGRESS = 183, PJSIP_SC_OK = 200, PJSIP_SC_ACCEPTED = 202, PJSIP_SC_MULTIPLE_CHOICES = 300, PJSIP_SC_MOVED_PERMANENTLY = 301, PJSIP_SC_MOVED_TEMPORARILY = 302, PJSIP_SC_USE_PROXY = 305, PJSIP_SC_ALTERNATIVE_SERVICE = 380, PJSIP_SC_BAD_REQUEST = 400, PJSIP_SC_UNAUTHORIZED = 401, PJSIP_SC_PAYMENT_REQUIRED = 402, PJSIP_SC_FORBIDDEN = 403, PJSIP_SC_NOT_FOUND = 404, PJSIP_SC_METHOD_NOT_ALLOWED = 405, PJSIP_SC_NOT_ACCEPTABLE = 406, PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407, PJSIP_SC_REQUEST_TIMEOUT = 408, PJSIP_SC_GONE = 410, PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413, PJSIP_SC_REQUEST_URI_TOO_LONG = 414, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415, PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416, PJSIP_SC_BAD_EXTENSION = 420, PJSIP_SC_EXTENSION_REQUIRED = 421, PJSIP_SC_SESSION_TIMER_TOO_SMALL = 422, PJSIP_SC_INTERVAL_TOO_BRIEF = 423, PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481, PJSIP_SC_LOOP_DETECTED = 482, PJSIP_SC_TOO_MANY_HOPS = 483, PJSIP_SC_ADDRESS_INCOMPLETE = 484, PJSIP_AC_AMBIGUOUS = 485, PJSIP_SC_BUSY_HERE = 486, PJSIP_SC_REQUEST_TERMINATED = 487, PJSIP_SC_NOT_ACCEPTABLE_HERE = 488, PJSIP_SC_BAD_EVENT = 489, PJSIP_SC_REQUEST_UPDATED = 490, PJSIP_SC_REQUEST_PENDING = 491, PJSIP_SC_UNDECIPHERABLE = 493, PJSIP_SC_INTERNAL_SERVER_ERROR = 500, PJSIP_SC_NOT_IMPLEMENTED = 501, PJSIP_SC_BAD_GATEWAY = 502, PJSIP_SC_SERVICE_UNAVAILABLE = 503, PJSIP_SC_SERVER_TIMEOUT = 504, PJSIP_SC_VERSION_NOT_SUPPORTED = 505, PJSIP_SC_MESSAGE_TOO_LARGE = 513, PJSIP_SC_PRECONDITION_FAILURE = 580, PJSIP_SC_BUSY_EVERYWHERE = 600, PJSIP_SC_DECLINE = 603, PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604, PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606, PJSIP_SC_TSX_TIMEOUT = PJSIP_SC_REQUEST_TIMEOUT, PJSIP_SC_TSX_TRANSPORT_ERROR = PJSIP_SC_SERVICE_UNAVAILABLE, PJSIP_SC__force_32bit = 0x7FFFFFFF} pjsip_status_code;
+
+typedef enum pjsip_hdr_e {PJSIP_H_ACCEPT, PJSIP_H_ACCEPT_ENCODING_UNIMP, PJSIP_H_ACCEPT_LANGUAGE_UNIMP, PJSIP_H_ALERT_INFO_UNIMP, PJSIP_H_ALLOW, PJSIP_H_AUTHENTICATION_INFO_UNIMP, PJSIP_H_AUTHORIZATION, PJSIP_H_CALL_ID, PJSIP_H_CALL_INFO_UNIMP, PJSIP_H_CONTACT, PJSIP_H_CONTENT_DISPOSITION_UNIMP, PJSIP_H_CONTENT_ENCODING_UNIMP, PJSIP_H_CONTENT_LANGUAGE_UNIMP, PJSIP_H_CONTENT_LENGTH, PJSIP_H_CONTENT_TYPE, PJSIP_H_CSEQ, PJSIP_H_DATE_UNIMP, PJSIP_H_ERROR_INFO_UNIMP, PJSIP_H_EXPIRES, PJSIP_H_FROM, PJSIP_H_IN_REPLY_TO_UNIMP, PJSIP_H_MAX_FORWARDS, PJSIP_H_MIME_VERSION_UNIMP, PJSIP_H_MIN_EXPIRES, PJSIP_H_ORGANIZATION_UNIMP, PJSIP_H_PRIORITY_UNIMP, PJSIP_H_PROXY_AUTHENTICATE, PJSIP_H_PROXY_AUTHORIZATION, PJSIP_H_PROXY_REQUIRE_UNIMP, PJSIP_H_RECORD_ROUTE, PJSIP_H_REPLY_TO_UNIMP, PJSIP_H_REQUIRE, PJSIP_H_RETRY_AFTER, PJSIP_H_ROUTE, PJSIP_H_SERVER_UNIMP, PJSIP_H_SUBJECT_UNIMP, PJSIP_H_SUPPORTED, PJSIP_H_TIMESTAMP_UNIMP, PJSIP_H_TO, PJSIP_H_UNSUPPORTED, PJSIP_H_USER_AGENT_UNIMP, PJSIP_H_VIA, PJSIP_H_WARNING_UNIMP, PJSIP_H_WWW_AUTHENTICATE, PJSIP_H_OTHER} pjsip_hdr_e;
+
+typedef enum pjsip_transport_type_e {PJSIP_TRANSPORT_UNSPECIFIED, PJSIP_TRANSPORT_UDP, PJSIP_TRANSPORT_TCP, PJSIP_TRANSPORT_TLS, PJSIP_TRANSPORT_SCTP, PJSIP_TRANSPORT_LOOP, PJSIP_TRANSPORT_LOOP_DGRAM, PJSIP_TRANSPORT_START_OTHER, PJSIP_TRANSPORT_IPV6 = 128, PJSIP_TRANSPORT_UDP6 = PJSIP_TRANSPORT_UDP + PJSIP_TRANSPORT_IPV6, PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6, PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6} pjsip_transport_type_e;
+
+enum pjsip_transport_flags_e {PJSIP_TRANSPORT_RELIABLE = 1, PJSIP_TRANSPORT_SECURE = 2, PJSIP_TRANSPORT_DATAGRAM = 4};
+
+typedef enum pjsip_transport_state {PJSIP_TP_STATE_CONNECTED, PJSIP_TP_STATE_DISCONNECTED} pjsip_transport_state;
+
+typedef enum pjsip_ssl_method {PJSIP_SSL_UNSPECIFIED_METHOD = 0, PJSIP_TLSV1_METHOD = 31, PJSIP_SSLV2_METHOD = 20, PJSIP_SSLV3_METHOD = 30, PJSIP_SSLV23_METHOD = 23} pjsip_ssl_method;
+
+typedef enum pjsip_tsx_state_e {PJSIP_TSX_STATE_NULL, PJSIP_TSX_STATE_CALLING, PJSIP_TSX_STATE_TRYING, PJSIP_TSX_STATE_PROCEEDING, PJSIP_TSX_STATE_COMPLETED, PJSIP_TSX_STATE_CONFIRMED, PJSIP_TSX_STATE_TERMINATED, PJSIP_TSX_STATE_DESTROYED, PJSIP_TSX_STATE_MAX} pjsip_tsx_state_e;
+
+typedef enum pjsip_role_e {PJSIP_ROLE_UAC, PJSIP_ROLE_UAS, PJSIP_UAC_ROLE = PJSIP_ROLE_UAC, PJSIP_UAS_ROLE = PJSIP_ROLE_UAS} pjsip_role_e;
+
+typedef enum pjsip_redirect_op {PJSIP_REDIRECT_REJECT, PJSIP_REDIRECT_ACCEPT, PJSIP_REDIRECT_ACCEPT_REPLACE, PJSIP_REDIRECT_PENDING, PJSIP_REDIRECT_STOP} pjsip_redirect_op;
+
+typedef enum pjrpid_activity {PJRPID_ACTIVITY_UNKNOWN, PJRPID_ACTIVITY_AWAY, PJRPID_ACTIVITY_BUSY} pjrpid_activity;
+
+typedef enum pjsip_evsub_state {PJSIP_EVSUB_STATE_NULL, PJSIP_EVSUB_STATE_SENT, PJSIP_EVSUB_STATE_ACCEPTED, PJSIP_EVSUB_STATE_PENDING, PJSIP_EVSUB_STATE_ACTIVE, PJSIP_EVSUB_STATE_TERMINATED, PJSIP_EVSUB_STATE_UNKNOWN} pjsip_evsub_state;
+
+typedef enum pjsip_inv_state {PJSIP_INV_STATE_NULL, PJSIP_INV_STATE_CALLING, PJSIP_INV_STATE_INCOMING, PJSIP_INV_STATE_EARLY, PJSIP_INV_STATE_CONNECTING, PJSIP_INV_STATE_CONFIRMED, PJSIP_INV_STATE_DISCONNECTED} pjsip_inv_state;
+
+enum pjsua_invalid_id_const_ {PJSUA_INVALID_ID = -1};
+
+typedef enum pjsua_state {PJSUA_STATE_NULL, PJSUA_STATE_CREATED, PJSUA_STATE_INIT, PJSUA_STATE_STARTING, PJSUA_STATE_RUNNING, PJSUA_STATE_CLOSING} pjsua_state;
+
+typedef enum pjsua_stun_use {PJSUA_STUN_USE_DEFAULT, PJSUA_STUN_USE_DISABLED} pjsua_stun_use;
+
+typedef enum pjsua_call_hold_type {PJSUA_CALL_HOLD_TYPE_RFC3264, PJSUA_CALL_HOLD_TYPE_RFC2543} pjsua_call_hold_type;
+
+typedef int pjsua_acc_id;
+
+typedef enum pjsua_destroy_flag {PJSUA_DESTROY_NO_RX_MSG = 1, PJSUA_DESTROY_NO_TX_MSG = 2, PJSUA_DESTROY_NO_NETWORK = PJSUA_DESTROY_NO_RX_MSG | PJSUA_DESTROY_NO_TX_MSG} pjsua_destroy_flag;
+
+typedef enum pjsua_100rel_use {PJSUA_100REL_NOT_USED, PJSUA_100REL_MANDATORY, PJSUA_100REL_OPTIONAL} pjsua_100rel_use;
+
+typedef enum pjsua_sip_timer_use {PJSUA_SIP_TIMER_INACTIVE, PJSUA_SIP_TIMER_OPTIONAL, PJSUA_SIP_TIMER_REQUIRED, PJSUA_SIP_TIMER_ALWAYS} pjsua_sip_timer_use;
+
+typedef enum pjsua_ipv6_use {PJSUA_IPV6_DISABLED, PJSUA_IPV6_ENABLED} pjsua_ipv6_use;
+
+typedef enum pjsua_buddy_status {PJSUA_BUDDY_STATUS_UNKNOWN, PJSUA_BUDDY_STATUS_ONLINE, PJSUA_BUDDY_STATUS_OFFLINE} pjsua_buddy_status;
+
+typedef enum pjsua_call_media_status {PJSUA_CALL_MEDIA_NONE, PJSUA_CALL_MEDIA_ACTIVE, PJSUA_CALL_MEDIA_LOCAL_HOLD, PJSUA_CALL_MEDIA_REMOTE_HOLD, PJSUA_CALL_MEDIA_ERROR} pjsua_call_media_status;
+
+typedef int pjsua_vid_win_id;
+
+typedef int pjsua_call_id;
+
+typedef enum pjsua_med_tp_st {PJSUA_MED_TP_NULL, PJSUA_MED_TP_CREATING, PJSUA_MED_TP_IDLE, PJSUA_MED_TP_INIT, PJSUA_MED_TP_RUNNING, PJSUA_MED_TP_DISABLED} pjsua_med_tp_st;
+
+typedef enum pjsua_call_vid_strm_op {PJSUA_CALL_VID_STRM_NO_OP, PJSUA_CALL_VID_STRM_ADD, PJSUA_CALL_VID_STRM_REMOVE, PJSUA_CALL_VID_STRM_CHANGE_DIR, PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV, PJSUA_CALL_VID_STRM_START_TRANSMIT, PJSUA_CALL_VID_STRM_STOP_TRANSMIT, PJSUA_CALL_VID_STRM_SEND_KEYFRAME} pjsua_call_vid_strm_op;
+
+typedef enum pjsua_vid_req_keyframe_method {PJSUA_VID_REQ_KEYFRAME_SIP_INFO = 1, PJSUA_VID_REQ_KEYFRAME_RTCP_PLI = 2} pjsua_vid_req_keyframe_method;
+
+typedef enum pjsua_call_flag {PJSUA_CALL_UNHOLD = 1, PJSUA_CALL_UPDATE_CONTACT = 2, PJSUA_CALL_INCLUDE_DISABLED_MEDIA = 4} pjsua_call_flag;
+
+typedef enum pjsua_create_media_transport_flag {PJSUA_MED_TP_CLOSE_MEMBER = 1} pjsua_create_media_transport_flag;
+
diff --git a/pjsip-apps/src/swig/symbols.lst b/pjsip-apps/src/swig/symbols.lst
new file mode 100644
index 00000000..685c3713
--- /dev/null
+++ b/pjsip-apps/src/swig/symbols.lst
@@ -0,0 +1,34 @@
+pj/types.h pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint16_t
+pj/file_io.h pj_file_access
+pj/log.h pj_log_decoration
+pj/sock_qos.h pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params
+pj/ssl_sock.h pj_ssl_cipher
+
+pjnath/nat_detect.h pj_stun_nat_type
+pjnath/turn_session.h pj_turn_tp_type
+
+pjmedia/event.h pjmedia_event_type
+pjmedia/transport_srtp.h pjmedia_srtp_use
+pjmedia/vid_stream.h pjmedia_vid_stream_rc_method
+pjmedia-videodev/videodev.h pjmedia_vid_dev_index pjmedia_vid_dev_std_index
+pjmedia-audiodev/audiodev.h pjmedia_aud_dev_route pjmedia_aud_dev_cap
+pjmedia/wav_port.h pjmedia_file_writer_option pjmedia_file_player_option
+pjmedia/types.h pjmedia_type pjmedia_dir pjmedia_tp_proto
+pjmedia/format.h pjmedia_format_id
+
+pjsip/sip_auth.h pjsip_cred_data_type
+pjsip/sip_dialog.h pjsip_dialog_cap_status
+pjsip/sip_event.h pjsip_event_id_e
+pjsip/sip_msg.h pjsip_status_code pjsip_hdr_e
+pjsip/sip_transport.h pjsip_transport_type_e pjsip_transport_flags_e pjsip_transport_state
+pjsip/sip_transport_tls.h pjsip_ssl_method
+pjsip/sip_transaction.h pjsip_tsx_state_e
+pjsip/sip_types.h pjsip_role_e
+pjsip/sip_util.h pjsip_redirect_op
+
+pjsip-simple/rpid.h pjrpid_activity
+pjsip-simple/evsub.h pjsip_evsub_state
+
+pjsip-ua/sip_inv.h pjsip_inv_state
+
+pjsua-lib/pjsua.h pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag