diff --git a/android/kotlin/AccountLogin/.gitignore b/android/kotlin/AccountLogin/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/android/kotlin/AccountLogin/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/android/kotlin/AccountLogin/.idea/$CACHE_FILE$ b/android/kotlin/AccountLogin/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..7da4bdc
--- /dev/null
+++ b/android/kotlin/AccountLogin/.idea/$CACHE_FILE$
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ Android
+
+
+ CorrectnessLintAndroid
+
+
+ Gradle
+
+
+ LintAndroid
+
+
+ Probable bugsGradle
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AccountLogin/.idea/.gitignore b/android/kotlin/AccountLogin/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/android/kotlin/AccountLogin/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/android/kotlin/AccountLogin/.idea/.name b/android/kotlin/AccountLogin/.idea/.name
new file mode 100644
index 0000000..9364b54
--- /dev/null
+++ b/android/kotlin/AccountLogin/.idea/.name
@@ -0,0 +1 @@
+Account Login
\ No newline at end of file
diff --git a/android/kotlin/AccountLogin/.idea/compiler.xml b/android/kotlin/AccountLogin/.idea/compiler.xml
new file mode 100644
index 0000000..61a9130
--- /dev/null
+++ b/android/kotlin/AccountLogin/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AccountLogin/.idea/gradle.xml b/android/kotlin/AccountLogin/.idea/gradle.xml
new file mode 100644
index 0000000..cd8ea57
--- /dev/null
+++ b/android/kotlin/AccountLogin/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java/.idea/jarRepositories.xml b/android/kotlin/AccountLogin/.idea/jarRepositories.xml
similarity index 83%
rename from java/.idea/jarRepositories.xml
rename to android/kotlin/AccountLogin/.idea/jarRepositories.xml
index a5f05cd..d9f0af1 100644
--- a/java/.idea/jarRepositories.xml
+++ b/android/kotlin/AccountLogin/.idea/jarRepositories.xml
@@ -21,5 +21,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/java/.idea/misc.xml b/android/kotlin/AccountLogin/.idea/misc.xml
similarity index 79%
rename from java/.idea/misc.xml
rename to android/kotlin/AccountLogin/.idea/misc.xml
index 37a7509..19aa6a5 100644
--- a/java/.idea/misc.xml
+++ b/android/kotlin/AccountLogin/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/java/.idea/vcs.xml b/android/kotlin/AccountLogin/.idea/vcs.xml
similarity index 67%
rename from java/.idea/vcs.xml
rename to android/kotlin/AccountLogin/.idea/vcs.xml
index 6c0b863..c2365ab 100644
--- a/java/.idea/vcs.xml
+++ b/android/kotlin/AccountLogin/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/java/app/.gitignore b/android/kotlin/AccountLogin/app/.gitignore
similarity index 100%
rename from java/app/.gitignore
rename to android/kotlin/AccountLogin/app/.gitignore
diff --git a/android/kotlin/AccountLogin/app/build.gradle b/android/kotlin/AccountLogin/app/build.gradle
new file mode 100644
index 0000000..6f7dcae
--- /dev/null
+++ b/android/kotlin/AccountLogin/app/build.gradle
@@ -0,0 +1,50 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "org.linphone.accountlogin"
+ minSdkVersion 23
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+// We need to declare this repository to be able to use Liblinphone SDK
+repositories {
+ maven {
+ url "https://linphone.org/maven_repository"
+ }
+}
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.2.1'
+ // Latest version is 4.5.x, using + to get the latest available
+ implementation 'org.linphone:linphone-sdk-android:4.5+'
+}
\ No newline at end of file
diff --git a/java/app/proguard-rules.pro b/android/kotlin/AccountLogin/app/proguard-rules.pro
similarity index 100%
rename from java/app/proguard-rules.pro
rename to android/kotlin/AccountLogin/app/proguard-rules.pro
diff --git a/android/kotlin/AccountLogin/app/src/main/AndroidManifest.xml b/android/kotlin/AccountLogin/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..bb00966
--- /dev/null
+++ b/android/kotlin/AccountLogin/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AccountLogin/app/src/main/java/org/linphone/accountlogin/AccountLoginActivity.kt b/android/kotlin/AccountLogin/app/src/main/java/org/linphone/accountlogin/AccountLoginActivity.kt
new file mode 100644
index 0000000..7cb8782
--- /dev/null
+++ b/android/kotlin/AccountLogin/app/src/main/java/org/linphone/accountlogin/AccountLoginActivity.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+package org.linphone.accountlogin
+
+import android.os.Bundle
+import android.widget.Button
+import android.widget.EditText
+import android.widget.RadioGroup
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import org.linphone.core.*
+
+class AccountLoginActivity: AppCompatActivity() {
+ private lateinit var core: Core
+
+ // Create a Core listener to listen for the callback we need
+ // In this case, we want to know about the account registration status
+ private val coreListener = object: CoreListenerStub() {
+ override fun onRegistrationStateChanged(
+ core: Core,
+ proxyConfig: ProxyConfig,
+ state: RegistrationState?,
+ message: String
+ ) {
+ // If account has been configured correctly, we will go through InProgress and Registered states
+ // Otherwise, we will be Failed.
+ findViewById(R.id.registration_status).text = message
+
+ if (state == RegistrationState.Failed) {
+ findViewById(R.id.connect).isEnabled = true
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.account_login_activity)
+
+ val factory = Factory.instance()
+ factory.setDebugMode(true, "Hello Linphone")
+ core = factory.createCore(null, null, this)
+
+ findViewById(R.id.connect).setOnClickListener {
+ login()
+ it.isEnabled = false
+ }
+
+ val coreVersion = findViewById(R.id.core_version)
+ coreVersion.text = core.version
+ }
+
+ private fun login() {
+ val username = findViewById(R.id.username).text.toString()
+ val password = findViewById(R.id.password).text.toString()
+ val domain = findViewById(R.id.domain).text.toString()
+ // Get the transport protocol to use.
+ // TLS is strongly recommended
+ // Only use UDP if you don't have the choice
+ val transportType = when (findViewById(R.id.transport).checkedRadioButtonId) {
+ R.id.udp -> TransportType.Udp
+ R.id.tcp -> TransportType.Tcp
+ else -> TransportType.Tls
+ }
+
+ // To create an account, we need a ProxyConfig object and an AuthInfo object
+ // The first one is how to connect to the proxy server, the second one stores the credentials
+
+ // The auth info can be created from the Factory as it's only a data class
+ // userID is set to null as it's the same as the username in our case
+ // ha1 is set to null as we are using the clear text password. Upon first register, the hash will be computed automatically.
+ // The realm will be determined automatically from the first register, as well as the algorithm
+ val authInfo = Factory.instance().createAuthInfo(username, null, password, null, null, domain, null)
+
+ // Proxy config object depends on the Core so we can't create it using the Factory
+ val proxyConfig = core.createProxyConfig()
+ // Proxy config needs an identity address that we can construct from the username and domain
+ val identity = Factory.instance().createAddress("sip:$username@$domain")
+ proxyConfig.identityAddress = identity
+
+ // We also need to configure where the proxy server is located
+ val address = Factory.instance().createAddress("sip:$domain")
+ // We use the Address object to easily set the transport protocol
+ address?.transport = transportType
+ proxyConfig.serverAddr = address?.asStringUriOnly()
+ // And we ensure the account will start the registration process
+ proxyConfig.enableRegister(true)
+
+ // Now let's add our objects to the Core
+ core.addAuthInfo(authInfo)
+ core.addProxyConfig(proxyConfig)
+
+ // Also set the newly added account as default
+ core.defaultProxyConfig = proxyConfig
+
+ // To be notified of the connection status of our account, we need to add the listener to the Core
+ core.addListener(coreListener)
+
+ // Finally we need the Core to be started for the registration to happen (it could have been started before)
+ core.start()
+ }
+}
\ No newline at end of file
diff --git a/java/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/kotlin/AccountLogin/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
similarity index 100%
rename from java/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
rename to android/kotlin/AccountLogin/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
diff --git a/java/app/src/main/res/drawable/ic_launcher_background.xml b/android/kotlin/AccountLogin/app/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from java/app/src/main/res/drawable/ic_launcher_background.xml
rename to android/kotlin/AccountLogin/app/src/main/res/drawable/ic_launcher_background.xml
diff --git a/android/kotlin/AccountLogin/app/src/main/res/layout/account_login_activity.xml b/android/kotlin/AccountLogin/app/src/main/res/layout/account_login_activity.xml
new file mode 100644
index 0000000..88328fb
--- /dev/null
+++ b/android/kotlin/AccountLogin/app/src/main/res/layout/account_login_activity.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/kotlin/AccountLogin/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 100%
rename from java/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to android/kotlin/AccountLogin/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/java/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/kotlin/AccountLogin/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 100%
rename from java/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to android/kotlin/AccountLogin/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
diff --git a/java/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from java/app/src/main/res/mipmap-hdpi/ic_launcher.png
rename to android/kotlin/AccountLogin/app/src/main/res/mipmap-hdpi/ic_launcher.png
diff --git a/java/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
similarity index 100%
rename from java/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
rename to android/kotlin/AccountLogin/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
diff --git a/android/kotlin/AccountLogin/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/android/kotlin/AccountLogin/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/kotlin/AccountLogin/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/android/kotlin/AccountLogin/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AccountLogin/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/kotlin/AccountLogin/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/android/kotlin/AccountLogin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AccountLogin/app/src/main/res/values-night/themes.xml b/android/kotlin/AccountLogin/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..3b213ab
--- /dev/null
+++ b/android/kotlin/AccountLogin/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AccountLogin/app/src/main/res/values/colors.xml b/android/kotlin/AccountLogin/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/android/kotlin/AccountLogin/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/android/kotlin/AccountLogin/app/src/main/res/values/strings.xml b/android/kotlin/AccountLogin/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b5e04a5
--- /dev/null
+++ b/android/kotlin/AccountLogin/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Account Login
+
\ No newline at end of file
diff --git a/android/kotlin/AccountLogin/app/src/main/res/values/themes.xml b/android/kotlin/AccountLogin/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..5e318eb
--- /dev/null
+++ b/android/kotlin/AccountLogin/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/java/build.gradle b/android/kotlin/AccountLogin/build.gradle
similarity index 73%
rename from java/build.gradle
rename to android/kotlin/AccountLogin/build.gradle
index da807a2..6534ac0 100644
--- a/java/build.gradle
+++ b/android/kotlin/AccountLogin/build.gradle
@@ -1,11 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.kotlin_version = "1.4.21"
repositories {
google()
jcenter()
}
dependencies {
- classpath "com.android.tools.build:gradle:4.0.0"
+ classpath "com.android.tools.build:gradle:4.1.1"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/java/gradle.properties b/android/kotlin/AccountLogin/gradle.properties
similarity index 85%
rename from java/gradle.properties
rename to android/kotlin/AccountLogin/gradle.properties
index c52ac9b..98bed16 100644
--- a/java/gradle.properties
+++ b/android/kotlin/AccountLogin/gradle.properties
@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx2048m
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
@@ -16,4 +16,6 @@ org.gradle.jvmargs=-Xmx2048m
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
\ No newline at end of file
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/java/gradle/wrapper/gradle-wrapper.jar b/android/kotlin/AccountLogin/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from java/gradle/wrapper/gradle-wrapper.jar
rename to android/kotlin/AccountLogin/gradle/wrapper/gradle-wrapper.jar
diff --git a/java/gradle/wrapper/gradle-wrapper.properties b/android/kotlin/AccountLogin/gradle/wrapper/gradle-wrapper.properties
similarity index 79%
rename from java/gradle/wrapper/gradle-wrapper.properties
rename to android/kotlin/AccountLogin/gradle/wrapper/gradle-wrapper.properties
index 94ac621..bc46a1b 100644
--- a/java/gradle/wrapper/gradle-wrapper.properties
+++ b/android/kotlin/AccountLogin/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Jun 04 10:43:14 CEST 2020
+#Mon Jan 18 10:29:47 CET 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/java/gradlew b/android/kotlin/AccountLogin/gradlew
similarity index 100%
rename from java/gradlew
rename to android/kotlin/AccountLogin/gradlew
diff --git a/java/gradlew.bat b/android/kotlin/AccountLogin/gradlew.bat
similarity index 100%
rename from java/gradlew.bat
rename to android/kotlin/AccountLogin/gradlew.bat
diff --git a/android/kotlin/AccountLogin/settings.gradle b/android/kotlin/AccountLogin/settings.gradle
new file mode 100644
index 0000000..1f17b14
--- /dev/null
+++ b/android/kotlin/AccountLogin/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Account Login"
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/.gitignore b/android/kotlin/AdvancedChat/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/android/kotlin/AdvancedChat/.idea/$CACHE_FILE$ b/android/kotlin/AdvancedChat/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..7da4bdc
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.idea/$CACHE_FILE$
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ Android
+
+
+ CorrectnessLintAndroid
+
+
+ Gradle
+
+
+ LintAndroid
+
+
+ Probable bugsGradle
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/.idea/.gitignore b/android/kotlin/AdvancedChat/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/android/kotlin/AdvancedChat/.idea/.name b/android/kotlin/AdvancedChat/.idea/.name
new file mode 100644
index 0000000..5f28c14
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.idea/.name
@@ -0,0 +1 @@
+Advanced Chat
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/.idea/compiler.xml b/android/kotlin/AdvancedChat/.idea/compiler.xml
new file mode 100644
index 0000000..61a9130
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/.idea/gradle.xml b/android/kotlin/AdvancedChat/.idea/gradle.xml
new file mode 100644
index 0000000..cd8ea57
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/.idea/jarRepositories.xml b/android/kotlin/AdvancedChat/.idea/jarRepositories.xml
new file mode 100644
index 0000000..d9f0af1
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/.idea/misc.xml b/android/kotlin/AdvancedChat/.idea/misc.xml
new file mode 100644
index 0000000..19aa6a5
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/.idea/vcs.xml b/android/kotlin/AdvancedChat/.idea/vcs.xml
new file mode 100644
index 0000000..c2365ab
--- /dev/null
+++ b/android/kotlin/AdvancedChat/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/.gitignore b/android/kotlin/AdvancedChat/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/build.gradle b/android/kotlin/AdvancedChat/app/build.gradle
new file mode 100644
index 0000000..758198a
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/build.gradle
@@ -0,0 +1,50 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "org.linphone.advancedchat"
+ minSdkVersion 23
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+// We need to declare this repository to be able to use Liblinphone SDK
+repositories {
+ maven {
+ url "https://linphone.org/maven_repository"
+ }
+}
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.2.1'
+ // Latest version is 4.5.x, using + to get the latest available
+ implementation 'org.linphone:linphone-sdk-android:4.5+'
+}
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/proguard-rules.pro b/android/kotlin/AdvancedChat/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/AndroidManifest.xml b/android/kotlin/AdvancedChat/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..bc81e9c
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/java/org/linphone/advancedchat/AdvancedChatActivity.kt b/android/kotlin/AdvancedChat/app/src/main/java/org/linphone/advancedchat/AdvancedChatActivity.kt
new file mode 100644
index 0000000..ca6c57a
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/java/org/linphone/advancedchat/AdvancedChatActivity.kt
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+package org.linphone.advancedchat
+
+import android.graphics.BitmapFactory
+import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.widget.*
+import androidx.appcompat.app.AppCompatActivity
+import org.linphone.core.*
+import java.io.File
+
+class AdvancedChatActivity: AppCompatActivity() {
+ private lateinit var core: Core
+ private var chatRoom: ChatRoom? = null
+
+ private val coreListener = object: CoreListenerStub() {
+ override fun onRegistrationStateChanged(
+ core: Core,
+ proxyConfig: ProxyConfig,
+ state: RegistrationState?,
+ message: String
+ ) {
+ findViewById(R.id.registration_status).text = message
+
+ if (state == RegistrationState.Failed) {
+ core.clearAllAuthInfo()
+ core.clearProxyConfig()
+ findViewById(R.id.connect).isEnabled = true
+ } else if (state == RegistrationState.Ok) {
+ findViewById(R.id.register_layout).visibility = View.GONE
+ findViewById(R.id.chat_layout).visibility = View.VISIBLE
+ }
+ }
+
+ override fun onMessageReceived(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
+ if (this@AdvancedChatActivity.chatRoom == null) {
+ // Check it is an one-to-one encrypted chat room
+ if (chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt()) &&
+ chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())) {
+ // Keep the chatRoom object to use it to send messages if it hasn't been created yet
+ this@AdvancedChatActivity.chatRoom = chatRoom
+ chatRoom.addListener(chatRoomListener)
+ enableEphemeral()
+
+ findViewById(R.id.remote_address).setText(chatRoom.participants.firstOrNull()?.address?.asStringUriOnly())
+ findViewById(R.id.remote_address).isEnabled = false
+ findViewById(R.id.send_message).isEnabled = true
+ }
+ }
+
+ // We will notify the sender the message has been read by us
+ chatRoom.markAsRead()
+ addMessageToHistory(message)
+ }
+ }
+
+ private val chatRoomListener = object: ChatRoomListenerStub() {
+ override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State?) {
+ if (newState == ChatRoom.State.Created) {
+ findViewById(R.id.send_message).isEnabled = true
+ enableEphemeral()
+ }
+ }
+
+ override fun onEphemeralEvent(chatRoom: ChatRoom, eventLog: EventLog) {
+ // This event is generated when the chat room ephemeral settings are being changed
+ }
+
+ override fun onEphemeralMessageDeleted(chatRoom: ChatRoom, eventLog: EventLog) {
+ // This is called when a message has expired and we should remove it from the view
+ val message = eventLog.chatMessage
+ val messageView = message?.userData as? View
+ findViewById(R.id.messages).removeView(messageView)
+ }
+
+ override fun onEphemeralMessageTimerStarted(chatRoom: ChatRoom, eventLog: EventLog) {
+ // This is called when a message has been read by all recipient, so the timer has started
+ val message = eventLog.chatMessage
+ val messageView = message?.userData as? View
+ messageView?.setBackgroundColor(getColor(R.color.purple_500))
+ }
+ }
+
+ private val chatMessageListener = object: ChatMessageListenerStub() {
+ override fun onMsgStateChanged(message: ChatMessage, state: ChatMessage.State?) {
+ val messageView = message.userData as? View
+ when (state) {
+ ChatMessage.State.InProgress -> {
+ messageView?.setBackgroundColor(getColor(R.color.yellow))
+ }
+ ChatMessage.State.Delivered -> {
+ // The proxy server has acknowledged the message with a 200 OK
+ messageView?.setBackgroundColor(getColor(R.color.orange))
+ }
+ ChatMessage.State.DeliveredToUser -> {
+ // User as received it
+ messageView?.setBackgroundColor(getColor(R.color.blue))
+ }
+ ChatMessage.State.Displayed -> {
+ // User as read it (client called chatRoom.markAsRead()
+ messageView?.setBackgroundColor(getColor(R.color.green))
+ }
+ ChatMessage.State.NotDelivered -> {
+ // User might be invalid or not registered
+ messageView?.setBackgroundColor(getColor(R.color.red))
+ }
+ ChatMessage.State.FileTransferDone -> {
+ // We finished uploading/downloading the file
+ if (!message.isOutgoing) {
+ findViewById(R.id.messages).removeView(messageView)
+ addMessageToHistory(message)
+ }
+ }
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.advanced_chat_activity)
+
+ val factory = Factory.instance()
+ factory.setDebugMode(true, "Hello Linphone")
+
+ Factory.instance().setLogCollectionPath(filesDir.absolutePath)
+ factory.enableLogCollection(LogCollectionState.Enabled)
+
+ // Delete previous databases if any
+ // If not done, will mess when connecting with the same account as before
+ File("${filesDir.absoluteFile}/linphone.db").delete()
+ File("${filesDir.absoluteFile}/x3dh.c25519.sqlite3").delete()
+ File("${filesDir.absoluteFile}/zrtp-secrets.db").delete()
+
+ core = factory.createCore(null, null, this)
+
+ findViewById(R.id.connect).setOnClickListener {
+ login()
+ it.isEnabled = false
+ }
+
+ findViewById(R.id.create_chat_room).setOnClickListener {
+ it.isEnabled = false
+ createFlexisipChatRoom()
+ }
+
+ findViewById(R.id.send_message).setOnClickListener {
+ sendMessage()
+ }
+ findViewById(R.id.send_message).isEnabled = false
+ }
+
+ private fun login() {
+ val username = findViewById(R.id.username).text.toString()
+ val password = findViewById(R.id.password).text.toString()
+ val domain = findViewById(R.id.domain).text.toString()
+ val transportType = when (findViewById(R.id.transport).checkedRadioButtonId) {
+ R.id.udp -> TransportType.Udp
+ R.id.tcp -> TransportType.Tcp
+ else -> TransportType.Tls
+ }
+ val authInfo = Factory.instance().createAuthInfo(username, null, password, null, null, domain, null)
+
+ val proxyConfig = core.createProxyConfig()
+ val identity = Factory.instance().createAddress("sip:$username@$domain")
+ proxyConfig.identityAddress = identity
+
+ val address = Factory.instance().createAddress("sip:$domain")
+ address?.transport = transportType
+ proxyConfig.serverAddr = address?.asStringUriOnly()
+ proxyConfig.enableRegister(true)
+
+ // We need a conference factory URI set on the proxy config to be able to create chat rooms with flexisip backend
+ proxyConfig.conferenceFactoryUri = "sip:conference-factory@sip.linphone.org"
+
+ core.addAuthInfo(authInfo)
+ core.addProxyConfig(proxyConfig)
+
+ // We also need a LIME X3DH server URL configured for end to end encryption
+ core.limeX3DhServerUrl = "https://lime.linphone.org/lime-server/lime-server.php"
+
+ core.defaultProxyConfig = proxyConfig
+ core.addListener(coreListener)
+ core.start()
+ }
+
+ private fun createFlexisipChatRoom() {
+ // In this tutorial we will create a Flexisip one-to-one chat room with end-to-end encryption
+ // For it to work, the proxy server we connect to must be an instance of Flexisip
+ // And we must have configured on the ProxyConfig a conference-factory URI
+ val params = core.createDefaultChatRoomParams()
+
+ // We won't create a group chat, only a 1-1 with advanced features such as end-to-end encryption
+ params.backend = ChatRoomBackend.FlexisipChat
+ params.enableGroup(false)
+
+ // We will rely on LIME encryption backend (we must have configured the core.limex3dhServerUrl first)
+ params.enableEncryption(true)
+ params.encryptionBackend = ChatRoomEncryptionBackend.Lime
+
+ // A flexisip chat room must have a subject
+ // But as we are doing a 1-1 chat room here we won't display it, so we can set whatever we want
+ params.subject = "dummy subject"
+
+ if (params.isValid) {
+ // We also need the SIP address of the person we will chat with
+ val remoteSipUri = findViewById(R.id.remote_address).text.toString()
+ val remoteAddress = Factory.instance().createAddress(remoteSipUri)
+
+ if (remoteAddress != null) {
+ // And finally we will need our local SIP address
+ val localAddress = core.defaultProxyConfig?.identityAddress
+ val room = core.createChatRoom(params, localAddress, arrayOf(remoteAddress))
+ if (room != null) {
+ // If chat room isn't created yet, wait for it to go in state Created
+ // as Flexisip chat room creation process is asynchronous
+ room.addListener(chatRoomListener)
+ chatRoom = room
+ findViewById(R.id.remote_address).isEnabled = false
+
+ // Chat room may already be created (for example if you logged in with an account for which the chat room already exists)
+ if (room.state == ChatRoom.State.Created) {
+ findViewById(R.id.send_message).isEnabled = true
+ enableEphemeral()
+ }
+ }
+ }
+ }
+ }
+
+ private fun enableEphemeral() {
+ // Once chat room has been created, we can enable ephemeral feature
+ // We enable ephemeral messages at the chat room level
+ // Please note this only affects messages we send, not the ones we receive
+ chatRoom?.enableEphemeral(true)
+ // Here we ask for a lifetime of 60 seconds, starting the moment the message has been read
+ chatRoom?.ephemeralLifetime = 60
+ }
+
+ private fun sendMessage() {
+ val message = findViewById(R.id.message).text.toString()
+ // We need to create a ChatMessage object using the ChatRoom
+ val chatMessage = chatRoom!!.createMessageFromUtf8(message)
+
+ // Then we can send it, progress will be notified using the onMsgStateChanged callback
+ chatMessage.addListener(chatMessageListener)
+
+ addMessageToHistory(chatMessage)
+
+ // Send the message
+ chatMessage.send()
+
+ // Clear the message input field
+ findViewById(R.id.message).text.clear()
+ }
+
+ private fun addMessageToHistory(chatMessage: ChatMessage) {
+ // To display a chat message, iterate over it's contents list
+ for (content in chatMessage.contents) {
+ when {
+ content.isText -> {
+ // Content is of type plain/text
+ addTextMessageToHistory(chatMessage, content)
+ }
+ content.isFile -> {
+ // Content represents a file we received and downloaded or a file we sent
+ // Here we assume it's an image
+ if (content.name?.endsWith(".jpeg") == true ||
+ content.name?.endsWith(".jpg") == true ||
+ content.name?.endsWith(".png") == true) {
+ addImageMessageToHistory(chatMessage, content)
+ }
+ }
+ content.isFileTransfer -> {
+ // Content represents a received file we didn't download yet
+ addDownloadButtonToHistory(chatMessage, content)
+ }
+ }
+ }
+ }
+
+ private fun addTextMessageToHistory(chatMessage: ChatMessage, content: Content) {
+ val messageView = TextView(this)
+ val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+ layoutParams.gravity = if (chatMessage.isOutgoing) Gravity.RIGHT else Gravity.LEFT
+ messageView.layoutParams = layoutParams
+
+ // Content is of type plain/text, we can get the text in the content
+ messageView.text = content.utf8Text
+
+ if (chatMessage.isOutgoing) {
+ messageView.setBackgroundColor(getColor(R.color.white))
+ } else {
+ messageView.setBackgroundColor(getColor(R.color.purple_200))
+ }
+
+ chatMessage.userData = messageView
+
+ findViewById(R.id.messages).addView(messageView)
+ findViewById(R.id.scroll).fullScroll(ScrollView.FOCUS_DOWN)
+ }
+
+ private fun addDownloadButtonToHistory(chatMessage: ChatMessage, content: Content) {
+ val buttonView = Button(this)
+ val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+ layoutParams.gravity = if (chatMessage.isOutgoing) Gravity.RIGHT else Gravity.LEFT
+ buttonView.layoutParams = layoutParams
+ buttonView.text = "Download"
+
+ chatMessage.userData = buttonView
+ buttonView.setOnClickListener {
+ buttonView.isEnabled = false
+ // Set the path to where we want the file to be stored
+ // Here we will use the app private storage
+ content.filePath = "${filesDir.absolutePath}/$content.name}"
+
+ // Start the download
+ chatMessage.downloadContent(content)
+
+ // Download progress will be notified through onMsgStateChanged callback,
+ // so we need to add a listener if not done yet
+ if (!chatMessage.isOutgoing) {
+ chatMessage.addListener(chatMessageListener)
+ }
+ }
+
+ findViewById(R.id.messages).addView(buttonView)
+ findViewById(R.id.scroll).fullScroll(ScrollView.FOCUS_DOWN)
+ }
+
+ private fun addImageMessageToHistory(chatMessage: ChatMessage, content: Content) {
+ val imageView = ImageView(this)
+ val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+ layoutParams.gravity = if (chatMessage.isOutgoing) Gravity.RIGHT else Gravity.LEFT
+ imageView.layoutParams = layoutParams
+
+ // As we downloaded the file to the content.filePath, we can now use it to display the image
+ imageView.setImageBitmap(BitmapFactory.decodeFile(content.filePath))
+
+ chatMessage.userData = imageView
+
+ findViewById(R.id.messages).addView(imageView)
+ findViewById(R.id.scroll).fullScroll(ScrollView.FOCUS_DOWN)
+ }
+}
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/kotlin/AdvancedChat/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/drawable/ic_launcher_background.xml b/android/kotlin/AdvancedChat/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/layout/advanced_chat_activity.xml b/android/kotlin/AdvancedChat/app/src/main/res/layout/advanced_chat_activity.xml
new file mode 100644
index 0000000..9ce47ed
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/layout/advanced_chat_activity.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/android/kotlin/AdvancedChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/values-night/themes.xml b/android/kotlin/AdvancedChat/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..c6c520e
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/values/colors.xml b/android/kotlin/AdvancedChat/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..2968fd8
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/values/colors.xml
@@ -0,0 +1,15 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #FFFFFF00
+ #FFFFA500
+ #FFFF0000
+ #FF00FF00
+ #FF0000FF
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/values/strings.xml b/android/kotlin/AdvancedChat/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..37873db
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Advanced Chat
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/app/src/main/res/values/themes.xml b/android/kotlin/AdvancedChat/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..e591a94
--- /dev/null
+++ b/android/kotlin/AdvancedChat/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/build.gradle b/android/kotlin/AdvancedChat/build.gradle
new file mode 100644
index 0000000..6534ac0
--- /dev/null
+++ b/android/kotlin/AdvancedChat/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.4.21"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.1.1"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/gradle.properties b/android/kotlin/AdvancedChat/gradle.properties
new file mode 100644
index 0000000..98bed16
--- /dev/null
+++ b/android/kotlin/AdvancedChat/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/android/kotlin/AdvancedChat/gradle/wrapper/gradle-wrapper.jar b/android/kotlin/AdvancedChat/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/android/kotlin/AdvancedChat/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/kotlin/AdvancedChat/gradle/wrapper/gradle-wrapper.properties b/android/kotlin/AdvancedChat/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..85cec80
--- /dev/null
+++ b/android/kotlin/AdvancedChat/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Jan 18 15:05:55 CET 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/android/kotlin/AdvancedChat/gradlew b/android/kotlin/AdvancedChat/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/android/kotlin/AdvancedChat/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/android/kotlin/AdvancedChat/gradlew.bat b/android/kotlin/AdvancedChat/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/android/kotlin/AdvancedChat/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/kotlin/AdvancedChat/settings.gradle b/android/kotlin/AdvancedChat/settings.gradle
new file mode 100644
index 0000000..0220d68
--- /dev/null
+++ b/android/kotlin/AdvancedChat/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Advanced Chat"
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/.gitignore b/android/kotlin/BasicChat/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/android/kotlin/BasicChat/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/android/kotlin/BasicChat/.idea/$CACHE_FILE$ b/android/kotlin/BasicChat/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..7da4bdc
--- /dev/null
+++ b/android/kotlin/BasicChat/.idea/$CACHE_FILE$
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ Android
+
+
+ CorrectnessLintAndroid
+
+
+ Gradle
+
+
+ LintAndroid
+
+
+ Probable bugsGradle
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/.idea/.name b/android/kotlin/BasicChat/.idea/.name
new file mode 100644
index 0000000..eb2c6be
--- /dev/null
+++ b/android/kotlin/BasicChat/.idea/.name
@@ -0,0 +1 @@
+Basic Chat
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/.idea/compiler.xml b/android/kotlin/BasicChat/.idea/compiler.xml
new file mode 100644
index 0000000..61a9130
--- /dev/null
+++ b/android/kotlin/BasicChat/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/.idea/gradle.xml b/android/kotlin/BasicChat/.idea/gradle.xml
new file mode 100644
index 0000000..cd8ea57
--- /dev/null
+++ b/android/kotlin/BasicChat/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/.idea/jarRepositories.xml b/android/kotlin/BasicChat/.idea/jarRepositories.xml
new file mode 100644
index 0000000..d9f0af1
--- /dev/null
+++ b/android/kotlin/BasicChat/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/.idea/misc.xml b/android/kotlin/BasicChat/.idea/misc.xml
new file mode 100644
index 0000000..19aa6a5
--- /dev/null
+++ b/android/kotlin/BasicChat/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/.idea/vcs.xml b/android/kotlin/BasicChat/.idea/vcs.xml
new file mode 100644
index 0000000..c2365ab
--- /dev/null
+++ b/android/kotlin/BasicChat/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/.gitignore b/android/kotlin/BasicChat/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/android/kotlin/BasicChat/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/build.gradle b/android/kotlin/BasicChat/app/build.gradle
new file mode 100644
index 0000000..4e91464
--- /dev/null
+++ b/android/kotlin/BasicChat/app/build.gradle
@@ -0,0 +1,50 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "org.linphone.basicchat"
+ minSdkVersion 23
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+// We need to declare this repository to be able to use Liblinphone SDK
+repositories {
+ maven {
+ url "https://linphone.org/maven_repository"
+ }
+}
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.2.1'
+ // Latest version is 4.5.x, using + to get the latest available
+ implementation 'org.linphone:linphone-sdk-android:4.5+'
+}
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/proguard-rules.pro b/android/kotlin/BasicChat/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/android/kotlin/BasicChat/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/AndroidManifest.xml b/android/kotlin/BasicChat/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..89a7e3f
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/assets/belledonne.png b/android/kotlin/BasicChat/app/src/main/assets/belledonne.png
new file mode 100644
index 0000000..67a6836
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/assets/belledonne.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/java/org/linphone/basicchat/BasicChatActivity.kt b/android/kotlin/BasicChat/app/src/main/java/org/linphone/basicchat/BasicChatActivity.kt
new file mode 100644
index 0000000..10d9486
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/java/org/linphone/basicchat/BasicChatActivity.kt
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+package org.linphone.basicchat
+
+import android.graphics.BitmapFactory
+import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.widget.*
+import androidx.appcompat.app.AppCompatActivity
+import org.linphone.core.*
+import java.io.File
+import java.io.FileOutputStream
+
+class BasicChatActivity: AppCompatActivity() {
+ private lateinit var core: Core
+ private var chatRoom: ChatRoom? = null
+
+ private val coreListener = object: CoreListenerStub() {
+ override fun onRegistrationStateChanged(
+ core: Core,
+ proxyConfig: ProxyConfig,
+ state: RegistrationState?,
+ message: String
+ ) {
+ findViewById(R.id.registration_status).text = message
+
+ if (state == RegistrationState.Failed) {
+ core.clearAllAuthInfo()
+ core.clearProxyConfig()
+ findViewById(R.id.connect).isEnabled = true
+ } else if (state == RegistrationState.Ok) {
+ findViewById(R.id.register_layout).visibility = View.GONE
+ findViewById(R.id.chat_layout).visibility = View.VISIBLE
+ }
+ }
+
+ override fun onMessageReceived(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
+ // We will be called in this when a message is received
+ // If the chat room wasn't existing, it is automatically created by the library
+ // If we already sent a chat message, the chatRoom variable will be the same as the one we already have
+ if (this@BasicChatActivity.chatRoom == null) {
+ if (chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())) {
+ // Keep the chatRoom object to use it to send messages if it hasn't been created yet
+ this@BasicChatActivity.chatRoom = chatRoom
+ findViewById(R.id.remote_address).setText(chatRoom.peerAddress.asStringUriOnly())
+ findViewById(R.id.remote_address).isEnabled = false
+ }
+ }
+
+ // We will notify the sender the message has been read by us
+ chatRoom.markAsRead()
+ addMessageToHistory(message)
+ }
+ }
+
+ private val chatMessageListener = object: ChatMessageListenerStub() {
+ override fun onMsgStateChanged(message: ChatMessage, state: ChatMessage.State?) {
+ val messageView = message.userData as? View
+ when (state) {
+ ChatMessage.State.InProgress -> {
+ messageView?.setBackgroundColor(getColor(R.color.yellow))
+ }
+ ChatMessage.State.Delivered -> {
+ // The proxy server has acknowledged the message with a 200 OK
+ messageView?.setBackgroundColor(getColor(R.color.orange))
+ }
+ ChatMessage.State.DeliveredToUser -> {
+ // User as received it
+ messageView?.setBackgroundColor(getColor(R.color.blue))
+ }
+ ChatMessage.State.Displayed -> {
+ // User as read it (client called chatRoom.markAsRead()
+ messageView?.setBackgroundColor(getColor(R.color.green))
+ }
+ ChatMessage.State.NotDelivered -> {
+ // User might be invalid or not registered
+ messageView?.setBackgroundColor(getColor(R.color.red))
+ }
+ ChatMessage.State.FileTransferDone -> {
+ // We finished uploading/downloading the file
+ if (!message.isOutgoing) {
+ findViewById(R.id.messages).removeView(messageView)
+ addMessageToHistory(message)
+ }
+ }
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.basic_chat_activity)
+
+ val factory = Factory.instance()
+ factory.setDebugMode(true, "Hello Linphone")
+ core = factory.createCore(null, null, this)
+
+ findViewById(R.id.connect).setOnClickListener {
+ login()
+ it.isEnabled = false
+ }
+
+ findViewById(R.id.send_message).setOnClickListener {
+ sendMessage()
+ }
+
+ findViewById(R.id.send_image).setOnClickListener {
+ sendImage()
+ }
+ }
+
+ private fun login() {
+ val username = findViewById(R.id.username).text.toString()
+ val password = findViewById(R.id.password).text.toString()
+ val domain = findViewById(R.id.domain).text.toString()
+ val transportType = when (findViewById(R.id.transport).checkedRadioButtonId) {
+ R.id.udp -> TransportType.Udp
+ R.id.tcp -> TransportType.Tcp
+ else -> TransportType.Tls
+ }
+ val authInfo = Factory.instance().createAuthInfo(username, null, password, null, null, domain, null)
+
+ val proxyConfig = core.createProxyConfig()
+ val identity = Factory.instance().createAddress("sip:$username@$domain")
+ proxyConfig.identityAddress = identity
+
+ val address = Factory.instance().createAddress("sip:$domain")
+ address?.transport = transportType
+ proxyConfig.serverAddr = address?.asStringUriOnly()
+ proxyConfig.enableRegister(true)
+
+ core.addAuthInfo(authInfo)
+ core.addProxyConfig(proxyConfig)
+
+ core.defaultProxyConfig = proxyConfig
+ core.addListener(coreListener)
+ core.start()
+ }
+
+ private fun createBasicChatRoom() {
+ // In this tutorial we will create a Basic chat room
+ // It doesn't include advanced features such as end-to-end encryption or groups
+ // But it is interoperable with any SIP service as it's relying on SIP SIMPLE messages
+ // If you try to enable a feature not supported by the basic backend, isValid() will return false
+ val params = core.createDefaultChatRoomParams()
+ params.backend = ChatRoomBackend.Basic
+ params.enableEncryption(false)
+ params.enableGroup(false)
+
+ if (params.isValid) {
+ // We also need the SIP address of the person we will chat with
+ val remoteSipUri = findViewById(R.id.remote_address).text.toString()
+ val remoteAddress = Factory.instance().createAddress(remoteSipUri)
+
+ if (remoteAddress != null) {
+ // And finally we will need our local SIP address
+ val localAddress = core.defaultProxyConfig?.identityAddress
+ val room = core.createChatRoom(params, localAddress, arrayOf(remoteAddress))
+ if (room != null) {
+ chatRoom = room
+ findViewById(R.id.remote_address).isEnabled = false
+ }
+ }
+ }
+ }
+
+ private fun sendMessage() {
+ if (chatRoom == null) {
+ // We need a ChatRoom object to send chat messages in it, so let's create it if it hasn't been done yet
+ createBasicChatRoom()
+ }
+
+ val message = findViewById(R.id.message).text.toString()
+ // We need to create a ChatMessage object using the ChatRoom
+ val chatMessage = chatRoom!!.createMessageFromUtf8(message)
+
+ // Then we can send it, progress will be notified using the onMsgStateChanged callback
+ chatMessage.addListener(chatMessageListener)
+
+ addMessageToHistory(chatMessage)
+
+ // Send the message
+ chatMessage.send()
+
+ // Clear the message input field
+ findViewById(R.id.message).text.clear()
+ }
+
+ private fun sendImage() {
+ if (chatRoom == null) {
+ // We need a ChatRoom object to send chat messages in it, so let's create it if it hasn't been done yet
+ createBasicChatRoom()
+ }
+
+ // We need to create a Content for our file transfer
+ val content = Factory.instance().createContent()
+ // Every content needs a content type & subtype
+ content.type = "image"
+ content.subtype = "png"
+
+ // The simplest way to upload a file is to provide it's path
+ // First copy the sample file from assets to the app directory if not done yet
+ val filePath = "${filesDir.absoluteFile}/belledonne.png"
+ copy("belledonne.png", filePath)
+ content.filePath = filePath
+
+ // We need to create a ChatMessage object using the ChatRoom
+ val chatMessage = chatRoom!!.createFileTransferMessage(content)
+
+ // Then we can send it, progress will be notified using the onMsgStateChanged callback
+ chatMessage.addListener(chatMessageListener)
+
+ // Ensure a file sharing server URL is correctly set in the Core
+ core.fileTransferServer = "https://www.linphone.org:444/lft.php"
+
+ addMessageToHistory(chatMessage)
+
+ // Send the message
+ chatMessage.send()
+ }
+
+ private fun addMessageToHistory(chatMessage: ChatMessage) {
+ // To display a chat message, iterate over it's contents list
+ for (content in chatMessage.contents) {
+ when {
+ content.isText -> {
+ // Content is of type plain/text
+ addTextMessageToHistory(chatMessage, content)
+ }
+ content.isFile -> {
+ // Content represents a file we received and downloaded or a file we sent
+ // Here we assume it's an image
+ if (content.name?.endsWith(".jpeg") == true ||
+ content.name?.endsWith(".jpg") == true ||
+ content.name?.endsWith(".png") == true) {
+ addImageMessageToHistory(chatMessage, content)
+ }
+ }
+ content.isFileTransfer -> {
+ // Content represents a received file we didn't download yet
+ addDownloadButtonToHistory(chatMessage, content)
+ }
+ }
+ }
+ }
+
+ private fun addTextMessageToHistory(chatMessage: ChatMessage, content: Content) {
+ val messageView = TextView(this)
+ val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+ layoutParams.gravity = if (chatMessage.isOutgoing) Gravity.RIGHT else Gravity.LEFT
+ messageView.layoutParams = layoutParams
+
+ // Content is of type plain/text, we can get the text in the content
+ messageView.text = content.utf8Text
+
+ if (chatMessage.isOutgoing) {
+ messageView.setBackgroundColor(getColor(R.color.white))
+ } else {
+ messageView.setBackgroundColor(getColor(R.color.purple_200))
+ }
+
+ chatMessage.userData = messageView
+
+ findViewById(R.id.messages).addView(messageView)
+ findViewById(R.id.scroll).fullScroll(ScrollView.FOCUS_DOWN)
+ }
+
+ private fun addDownloadButtonToHistory(chatMessage: ChatMessage, content: Content) {
+ val buttonView = Button(this)
+ val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+ layoutParams.gravity = if (chatMessage.isOutgoing) Gravity.RIGHT else Gravity.LEFT
+ buttonView.layoutParams = layoutParams
+ buttonView.text = "Download"
+
+ chatMessage.userData = buttonView
+ buttonView.setOnClickListener {
+ buttonView.isEnabled = false
+ // Set the path to where we want the file to be stored
+ // Here we will use the app private storage
+ content.filePath = "${filesDir.absolutePath}/$content.name}"
+
+ // Start the download
+ chatMessage.downloadContent(content)
+
+ // Download progress will be notified through onMsgStateChanged callback,
+ // so we need to add a listener if not done yet
+ if (!chatMessage.isOutgoing) {
+ chatMessage.addListener(chatMessageListener)
+ }
+ }
+
+ findViewById(R.id.messages).addView(buttonView)
+ findViewById(R.id.scroll).fullScroll(ScrollView.FOCUS_DOWN)
+ }
+
+ private fun addImageMessageToHistory(chatMessage: ChatMessage, content: Content) {
+ val imageView = ImageView(this)
+ val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+ layoutParams.gravity = if (chatMessage.isOutgoing) Gravity.RIGHT else Gravity.LEFT
+ imageView.layoutParams = layoutParams
+
+ // As we downloaded the file to the content.filePath, we can now use it to display the image
+ imageView.setImageBitmap(BitmapFactory.decodeFile(content.filePath))
+
+ chatMessage.userData = imageView
+
+ findViewById(R.id.messages).addView(imageView)
+ findViewById(R.id.scroll).fullScroll(ScrollView.FOCUS_DOWN)
+ }
+
+ private fun copy(from: String, to: String) {
+ // Used to copy a file from the assets to the app directory
+ val outFile = File(to)
+ if (outFile.exists()) {
+ return
+ }
+
+ val outStream = FileOutputStream(outFile)
+ val inFile = assets.open(from)
+ val buffer = ByteArray(1024)
+ var length: Int = inFile.read(buffer)
+
+ while (length > 0) {
+ outStream.write(buffer, 0, length)
+ length = inFile.read(buffer)
+ }
+
+ inFile.close()
+ outStream.flush()
+ outStream.close()
+ }
+}
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/kotlin/BasicChat/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/res/drawable/ic_launcher_background.xml b/android/kotlin/BasicChat/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/kotlin/BasicChat/app/src/main/res/layout/basic_chat_activity.xml b/android/kotlin/BasicChat/app/src/main/res/layout/basic_chat_activity.xml
new file mode 100644
index 0000000..71ccc7a
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/layout/basic_chat_activity.xml
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/kotlin/BasicChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/kotlin/BasicChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/kotlin/BasicChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/android/kotlin/BasicChat/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/BasicChat/app/src/main/res/values-night/themes.xml b/android/kotlin/BasicChat/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..fc6c140
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/res/values/colors.xml b/android/kotlin/BasicChat/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..2968fd8
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/values/colors.xml
@@ -0,0 +1,15 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #FFFFFF00
+ #FFFFA500
+ #FFFF0000
+ #FF00FF00
+ #FF0000FF
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/res/values/strings.xml b/android/kotlin/BasicChat/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7f7205c
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Basic Chat
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/app/src/main/res/values/themes.xml b/android/kotlin/BasicChat/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..af3faf4
--- /dev/null
+++ b/android/kotlin/BasicChat/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/build.gradle b/android/kotlin/BasicChat/build.gradle
new file mode 100644
index 0000000..6534ac0
--- /dev/null
+++ b/android/kotlin/BasicChat/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.4.21"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.1.1"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/gradle.properties b/android/kotlin/BasicChat/gradle.properties
new file mode 100644
index 0000000..98bed16
--- /dev/null
+++ b/android/kotlin/BasicChat/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/android/kotlin/BasicChat/gradle/wrapper/gradle-wrapper.jar b/android/kotlin/BasicChat/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/android/kotlin/BasicChat/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/kotlin/BasicChat/gradle/wrapper/gradle-wrapper.properties b/android/kotlin/BasicChat/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..7083a91
--- /dev/null
+++ b/android/kotlin/BasicChat/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Jan 18 12:00:36 CET 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/android/kotlin/BasicChat/gradlew b/android/kotlin/BasicChat/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/android/kotlin/BasicChat/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/android/kotlin/BasicChat/gradlew.bat b/android/kotlin/BasicChat/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/android/kotlin/BasicChat/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/kotlin/BasicChat/settings.gradle b/android/kotlin/BasicChat/settings.gradle
new file mode 100644
index 0000000..6f05dab
--- /dev/null
+++ b/android/kotlin/BasicChat/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Basic Chat"
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/.gitignore b/android/kotlin/HelloWorld/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/android/kotlin/HelloWorld/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/android/kotlin/HelloWorld/.idea/$CACHE_FILE$ b/android/kotlin/HelloWorld/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..7da4bdc
--- /dev/null
+++ b/android/kotlin/HelloWorld/.idea/$CACHE_FILE$
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ Android
+
+
+ CorrectnessLintAndroid
+
+
+ Gradle
+
+
+ LintAndroid
+
+
+ Probable bugsGradle
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/.idea/.gitignore b/android/kotlin/HelloWorld/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/android/kotlin/HelloWorld/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/android/kotlin/HelloWorld/.idea/.name b/android/kotlin/HelloWorld/.idea/.name
new file mode 100644
index 0000000..5e1c309
--- /dev/null
+++ b/android/kotlin/HelloWorld/.idea/.name
@@ -0,0 +1 @@
+Hello World
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/.idea/compiler.xml b/android/kotlin/HelloWorld/.idea/compiler.xml
new file mode 100644
index 0000000..61a9130
--- /dev/null
+++ b/android/kotlin/HelloWorld/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/.idea/gradle.xml b/android/kotlin/HelloWorld/.idea/gradle.xml
new file mode 100644
index 0000000..cd8ea57
--- /dev/null
+++ b/android/kotlin/HelloWorld/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/.idea/jarRepositories.xml b/android/kotlin/HelloWorld/.idea/jarRepositories.xml
new file mode 100644
index 0000000..d9f0af1
--- /dev/null
+++ b/android/kotlin/HelloWorld/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/.idea/misc.xml b/android/kotlin/HelloWorld/.idea/misc.xml
new file mode 100644
index 0000000..19aa6a5
--- /dev/null
+++ b/android/kotlin/HelloWorld/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/.idea/vcs.xml b/android/kotlin/HelloWorld/.idea/vcs.xml
new file mode 100644
index 0000000..c2365ab
--- /dev/null
+++ b/android/kotlin/HelloWorld/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/.gitignore b/android/kotlin/HelloWorld/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/build.gradle b/android/kotlin/HelloWorld/app/build.gradle
new file mode 100644
index 0000000..e9be437
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/build.gradle
@@ -0,0 +1,48 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "org.linphone.helloworld"
+ minSdkVersion 23
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+// We need to declare this repository to be able to use Liblinphone SDK
+repositories {
+ maven {
+ url "https://linphone.org/maven_repository"
+ }
+}
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.2.1'
+ // Latest version is 4.5.x, using + to get the latest available
+ implementation 'org.linphone:linphone-sdk-android:4.5+'
+}
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/proguard-rules.pro b/android/kotlin/HelloWorld/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/AndroidManifest.xml b/android/kotlin/HelloWorld/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8edac74
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/java/org/linphone/helloworld/HelloWorldActivity.kt b/android/kotlin/HelloWorld/app/src/main/java/org/linphone/helloworld/HelloWorldActivity.kt
new file mode 100644
index 0000000..24fd85f
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/java/org/linphone/helloworld/HelloWorldActivity.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+package org.linphone.helloworld
+
+import android.os.Bundle
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import org.linphone.core.Factory
+
+class HelloWorldActivity: AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // Check the app/build.gradle to see how to import the LibLinphone SDK !!!
+
+ setContentView(R.layout.hello_world_activity)
+ val coreVersion = findViewById(R.id.core_version)
+
+ // Core is the main object of the SDK. You can't do much without it.
+ // To create a Core, we need the instance of the Factory.
+ val factory = Factory.instance()
+
+ // Some configuration can be done before the Core is created, for example enable debug logs.
+ factory.setDebugMode(true, "Hello Linphone")
+
+ // Your Core can use up to 2 configuration files, but that isn't mandatory.
+ // On Android the Core needs to have the application context to work.
+ // If you don't, the following method call will crash.
+ val core = factory.createCore(null, null, this)
+
+ // Now we can start using the Core object
+ coreVersion.text = core.version
+ }
+}
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/kotlin/HelloWorld/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/res/drawable/ic_launcher_background.xml b/android/kotlin/HelloWorld/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/kotlin/HelloWorld/app/src/main/res/layout/hello_world_activity.xml b/android/kotlin/HelloWorld/app/src/main/res/layout/hello_world_activity.xml
new file mode 100644
index 0000000..d851e0b
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/layout/hello_world_activity.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/kotlin/HelloWorld/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/kotlin/HelloWorld/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/android/kotlin/HelloWorld/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/HelloWorld/app/src/main/res/values-night/themes.xml b/android/kotlin/HelloWorld/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..a956ae7
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/res/values/colors.xml b/android/kotlin/HelloWorld/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/res/values/strings.xml b/android/kotlin/HelloWorld/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..2673a8e
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Hello World
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/app/src/main/res/values/themes.xml b/android/kotlin/HelloWorld/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..e8e5aa0
--- /dev/null
+++ b/android/kotlin/HelloWorld/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/build.gradle b/android/kotlin/HelloWorld/build.gradle
new file mode 100644
index 0000000..6534ac0
--- /dev/null
+++ b/android/kotlin/HelloWorld/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.4.21"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.1.1"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/gradle.properties b/android/kotlin/HelloWorld/gradle.properties
new file mode 100644
index 0000000..98bed16
--- /dev/null
+++ b/android/kotlin/HelloWorld/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/android/kotlin/HelloWorld/gradle/wrapper/gradle-wrapper.jar b/android/kotlin/HelloWorld/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/android/kotlin/HelloWorld/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/kotlin/HelloWorld/gradle/wrapper/gradle-wrapper.properties b/android/kotlin/HelloWorld/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..415477c
--- /dev/null
+++ b/android/kotlin/HelloWorld/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Jan 18 10:03:31 CET 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/android/kotlin/HelloWorld/gradlew b/android/kotlin/HelloWorld/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/android/kotlin/HelloWorld/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/android/kotlin/HelloWorld/gradlew.bat b/android/kotlin/HelloWorld/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/android/kotlin/HelloWorld/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/kotlin/HelloWorld/settings.gradle b/android/kotlin/HelloWorld/settings.gradle
new file mode 100644
index 0000000..afaec3d
--- /dev/null
+++ b/android/kotlin/HelloWorld/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Hello World"
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/.gitignore b/android/kotlin/IncomingCall/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/android/kotlin/IncomingCall/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/android/kotlin/IncomingCall/.idea/$CACHE_FILE$ b/android/kotlin/IncomingCall/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..7da4bdc
--- /dev/null
+++ b/android/kotlin/IncomingCall/.idea/$CACHE_FILE$
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ Android
+
+
+ CorrectnessLintAndroid
+
+
+ Gradle
+
+
+ LintAndroid
+
+
+ Probable bugsGradle
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/.idea/.gitignore b/android/kotlin/IncomingCall/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/android/kotlin/IncomingCall/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/android/kotlin/IncomingCall/.idea/.name b/android/kotlin/IncomingCall/.idea/.name
new file mode 100644
index 0000000..a238237
--- /dev/null
+++ b/android/kotlin/IncomingCall/.idea/.name
@@ -0,0 +1 @@
+Incoming Call
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/.idea/compiler.xml b/android/kotlin/IncomingCall/.idea/compiler.xml
new file mode 100644
index 0000000..61a9130
--- /dev/null
+++ b/android/kotlin/IncomingCall/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/.idea/gradle.xml b/android/kotlin/IncomingCall/.idea/gradle.xml
new file mode 100644
index 0000000..cd8ea57
--- /dev/null
+++ b/android/kotlin/IncomingCall/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/.idea/jarRepositories.xml b/android/kotlin/IncomingCall/.idea/jarRepositories.xml
new file mode 100644
index 0000000..d9f0af1
--- /dev/null
+++ b/android/kotlin/IncomingCall/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/.idea/misc.xml b/android/kotlin/IncomingCall/.idea/misc.xml
new file mode 100644
index 0000000..19aa6a5
--- /dev/null
+++ b/android/kotlin/IncomingCall/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/.idea/vcs.xml b/android/kotlin/IncomingCall/.idea/vcs.xml
new file mode 100644
index 0000000..c2365ab
--- /dev/null
+++ b/android/kotlin/IncomingCall/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/.gitignore b/android/kotlin/IncomingCall/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/build.gradle b/android/kotlin/IncomingCall/app/build.gradle
new file mode 100644
index 0000000..f06589b
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/build.gradle
@@ -0,0 +1,52 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "org.linphone.incomingcall"
+ minSdkVersion 23
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+// We need to declare this repository to be able to use Liblinphone SDK
+repositories {
+ maven {
+ url "https://linphone.org/maven_repository"
+ }
+}
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.2.1'
+ // Latest version is 4.5.x, using + to get the latest available
+ implementation 'org.linphone:linphone-sdk-android:4.5+'
+ // Adding this dependency allows the linphone-sdk to automatically handle audio focus
+ implementation 'androidx.media:media:1.2.0'
+}
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/proguard-rules.pro b/android/kotlin/IncomingCall/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/AndroidManifest.xml b/android/kotlin/IncomingCall/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..57d7b5c
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/java/org/linphone/incomingcall/IncomingCallActivity.kt b/android/kotlin/IncomingCall/app/src/main/java/org/linphone/incomingcall/IncomingCallActivity.kt
new file mode 100644
index 0000000..9f9ea41
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/java/org/linphone/incomingcall/IncomingCallActivity.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+package org.linphone.incomingcall
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.view.View
+import android.widget.*
+import androidx.appcompat.app.AppCompatActivity
+import org.linphone.core.*
+
+class IncomingCallActivity: AppCompatActivity() {
+ private lateinit var core: Core
+
+ private val coreListener = object: CoreListenerStub() {
+ override fun onRegistrationStateChanged(
+ core: Core,
+ proxyConfig: ProxyConfig,
+ state: RegistrationState?,
+ message: String
+ ) {
+ findViewById(R.id.registration_status).text = message
+
+ if (state == RegistrationState.Failed) {
+ findViewById(R.id.connect).isEnabled = true
+ } else if (state == RegistrationState.Ok) {
+ findViewById(R.id.register_layout).visibility = View.GONE
+ findViewById(R.id.call_layout).visibility = View.VISIBLE
+ }
+ }
+
+ override fun onAudioDeviceChanged(core: Core, audioDevice: AudioDevice) {
+ // This callback will be triggered when a successful audio device has been changed
+ }
+
+ override fun onAudioDevicesListUpdated(core: Core) {
+ // This callback will be triggered when the available devices list has changed,
+ // for example after a bluetooth headset has been connected/disconnected.
+ }
+
+ override fun onCallStateChanged(
+ core: Core,
+ call: Call,
+ state: Call.State?,
+ message: String
+ ) {
+ findViewById(R.id.call_status).text = message
+
+ // When a call is received
+ when (state) {
+ Call.State.IncomingReceived -> {
+ findViewById(R.id.hang_up).isEnabled = true
+ findViewById(R.id.answer).isEnabled = true
+ findViewById(R.id.remote_address).setText(call.remoteAddress.asStringUriOnly())
+ }
+ Call.State.Connected -> {
+ findViewById(R.id.mute_mic).isEnabled = true
+ findViewById(R.id.toggle_speaker).isEnabled = true
+ }
+ Call.State.Released -> {
+ findViewById(R.id.hang_up).isEnabled = false
+ findViewById(R.id.answer).isEnabled = false
+ findViewById(R.id.mute_mic).isEnabled = false
+ findViewById(R.id.toggle_speaker).isEnabled = false
+ findViewById(R.id.remote_address).text.clear()
+ }
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.incoming_call_activity)
+
+ val factory = Factory.instance()
+ factory.setDebugMode(true, "Hello Linphone")
+ core = factory.createCore(null, null, this)
+
+ findViewById(R.id.connect).setOnClickListener {
+ login()
+ it.isEnabled = false
+ }
+
+ findViewById(R.id.hang_up).isEnabled = false
+ findViewById(R.id.answer).isEnabled = false
+ findViewById(R.id.mute_mic).isEnabled = false
+ findViewById(R.id.toggle_speaker).isEnabled = false
+ findViewById(R.id.remote_address).isEnabled = false
+
+ findViewById(R.id.hang_up).setOnClickListener {
+ // Terminates the call, whether it is ringing or running
+ core.currentCall?.terminate()
+ }
+
+ findViewById(R.id.answer).setOnClickListener {
+ // if we wanted, we could create a CallParams object
+ // and answer using this object to make changes to the call configuration
+ // (see OutgoingCall tutorial)
+ core.currentCall?.accept()
+ }
+
+ findViewById(R.id.mute_mic).setOnClickListener {
+ // The following toggles the microphone, disabling completely / enabling the sound capture
+ // from the device microphone
+ core.enableMic(!core.micEnabled())
+ }
+
+ findViewById(R.id.toggle_speaker).setOnClickListener {
+ toggleSpeaker()
+ }
+ }
+
+ private fun toggleSpeaker() {
+ // Get the currently used audio device
+ val currentAudioDevice = core.currentCall?.outputAudioDevice
+ val speakerEnabled = currentAudioDevice?.type == AudioDevice.Type.Speaker
+
+ // We can get a list of all available audio devices using
+ // Note that on tablets for example, there may be no Earpiece device
+ for (audioDevice in core.audioDevices) {
+ if (speakerEnabled && audioDevice.type == AudioDevice.Type.Earpiece) {
+ core.currentCall?.outputAudioDevice = audioDevice
+ return
+ } else if (!speakerEnabled && audioDevice.type == AudioDevice.Type.Speaker) {
+ core.currentCall?.outputAudioDevice = audioDevice
+ return
+ }/* If we wanted to route the audio to a bluetooth headset
+ else if (audioDevice.type == AudioDevice.Type.Bluetooth) {
+ core.currentCall?.outputAudioDevice = audioDevice
+ }*/
+ }
+ }
+
+ private fun login() {
+ val username = findViewById(R.id.username).text.toString()
+ val password = findViewById(R.id.password).text.toString()
+ val domain = findViewById(R.id.domain).text.toString()
+ val transportType = when (findViewById(R.id.transport).checkedRadioButtonId) {
+ R.id.udp -> TransportType.Udp
+ R.id.tcp -> TransportType.Tcp
+ else -> TransportType.Tls
+ }
+ val authInfo = Factory.instance().createAuthInfo(username, null, password, null, null, domain, null)
+
+ val proxyConfig = core.createProxyConfig()
+ val identity = Factory.instance().createAddress("sip:$username@$domain")
+ proxyConfig.identityAddress = identity
+
+ val address = Factory.instance().createAddress("sip:$domain")
+ address?.transport = transportType
+ proxyConfig.serverAddr = address?.asStringUriOnly()
+ proxyConfig.enableRegister(true)
+
+ core.addAuthInfo(authInfo)
+ core.addProxyConfig(proxyConfig)
+
+ core.defaultProxyConfig = proxyConfig
+ core.addListener(coreListener)
+ core.start()
+
+ // We will need the RECORD_AUDIO permission for video call
+ if (packageManager.checkPermission(Manifest.permission.RECORD_AUDIO, packageName) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), 0)
+ return
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/kotlin/IncomingCall/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/res/drawable/ic_launcher_background.xml b/android/kotlin/IncomingCall/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/kotlin/IncomingCall/app/src/main/res/layout/incoming_call_activity.xml b/android/kotlin/IncomingCall/app/src/main/res/layout/incoming_call_activity.xml
new file mode 100644
index 0000000..74aae7f
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/layout/incoming_call_activity.xml
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/kotlin/IncomingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/kotlin/IncomingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/android/kotlin/IncomingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/IncomingCall/app/src/main/res/values-night/themes.xml b/android/kotlin/IncomingCall/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..921c56a
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/res/values/colors.xml b/android/kotlin/IncomingCall/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/res/values/strings.xml b/android/kotlin/IncomingCall/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..3f567c9
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Incoming Call
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/app/src/main/res/values/themes.xml b/android/kotlin/IncomingCall/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..86469d5
--- /dev/null
+++ b/android/kotlin/IncomingCall/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/build.gradle b/android/kotlin/IncomingCall/build.gradle
new file mode 100644
index 0000000..528ebba
--- /dev/null
+++ b/android/kotlin/IncomingCall/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.4.21"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.1.2"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/gradle.properties b/android/kotlin/IncomingCall/gradle.properties
new file mode 100644
index 0000000..98bed16
--- /dev/null
+++ b/android/kotlin/IncomingCall/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/android/kotlin/IncomingCall/gradle/wrapper/gradle-wrapper.jar b/android/kotlin/IncomingCall/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/android/kotlin/IncomingCall/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/kotlin/IncomingCall/gradle/wrapper/gradle-wrapper.properties b/android/kotlin/IncomingCall/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0ca7592
--- /dev/null
+++ b/android/kotlin/IncomingCall/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jan 22 11:11:30 CET 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/android/kotlin/IncomingCall/gradlew b/android/kotlin/IncomingCall/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/android/kotlin/IncomingCall/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/android/kotlin/IncomingCall/gradlew.bat b/android/kotlin/IncomingCall/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/android/kotlin/IncomingCall/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/kotlin/IncomingCall/settings.gradle b/android/kotlin/IncomingCall/settings.gradle
new file mode 100644
index 0000000..4649fe0
--- /dev/null
+++ b/android/kotlin/IncomingCall/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Incoming Call"
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/.gitignore b/android/kotlin/OutgoingCall/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/android/kotlin/OutgoingCall/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/android/kotlin/OutgoingCall/.idea/$CACHE_FILE$ b/android/kotlin/OutgoingCall/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..7da4bdc
--- /dev/null
+++ b/android/kotlin/OutgoingCall/.idea/$CACHE_FILE$
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ Android
+
+
+ CorrectnessLintAndroid
+
+
+ Gradle
+
+
+ LintAndroid
+
+
+ Probable bugsGradle
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/.idea/.gitignore b/android/kotlin/OutgoingCall/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/android/kotlin/OutgoingCall/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/android/kotlin/OutgoingCall/.idea/.name b/android/kotlin/OutgoingCall/.idea/.name
new file mode 100644
index 0000000..5974658
--- /dev/null
+++ b/android/kotlin/OutgoingCall/.idea/.name
@@ -0,0 +1 @@
+Outgoing Call
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/.idea/compiler.xml b/android/kotlin/OutgoingCall/.idea/compiler.xml
new file mode 100644
index 0000000..61a9130
--- /dev/null
+++ b/android/kotlin/OutgoingCall/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java/.idea/gradle.xml b/android/kotlin/OutgoingCall/.idea/gradle.xml
similarity index 79%
rename from java/.idea/gradle.xml
rename to android/kotlin/OutgoingCall/.idea/gradle.xml
index b9f8a5e..9d38be2 100644
--- a/java/.idea/gradle.xml
+++ b/android/kotlin/OutgoingCall/.idea/gradle.xml
@@ -1,12 +1,13 @@
+
-
+
@@ -14,6 +15,7 @@
+
diff --git a/android/kotlin/OutgoingCall/.idea/jarRepositories.xml b/android/kotlin/OutgoingCall/.idea/jarRepositories.xml
new file mode 100644
index 0000000..d9f0af1
--- /dev/null
+++ b/android/kotlin/OutgoingCall/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/.idea/misc.xml b/android/kotlin/OutgoingCall/.idea/misc.xml
new file mode 100644
index 0000000..19aa6a5
--- /dev/null
+++ b/android/kotlin/OutgoingCall/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/.idea/vcs.xml b/android/kotlin/OutgoingCall/.idea/vcs.xml
new file mode 100644
index 0000000..c2365ab
--- /dev/null
+++ b/android/kotlin/OutgoingCall/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/.gitignore b/android/kotlin/OutgoingCall/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/build.gradle b/android/kotlin/OutgoingCall/app/build.gradle
new file mode 100644
index 0000000..2d7fad6
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/build.gradle
@@ -0,0 +1,52 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "org.linphone.outgoingcall"
+ minSdkVersion 23
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+// We need to declare this repository to be able to use Liblinphone SDK
+repositories {
+ maven {
+ url "https://linphone.org/maven_repository"
+ }
+}
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.2.1'
+ // Latest version is 4.5.x, using + to get the latest available
+ implementation 'org.linphone:linphone-sdk-android:4.5+'
+ // Adding this dependency allows the linphone-sdk to automatically handle audio focus
+ implementation 'androidx.media:media:1.2.0'
+}
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/proguard-rules.pro b/android/kotlin/OutgoingCall/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/AndroidManifest.xml b/android/kotlin/OutgoingCall/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c431c00
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/java/org/linphone/outgoingcall/OutgoingCallActivity.kt b/android/kotlin/OutgoingCall/app/src/main/java/org/linphone/outgoingcall/OutgoingCallActivity.kt
new file mode 100644
index 0000000..a2b62df
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/java/org/linphone/outgoingcall/OutgoingCallActivity.kt
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+package org.linphone.outgoingcall
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.view.View
+import android.widget.*
+import androidx.appcompat.app.AppCompatActivity
+import org.linphone.core.*
+import org.linphone.core.tools.Log
+
+class OutgoingCallActivity: AppCompatActivity() {
+ private lateinit var core: Core
+
+ private val coreListener = object: CoreListenerStub() {
+ override fun onRegistrationStateChanged(
+ core: Core,
+ proxyConfig: ProxyConfig,
+ state: RegistrationState?,
+ message: String
+ ) {
+ findViewById(R.id.registration_status).text = message
+
+ if (state == RegistrationState.Failed) {
+ findViewById(R.id.connect).isEnabled = true
+ } else if (state == RegistrationState.Ok) {
+ findViewById(R.id.register_layout).visibility = View.GONE
+ findViewById(R.id.call_layout).visibility = View.VISIBLE
+ }
+ }
+
+ override fun onCallStateChanged(
+ core: Core,
+ call: Call,
+ state: Call.State?,
+ message: String
+ ) {
+ // This function will be called each time a call state changes,
+ // which includes new incoming/outgoing calls
+ findViewById(R.id.call_status).text = message
+
+ when (state) {
+ Call.State.OutgoingInit -> {
+ // First state an outgoing call will go through
+ }
+ Call.State.OutgoingProgress -> {
+ // Right after outgoing init
+ }
+ Call.State.OutgoingRinging -> {
+ // This state will be reached upon reception of the 180 RINGING
+ }
+ Call.State.Connected -> {
+ // When the 200 OK has been received
+ }
+ Call.State.StreamsRunning -> {
+ // This state indicates the call is active.
+ // You may reach this state multiple times, for example after a pause/resume
+ // or after the ICE negotiation completes
+ // Wait for the call to be connected before allowing a call update
+ findViewById(R.id.pause).isEnabled = true
+ findViewById(R.id.pause).text = "Pause"
+ findViewById(R.id.toggle_video).isEnabled = true
+
+ // Only enable toggle camera button if there is more than 1 camera and the video is enabled
+ // We check if core.videoDevicesList.size > 2 because of the fake camera with static image created by our SDK (see below)
+ findViewById(R.id.toggle_camera).isEnabled = core.videoDevicesList.size > 2 && call.currentParams.videoEnabled()
+ }
+ Call.State.Paused -> {
+ // When you put a call in pause, it will became Paused
+ findViewById(R.id.pause).text = "Resume"
+ findViewById(R.id.toggle_video).isEnabled = false
+ }
+ Call.State.PausedByRemote -> {
+ // When the remote end of the call pauses it, it will be PausedByRemote
+ }
+ Call.State.Updating -> {
+ // When we request a call update, for example when toggling video
+ }
+ Call.State.UpdatedByRemote -> {
+ // When the remote requests a call update
+ }
+ Call.State.Released -> {
+ // Call state will be released shortly after the End state
+ findViewById(R.id.remote_address).isEnabled = true
+ findViewById(R.id.call).isEnabled = true
+ findViewById(R.id.pause).isEnabled = false
+ findViewById(R.id.pause).text = "Pause"
+ findViewById(R.id.toggle_video).isEnabled = false
+ findViewById(R.id.hang_up).isEnabled = false
+ findViewById(R.id.toggle_camera).isEnabled = false
+ }
+ Call.State.Error -> {
+
+ }
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.outgoing_call_activity)
+
+ val factory = Factory.instance()
+ factory.setDebugMode(true, "Hello Linphone")
+ core = factory.createCore(null, null, this)
+
+ findViewById(R.id.connect).setOnClickListener {
+ login()
+ it.isEnabled = false
+ }
+
+ // For video to work, we need two TextureViews:
+ // one for the remote video and one for the local preview
+ core.nativeVideoWindowId = findViewById(R.id.remote_video_surface)
+ // The local preview is a org.linphone.mediastream.video.capture.CaptureTextureView
+ // which inherits from TextureView and contains code to keep the ratio of the capture video
+ core.nativePreviewWindowId = findViewById(R.id.local_preview_video_surface)
+
+ // Here we enable the video capture & display at Core level
+ // It doesn't mean calls will be made with video automatically,
+ // But it allows to use it later
+ core.enableVideoCapture(true)
+ core.enableVideoDisplay(true)
+
+ // When enabling the video, the remote will either automatically answer the update request
+ // or it will ask it's user depending on it's policy.
+ // Here we have configured the policy to always automatically accept video requests
+ core.videoActivationPolicy.automaticallyAccept = true
+ // If you don't want to automatically accept,
+ // you'll have to use a code similar to the one in toggleVideo to answer a received request
+
+ // If the following property is enabled, it will automatically configure created call params with video enabled
+ //core.videoActivationPolicy.automaticallyInitiate = true
+
+ findViewById(R.id.call).setOnClickListener {
+ outgoingCall()
+ findViewById(R.id.remote_address).isEnabled = false
+ it.isEnabled = false
+ findViewById(R.id.hang_up).isEnabled = true
+ }
+
+ findViewById(R.id.hang_up).setOnClickListener {
+ hangUp()
+ }
+
+ findViewById(R.id.pause).setOnClickListener {
+ pauseOrResume()
+ }
+
+ findViewById(R.id.toggle_video).setOnClickListener {
+ toggleVideo()
+ }
+
+ findViewById(R.id.toggle_camera).setOnClickListener {
+ toggleCamera()
+ }
+
+ findViewById(R.id.pause).isEnabled = false
+ findViewById(R.id.toggle_video).isEnabled = false
+ findViewById(R.id.toggle_camera).isEnabled = false
+ findViewById(R.id.hang_up).isEnabled = false
+ }
+
+ private fun login() {
+ val username = findViewById(R.id.username).text.toString()
+ val password = findViewById(R.id.password).text.toString()
+ val domain = findViewById(R.id.domain).text.toString()
+ val transportType = when (findViewById(R.id.transport).checkedRadioButtonId) {
+ R.id.udp -> TransportType.Udp
+ R.id.tcp -> TransportType.Tcp
+ else -> TransportType.Tls
+ }
+ val authInfo = Factory.instance().createAuthInfo(username, null, password, null, null, domain, null)
+
+ val proxyConfig = core.createProxyConfig()
+ val identity = Factory.instance().createAddress("sip:$username@$domain")
+ proxyConfig.identityAddress = identity
+
+ val address = Factory.instance().createAddress("sip:$domain")
+ address?.transport = transportType
+ proxyConfig.serverAddr = address?.asStringUriOnly()
+ proxyConfig.enableRegister(true)
+
+ core.addAuthInfo(authInfo)
+ core.addProxyConfig(proxyConfig)
+
+ core.defaultProxyConfig = proxyConfig
+ core.addListener(coreListener)
+ core.start()
+
+ // We will need the RECORD_AUDIO permission for video call
+ if (packageManager.checkPermission(Manifest.permission.RECORD_AUDIO, packageName) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), 0)
+ return
+ }
+ }
+
+ private fun outgoingCall() {
+ // As for everything we need to get the SIP URI of the remote and convert it to an Address
+ val remoteSipUri = findViewById(R.id.remote_address).text.toString()
+ val remoteAddress = Factory.instance().createAddress(remoteSipUri)
+ remoteAddress ?: return // If address parsing fails, we can't continue with outgoing call process
+
+ // We also need a CallParams object
+ // Create call params expects a Call object for incoming calls, but for outgoing we must use null safely
+ val params = core.createCallParams(null)
+ params ?: return // Same for params
+
+ // We can now configure it
+ // Here we ask for no encryption but we could ask for ZRTP/SRTP/DTLS
+ params.mediaEncryption = MediaEncryption.None
+ // If we wanted to start the call with video directly
+ //params.enableVideo(true)
+
+ // Finally we start the call
+ core.inviteAddressWithParams(remoteAddress, params)
+ // Call process can be followed in onCallStateChanged callback from core listener
+ }
+
+ private fun hangUp() {
+ if (core.callsNb == 0) return
+
+ // If the call state isn't paused, we can get it using core.currentCall
+ val call = if (core.currentCall != null) core.currentCall else core.calls[0]
+ call ?: return
+
+ // Terminating a call is quite simple
+ call.terminate()
+ }
+
+ private fun toggleVideo() {
+ if (core.callsNb == 0) return
+ val call = if (core.currentCall != null) core.currentCall else core.calls[0]
+ call ?: return
+
+ // We will need the CAMERA permission for video call
+ if (packageManager.checkPermission(Manifest.permission.CAMERA, packageName) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(arrayOf(Manifest.permission.CAMERA), 0)
+ return
+ }
+
+ // To update the call, we need to create a new call params, from the call object this time
+ val params = core.createCallParams(call)
+ // Here we toggle the video state (disable it if enabled, enable it if disabled)
+ // Note that we are using currentParams and not params or remoteParams
+ // params is the object you configured when the call was started
+ // remote params is the same but for the remote
+ // current params is the real params of the call, resulting of the mix of local & remote params
+ params?.enableVideo(!call.currentParams.videoEnabled())
+ // Finally we request the call update
+ call.update(params)
+
+ // Note that when toggling off the video, TextureViews will keep showing the latest frame displayed
+ }
+
+ private fun toggleCamera() {
+ // Currently used camera
+ val currentDevice = core.videoDevice
+
+ // Let's iterate over all camera available and choose another one
+ for (camera in core.videoDevicesList) {
+ // All devices will have a "Static picture" fake camera, and we don't want to use it
+ if (camera != currentDevice && camera != "StaticImage: Static picture") {
+ core.videoDevice = camera
+ break
+ }
+ }
+ }
+
+ private fun pauseOrResume() {
+ if (core.callsNb == 0) return
+ val call = if (core.currentCall != null) core.currentCall else core.calls[0]
+ call ?: return
+
+ if (call.state != Call.State.Paused && call.state != Call.State.Pausing) {
+ // If our call isn't paused, let's pause it
+ call.pause()
+ } else if (call.state != Call.State.Resuming) {
+ // Otherwise let's resume it
+ call.resume()
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/kotlin/OutgoingCall/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/drawable/ic_launcher_background.xml b/android/kotlin/OutgoingCall/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/layout/outgoing_call_activity.xml b/android/kotlin/OutgoingCall/app/src/main/res/layout/outgoing_call_activity.xml
new file mode 100644
index 0000000..5909a2b
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/layout/outgoing_call_activity.xml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/android/kotlin/OutgoingCall/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/values-night/themes.xml b/android/kotlin/OutgoingCall/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..46a86e6
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/values/colors.xml b/android/kotlin/OutgoingCall/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/values/strings.xml b/android/kotlin/OutgoingCall/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..bb2b913
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Outgoing Call
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/app/src/main/res/values/themes.xml b/android/kotlin/OutgoingCall/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..d97147e
--- /dev/null
+++ b/android/kotlin/OutgoingCall/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/build.gradle b/android/kotlin/OutgoingCall/build.gradle
new file mode 100644
index 0000000..6534ac0
--- /dev/null
+++ b/android/kotlin/OutgoingCall/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.4.21"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.1.1"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/gradle.properties b/android/kotlin/OutgoingCall/gradle.properties
new file mode 100644
index 0000000..98bed16
--- /dev/null
+++ b/android/kotlin/OutgoingCall/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/android/kotlin/OutgoingCall/gradle/wrapper/gradle-wrapper.jar b/android/kotlin/OutgoingCall/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/android/kotlin/OutgoingCall/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/kotlin/OutgoingCall/gradle/wrapper/gradle-wrapper.properties b/android/kotlin/OutgoingCall/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..653dc41
--- /dev/null
+++ b/android/kotlin/OutgoingCall/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Jan 19 14:03:15 CET 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/android/kotlin/OutgoingCall/gradlew b/android/kotlin/OutgoingCall/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/android/kotlin/OutgoingCall/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/android/kotlin/OutgoingCall/gradlew.bat b/android/kotlin/OutgoingCall/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/android/kotlin/OutgoingCall/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/kotlin/OutgoingCall/settings.gradle b/android/kotlin/OutgoingCall/settings.gradle
new file mode 100644
index 0000000..b89f134
--- /dev/null
+++ b/android/kotlin/OutgoingCall/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Outgoing Call"
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/.gitignore b/android/kotlin/Pushnotifications/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/android/kotlin/Pushnotifications/.idea/$CACHE_FILE$ b/android/kotlin/Pushnotifications/.idea/$CACHE_FILE$
new file mode 100644
index 0000000..7da4bdc
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.idea/$CACHE_FILE$
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ Android
+
+
+ CorrectnessLintAndroid
+
+
+ Gradle
+
+
+ LintAndroid
+
+
+ Probable bugsGradle
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/.idea/.gitignore b/android/kotlin/Pushnotifications/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/android/kotlin/Pushnotifications/.idea/.name b/android/kotlin/Pushnotifications/.idea/.name
new file mode 100644
index 0000000..48144fe
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.idea/.name
@@ -0,0 +1 @@
+Push notifications
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/.idea/compiler.xml b/android/kotlin/Pushnotifications/.idea/compiler.xml
new file mode 100644
index 0000000..61a9130
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/.idea/gradle.xml b/android/kotlin/Pushnotifications/.idea/gradle.xml
new file mode 100644
index 0000000..cd8ea57
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/.idea/jarRepositories.xml b/android/kotlin/Pushnotifications/.idea/jarRepositories.xml
new file mode 100644
index 0000000..d9f0af1
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/.idea/misc.xml b/android/kotlin/Pushnotifications/.idea/misc.xml
new file mode 100644
index 0000000..19aa6a5
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/.idea/vcs.xml b/android/kotlin/Pushnotifications/.idea/vcs.xml
new file mode 100644
index 0000000..c2365ab
--- /dev/null
+++ b/android/kotlin/Pushnotifications/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/.gitignore b/android/kotlin/Pushnotifications/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/build.gradle b/android/kotlin/Pushnotifications/app/build.gradle
new file mode 100644
index 0000000..d5c6948
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/build.gradle
@@ -0,0 +1,55 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "org.linphone.pushnotifications"
+ minSdkVersion 23
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+// We need to declare this repository to be able to use Liblinphone SDK
+repositories {
+ maven {
+ url "https://linphone.org/maven_repository"
+ }
+}
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.2.1'
+ // Latest version is 4.5.x, using + to get the latest available
+ implementation 'org.linphone:linphone-sdk-android:4.5+'
+ // Required for firebase push notifications
+ implementation 'com.google.firebase:firebase-messaging:19.0.1'
+}
+
+// Required for firebase push notifications
+apply plugin: 'com.google.gms.google-services'
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/google-services.json b/android/kotlin/Pushnotifications/app/google-services.json
new file mode 100644
index 0000000..08d6675
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/google-services.json
@@ -0,0 +1,40 @@
+{
+ "project_info": {
+ "project_number": "929724111839",
+ "firebase_url": "https://linphone-android-8a563.firebaseio.com",
+ "project_id": "linphone-android-8a563",
+ "storage_bucket": "linphone-android-8a563.appspot.com"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:929724111839:android:cfa1ce5fe611ab2818c26a",
+ "android_client_info": {
+ "package_name": "org.linphone.pushnotifications"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "929724111839-v5so1tcd65iil7dd7sde8jgii44h8luf.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCKrwWhkbA7Iy3wpEI8_ZvKOMp5jf6vV6A"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "929724111839-a6g55p0jkq0qfi8utt0v4eh3l405g6nj.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/proguard-rules.pro b/android/kotlin/Pushnotifications/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/AndroidManifest.xml b/android/kotlin/Pushnotifications/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..41c6f5c
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/java/org/linphone/pushnotifications/PushBroadcastReceiver.kt b/android/kotlin/Pushnotifications/app/src/main/java/org/linphone/pushnotifications/PushBroadcastReceiver.kt
new file mode 100644
index 0000000..5ab05ab
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/java/org/linphone/pushnotifications/PushBroadcastReceiver.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+package org.linphone.pushnotifications
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.widget.Toast
+
+class PushBroadcastReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ Toast.makeText(context, "Push received with app shut down", Toast.LENGTH_LONG).show()
+ // A push have been received but there was no Core alive, you should create it again
+ // This way the core will register and it will handle the message or call event like if the app was started
+ }
+}
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/java/org/linphone/pushnotifications/PushNotificationsActivity.kt b/android/kotlin/Pushnotifications/app/src/main/java/org/linphone/pushnotifications/PushNotificationsActivity.kt
new file mode 100644
index 0000000..7365ac2
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/java/org/linphone/pushnotifications/PushNotificationsActivity.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * 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 3 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, see .
+ */
+package org.linphone.pushnotifications
+
+import android.os.Bundle
+import android.view.View
+import android.widget.*
+import androidx.appcompat.app.AppCompatActivity
+import org.linphone.core.*
+
+class PushNotificationsActivity: AppCompatActivity() {
+ private lateinit var core: Core
+
+ private val coreListener = object: CoreListenerStub() {
+ override fun onRegistrationStateChanged(
+ core: Core,
+ proxyConfig: ProxyConfig,
+ state: RegistrationState?,
+ message: String
+ ) {
+ findViewById(R.id.registration_status).text = message
+
+ if (state == RegistrationState.Failed) {
+ findViewById(R.id.connect).isEnabled = true
+ } else if (state == RegistrationState.Ok) {
+ findViewById(R.id.register_layout).visibility =
+ View.GONE
+
+ // This will display the push information stored in the contact URI parameters
+ findViewById(R.id.push_info).text = proxyConfig.contactUriParameters
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.push_notifications_activity)
+
+ // For push notifications to work, you have to copy your google-services.json in the app/ folder
+ // And you must declare our FirebaseMessaging service in the Manifest
+ // You also have to make some changes in your build.gradle files, see the ones in this project
+
+ val factory = Factory.instance()
+ factory.setDebugMode(true, "Hello Linphone")
+ core = factory.createCore(null, null, this)
+
+ // Make sure the core is configured to use push notification token from firebase
+ core.isPushNotificationEnabled = true
+
+ findViewById(R.id.connect).setOnClickListener {
+ login()
+ it.isEnabled = false
+ }
+ }
+
+ private fun login() {
+ val username = findViewById(R.id.username).text.toString()
+ val password = findViewById(R.id.password).text.toString()
+ val domain = findViewById(R.id.domain).text.toString()
+ val transportType = when (findViewById(R.id.transport).checkedRadioButtonId) {
+ R.id.udp -> TransportType.Udp
+ R.id.tcp -> TransportType.Tcp
+ else -> TransportType.Tls
+ }
+ val authInfo = Factory.instance().createAuthInfo(username, null, password, null, null, domain, null)
+
+ val proxyConfig = core.createProxyConfig()
+ val identity = Factory.instance().createAddress("sip:$username@$domain")
+ proxyConfig.identityAddress = identity
+
+ val address = Factory.instance().createAddress("sip:$domain")
+ address?.transport = transportType
+ proxyConfig.serverAddr = address?.asStringUriOnly()
+ proxyConfig.enableRegister(true)
+
+ // Ensure push notification is enabled for this account
+ proxyConfig.isPushNotificationAllowed = true
+
+ core.addAuthInfo(authInfo)
+ core.addProxyConfig(proxyConfig)
+
+ core.defaultProxyConfig = proxyConfig
+ core.addListener(coreListener)
+ core.start()
+
+ if (!core.isPushNotificationAvailable) {
+ Toast.makeText(this, "Something is wrong with the push setup!", Toast.LENGTH_LONG).show()
+ }
+
+ // And that's it!
+ // You can kill this app and send a message or initiate a call to the identity you registered and you'll see the toast.
+
+ // When a push notification will be received by your app, either:
+ // - the Core is alive and it will check it is properly registered & connected to the proxy
+ // - the Core isn't available and a broadcast on org.linphone.core.action.PUSH_RECEIVED will be fired
+
+ // Another way is to create your own Application object and create the Core in it
+ // This way, when a push will be received, the Core will be created before the push being handled
+ // so the first case above will always be true. See our linphone-android app for an example of that.
+ }
+}
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/kotlin/Pushnotifications/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/drawable/ic_launcher_background.xml b/android/kotlin/Pushnotifications/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/layout/push_notifications_activity.xml b/android/kotlin/Pushnotifications/app/src/main/res/layout/push_notifications_activity.xml
new file mode 100644
index 0000000..1ae2aec
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/layout/push_notifications_activity.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/android/kotlin/Pushnotifications/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/values-night/themes.xml b/android/kotlin/Pushnotifications/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..e1d68e9
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/values/colors.xml b/android/kotlin/Pushnotifications/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/values/strings.xml b/android/kotlin/Pushnotifications/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..18fb05a
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Push notifications
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/app/src/main/res/values/themes.xml b/android/kotlin/Pushnotifications/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..7c38259
--- /dev/null
+++ b/android/kotlin/Pushnotifications/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/build.gradle b/android/kotlin/Pushnotifications/build.gradle
new file mode 100644
index 0000000..69ebcf5
--- /dev/null
+++ b/android/kotlin/Pushnotifications/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.4.21"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.1.2"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // This is also mandatory for push notifications
+ classpath 'com.google.gms:google-services:4.3.4'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/gradle.properties b/android/kotlin/Pushnotifications/gradle.properties
new file mode 100644
index 0000000..98bed16
--- /dev/null
+++ b/android/kotlin/Pushnotifications/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/android/kotlin/Pushnotifications/gradle/wrapper/gradle-wrapper.jar b/android/kotlin/Pushnotifications/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/android/kotlin/Pushnotifications/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/kotlin/Pushnotifications/gradle/wrapper/gradle-wrapper.properties b/android/kotlin/Pushnotifications/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bd31813
--- /dev/null
+++ b/android/kotlin/Pushnotifications/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jan 22 14:11:17 CET 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/android/kotlin/Pushnotifications/gradlew b/android/kotlin/Pushnotifications/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/android/kotlin/Pushnotifications/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/android/kotlin/Pushnotifications/gradlew.bat b/android/kotlin/Pushnotifications/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/android/kotlin/Pushnotifications/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/kotlin/Pushnotifications/settings.gradle b/android/kotlin/Pushnotifications/settings.gradle
new file mode 100644
index 0000000..1bbc888
--- /dev/null
+++ b/android/kotlin/Pushnotifications/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Push notifications"
\ No newline at end of file
diff --git a/java/.idea/.name b/java/.idea/.name
deleted file mode 100644
index 455566c..0000000
--- a/java/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-Linphone SDK Tutorials
\ No newline at end of file
diff --git a/java/.idea/codeStyles/Project.xml b/java/.idea/codeStyles/Project.xml
deleted file mode 100644
index 681f41a..0000000
--- a/java/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- xmlns:android
-
- ^$
-
-
-
-
-
-
-
-
- xmlns:.*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*:id
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- .*:name
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- name
-
- ^$
-
-
-
-
-
-
-
-
- style
-
- ^$
-
-
-
-
-
-
-
-
- .*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*
-
- http://schemas.android.com/apk/res/android
-
-
- ANDROID_ATTRIBUTE_ORDER
-
-
-
-
-
-
- .*
-
- .*
-
-
- BY_NAME
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/java/.idea/runConfigurations.xml b/java/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/java/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/java/app/build.gradle b/java/app/build.gradle
deleted file mode 100644
index 2fcd76c..0000000
--- a/java/app/build.gradle
+++ /dev/null
@@ -1,32 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion 29
- buildToolsVersion "29.0.3"
-
- defaultConfig {
- applicationId "org.linphone.tutorials"
- minSdkVersion 23
- targetSdkVersion 29
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
- implementation fileTree(dir: "libs", include: ["*.jar"])
- implementation 'androidx.appcompat:appcompat:1.1.0'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
-
-}
\ No newline at end of file
diff --git a/java/app/src/main/AndroidManifest.xml b/java/app/src/main/AndroidManifest.xml
deleted file mode 100644
index 4321fa1..0000000
--- a/java/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/java/app/src/main/res/values/colors.xml b/java/app/src/main/res/values/colors.xml
deleted file mode 100644
index 4faecfa..0000000
--- a/java/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- #6200EE
- #3700B3
- #03DAC5
-
\ No newline at end of file
diff --git a/java/app/src/main/res/values/strings.xml b/java/app/src/main/res/values/strings.xml
deleted file mode 100644
index 8d9db68..0000000
--- a/java/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Linphone SDK Tutorials
-
\ No newline at end of file
diff --git a/java/app/src/main/res/values/styles.xml b/java/app/src/main/res/values/styles.xml
deleted file mode 100644
index fac9291..0000000
--- a/java/app/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/java/settings.gradle b/java/settings.gradle
deleted file mode 100644
index 24b59fc..0000000
--- a/java/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-include ':app'
-rootProject.name = "Linphone SDK Tutorials"
\ No newline at end of file