Committed by
GitHub
Changes for compose components livekit library (#282)
* save work * update deps * update deps * Fix build * Fix github actions * Spotless fixes
正在显示
39 个修改的文件
包含
640 行增加
和
181 行删除
| @@ -32,7 +32,7 @@ jobs: | @@ -32,7 +32,7 @@ jobs: | ||
| 32 | - name: set up JDK 12 | 32 | - name: set up JDK 12 |
| 33 | uses: actions/setup-java@v3.12.0 | 33 | uses: actions/setup-java@v3.12.0 |
| 34 | with: | 34 | with: |
| 35 | - java-version: '12' | 35 | + java-version: '17' |
| 36 | distribution: 'adopt' | 36 | distribution: 'adopt' |
| 37 | 37 | ||
| 38 | - uses: actions/cache@v3.3.2 | 38 | - uses: actions/cache@v3.3.2 |
| @@ -161,12 +161,14 @@ jobs: | @@ -161,12 +161,14 @@ jobs: | ||
| 161 | sed -i -e "s,signing.keyId=,signing.keyId=$GPG_KEY_ID,g" gradle.properties | 161 | sed -i -e "s,signing.keyId=,signing.keyId=$GPG_KEY_ID,g" gradle.properties |
| 162 | sed -i -e "s,signing.password=,signing.password=$GPG_PASSWORD,g" gradle.properties | 162 | sed -i -e "s,signing.password=,signing.password=$GPG_PASSWORD,g" gradle.properties |
| 163 | sed -i -e "s,signing.secretKeyRingFile=,signing.secretKeyRingFile=$GITHUB_WORKSPACE/release.gpg,g" gradle.properties | 163 | sed -i -e "s,signing.secretKeyRingFile=,signing.secretKeyRingFile=$GITHUB_WORKSPACE/release.gpg,g" gradle.properties |
| 164 | + sed -i -e "s,STAGING_PROFILE_ID=,STAGING_PROFILE_ID=$PROFILE_ID,g" gradle.properties | ||
| 164 | env: | 165 | env: |
| 165 | GPG_KEY_ARMOR: "${{ secrets.SIGNING_KEY_ARMOR }}" | 166 | GPG_KEY_ARMOR: "${{ secrets.SIGNING_KEY_ARMOR }}" |
| 166 | GPG_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} | 167 | GPG_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} |
| 167 | GPG_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} | 168 | GPG_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} |
| 168 | NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }} | 169 | NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }} |
| 169 | NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} | 170 | NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} |
| 171 | + PROFILE_ID: ${{ secrets.STAGING_PROFILE_ID }} | ||
| 170 | 172 | ||
| 171 | - name: Publish snapshot | 173 | - name: Publish snapshot |
| 172 | if: github.event_name == 'push' && contains(steps.version_name.outputs.version_name,'SNAPSHOT') | 174 | if: github.event_name == 'push' && contains(steps.version_name.outputs.version_name,'SNAPSHOT') |
| @@ -15,15 +15,15 @@ jobs: | @@ -15,15 +15,15 @@ jobs: | ||
| 15 | working-directory: ./client-sdk-android | 15 | working-directory: ./client-sdk-android |
| 16 | steps: | 16 | steps: |
| 17 | - name: checkout client-sdk-android | 17 | - name: checkout client-sdk-android |
| 18 | - uses: actions/checkout@v2.3.4 | 18 | + uses: actions/checkout@v4.0.0 |
| 19 | with: | 19 | with: |
| 20 | path: ./client-sdk-android | 20 | path: ./client-sdk-android |
| 21 | submodules: recursive | 21 | submodules: recursive |
| 22 | 22 | ||
| 23 | - name: set up JDK 12 | 23 | - name: set up JDK 12 |
| 24 | - uses: actions/setup-java@v2 | 24 | + uses: actions/setup-java@v3.12.0 |
| 25 | with: | 25 | with: |
| 26 | - java-version: '12' | 26 | + java-version: '17' |
| 27 | distribution: 'adopt' | 27 | distribution: 'adopt' |
| 28 | 28 | ||
| 29 | - name: Grant execute permission for gradlew | 29 | - name: Grant execute permission for gradlew |
| @@ -51,15 +51,17 @@ jobs: | @@ -51,15 +51,17 @@ jobs: | ||
| 51 | sed -i -e "s,signing.keyId=,signing.keyId=$GPG_KEY_ID,g" gradle.properties | 51 | sed -i -e "s,signing.keyId=,signing.keyId=$GPG_KEY_ID,g" gradle.properties |
| 52 | sed -i -e "s,signing.password=,signing.password=$GPG_PASSWORD,g" gradle.properties | 52 | sed -i -e "s,signing.password=,signing.password=$GPG_PASSWORD,g" gradle.properties |
| 53 | sed -i -e "s,signing.secretKeyRingFile=,signing.secretKeyRingFile=$GITHUB_WORKSPACE/release.gpg,g" gradle.properties | 53 | sed -i -e "s,signing.secretKeyRingFile=,signing.secretKeyRingFile=$GITHUB_WORKSPACE/release.gpg,g" gradle.properties |
| 54 | + sed -i -e "s,STAGING_PROFILE_ID=,STAGING_PROFILE_ID=$PROFILE_ID,g" gradle.properties | ||
| 54 | env: | 55 | env: |
| 55 | GPG_KEY_ARMOR: "${{ secrets.SIGNING_KEY_ARMOR }}" | 56 | GPG_KEY_ARMOR: "${{ secrets.SIGNING_KEY_ARMOR }}" |
| 56 | GPG_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} | 57 | GPG_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} |
| 57 | GPG_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} | 58 | GPG_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} |
| 58 | NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }} | 59 | NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }} |
| 59 | NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} | 60 | NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} |
| 61 | + PROFILE_ID: ${{ secrets.STAGING_PROFILE_ID }} | ||
| 60 | 62 | ||
| 61 | - name: Publish to sonatype | 63 | - name: Publish to sonatype |
| 62 | run: ./gradlew publish | 64 | run: ./gradlew publish |
| 63 | - | 65 | + |
| 64 | - name: Close and release to maven | 66 | - name: Close and release to maven |
| 65 | run: ./gradlew closeAndReleaseRepository | 67 | run: ./gradlew closeAndReleaseRepository |
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <project version="4"> | 2 | <project version="4"> |
| 3 | <component name="CompilerConfiguration"> | 3 | <component name="CompilerConfiguration"> |
| 4 | - <bytecodeTargetLevel target="1.8" /> | 4 | + <bytecodeTargetLevel target="17" /> |
| 5 | </component> | 5 | </component> |
| 6 | </project> | 6 | </project> |
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <project version="4"> | 2 | <project version="4"> |
| 3 | <component name="KotlinJpsPluginSettings"> | 3 | <component name="KotlinJpsPluginSettings"> |
| 4 | - <option name="version" value="1.7.10" /> | 4 | + <option name="version" value="1.8.20" /> |
| 5 | </component> | 5 | </component> |
| 6 | </project> | 6 | </project> |
| @@ -8,11 +8,11 @@ buildscript { | @@ -8,11 +8,11 @@ buildscript { | ||
| 8 | mavenCentral() | 8 | mavenCentral() |
| 9 | } | 9 | } |
| 10 | dependencies { | 10 | dependencies { |
| 11 | - classpath 'com.android.tools.build:gradle:7.2.2' | ||
| 12 | - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10" | 11 | + classpath "com.android.tools.build:gradle:$android_build_tools_version" |
| 12 | + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | ||
| 13 | classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" | 13 | classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" |
| 14 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" | 14 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" |
| 15 | - classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.19' | 15 | + classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.3' |
| 16 | classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0" | 16 | classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0" |
| 17 | classpath 'com.dicedmelon.gradle:jacoco-android:0.1.5' | 17 | classpath 'com.dicedmelon.gradle:jacoco-android:0.1.5' |
| 18 | classpath "com.diffplug.spotless:spotless-plugin-gradle:6.21.0" | 18 | classpath "com.diffplug.spotless:spotless-plugin-gradle:6.21.0" |
| @@ -72,7 +72,7 @@ task clean(type: Delete) { | @@ -72,7 +72,7 @@ task clean(type: Delete) { | ||
| 72 | nexusStaging { | 72 | nexusStaging { |
| 73 | serverUrl = "https://s01.oss.sonatype.org/service/local/" | 73 | serverUrl = "https://s01.oss.sonatype.org/service/local/" |
| 74 | packageGroup = GROUP | 74 | packageGroup = GROUP |
| 75 | - stagingProfileId = "16b57cbf143daa" | 75 | + stagingProfileId = STAGING_PROFILE_ID |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | afterEvaluate { | 78 | afterEvaluate { |
| 1 | ext { | 1 | ext { |
| 2 | - compose_version = '1.2.1' | ||
| 3 | - compose_compiler_version = '1.3.0' | ||
| 4 | - kotlin_version = '1.7.10' | ||
| 5 | - java_version = JavaVersion.VERSION_1_8 | 2 | + android_build_tools_version = '8.0.2' |
| 3 | + compose_version = '1.4.2' | ||
| 4 | + compose_compiler_version = '1.4.6' | ||
| 5 | + kotlin_version = '1.8.20' | ||
| 6 | + java_version = JavaVersion.VERSION_17 | ||
| 6 | dokka_version = '1.5.0' | 7 | dokka_version = '1.5.0' |
| 7 | androidSdk = [ | 8 | androidSdk = [ |
| 8 | compileVersion: 33, | 9 | compileVersion: 33, |
| @@ -10,21 +11,27 @@ ext { | @@ -10,21 +11,27 @@ ext { | ||
| 10 | minVersion : 21, | 11 | minVersion : 21, |
| 11 | ] | 12 | ] |
| 12 | versions = [ | 13 | versions = [ |
| 13 | - androidx_core : "1.8.0", | 14 | + androidx_core : "1.10.1", |
| 14 | androidx_lifecycle: "2.5.1", | 15 | androidx_lifecycle: "2.5.1", |
| 15 | autoService : '1.0.1', | 16 | autoService : '1.0.1', |
| 16 | - dagger : "2.43", | 17 | + coroutines : "1.6.0", |
| 18 | + dagger : "2.46", | ||
| 17 | groupie : "2.9.0", | 19 | groupie : "2.9.0", |
| 18 | junit : "4.13.2", | 20 | junit : "4.13.2", |
| 19 | junitJupiter : "5.5.0", | 21 | junitJupiter : "5.5.0", |
| 20 | - coroutines : "1.6.0", | ||
| 21 | lint : "30.0.1", | 22 | lint : "30.0.1", |
| 23 | + serialization : "1.5.0", | ||
| 22 | protobuf : "3.22.0", | 24 | protobuf : "3.22.0", |
| 23 | ] | 25 | ] |
| 24 | generated = [ | 26 | generated = [ |
| 25 | protoSrc: "$projectDir/protocol", | 27 | protoSrc: "$projectDir/protocol", |
| 26 | ] | 28 | ] |
| 27 | deps = [ | 29 | deps = [ |
| 30 | + androidx : [ | ||
| 31 | + 'annotation' : 'androidx.annotation:annotation:1.6.0', | ||
| 32 | + 'activity_compose' : 'androidx.activity:activity-compose:1.7.1', | ||
| 33 | + 'constraintlayout_compose': "androidx.constraintlayout:constraintlayout-compose:1.0.1", | ||
| 34 | + ], | ||
| 28 | auto : [ | 35 | auto : [ |
| 29 | 'service' : "com.google.auto.service:auto-service:${versions.autoService}", | 36 | 'service' : "com.google.auto.service:auto-service:${versions.autoService}", |
| 30 | 'serviceAnnotations': "com.google.auto.service:auto-service-annotations:${versions.autoService}", | 37 | 'serviceAnnotations': "com.google.auto.service:auto-service-annotations:${versions.autoService}", |
| @@ -33,7 +40,11 @@ ext { | @@ -33,7 +40,11 @@ ext { | ||
| 33 | "lib" : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}", | 40 | "lib" : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}", |
| 34 | "test": "org.jetbrains.kotlinx:kotlinx-coroutines-test: ${versions.coroutines}", | 41 | "test": "org.jetbrains.kotlinx:kotlinx-coroutines-test: ${versions.coroutines}", |
| 35 | ], | 42 | ], |
| 43 | + compose : [ | ||
| 44 | + "bom": "androidx.compose:compose-bom:2023.04.01", | ||
| 45 | + ], | ||
| 36 | timber : "com.github.ajalt:timberkt:1.5.1", | 46 | timber : "com.github.ajalt:timberkt:1.5.1", |
| 47 | + | ||
| 37 | // lint | 48 | // lint |
| 38 | lint : "com.android.tools.lint:lint:${versions.lint}", | 49 | lint : "com.android.tools.lint:lint:${versions.lint}", |
| 39 | lintApi : "com.android.tools.lint:lint-api:${versions.lint}", | 50 | lintApi : "com.android.tools.lint:lint-api:${versions.lint}", |
| @@ -41,9 +52,20 @@ ext { | @@ -41,9 +52,20 @@ ext { | ||
| 41 | lintTests : "com.android.tools.lint:lint-tests:${versions.lint}", | 52 | lintTests : "com.android.tools.lint:lint-tests:${versions.lint}", |
| 42 | 53 | ||
| 43 | // tests | 54 | // tests |
| 55 | + androidx_test : [ | ||
| 56 | + "core" : 'androidx.test:core:1.5.0', | ||
| 57 | + "junit": "androidx.test.ext:junit:1.1.5", | ||
| 58 | + ], | ||
| 59 | + espresso : 'androidx.test.espresso:espresso-core:3.5.1', | ||
| 44 | junit : "junit:junit:${versions.junit}", | 60 | junit : "junit:junit:${versions.junit}", |
| 45 | junitJupiterApi : "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}", | 61 | junitJupiterApi : "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}", |
| 46 | junitJupiterEngine: "org.junit.jupiter:junit-jupiter-engine:${versions.junitJupiter}", | 62 | junitJupiterEngine: "org.junit.jupiter:junit-jupiter-engine:${versions.junitJupiter}", |
| 63 | + mockito : [ | ||
| 64 | + "core" : 'org.mockito:mockito-core:4.0.0', | ||
| 65 | + "kotlin": "org.mockito.kotlin:mockito-kotlin:4.0.0", | ||
| 66 | + ], | ||
| 67 | + robolectric : 'org.robolectric:robolectric:4.10.2', | ||
| 68 | + | ||
| 47 | ] | 69 | ] |
| 48 | annotations = [ | 70 | annotations = [ |
| 49 | ] | 71 | ] |
| @@ -51,6 +51,8 @@ signing.keyId= | @@ -51,6 +51,8 @@ signing.keyId= | ||
| 51 | signing.password= | 51 | signing.password= |
| 52 | signing.secretKeyRingFile= | 52 | signing.secretKeyRingFile= |
| 53 | 53 | ||
| 54 | +STAGING_PROFILE_ID= | ||
| 55 | + | ||
| 54 | RELEASE_SIGNING_ENABLED=true | 56 | RELEASE_SIGNING_ENABLED=true |
| 55 | 57 | ||
| 56 | # For instrumented tests. | 58 | # For instrumented tests. |
| @@ -58,4 +60,4 @@ RELEASE_SIGNING_ENABLED=true | @@ -58,4 +60,4 @@ RELEASE_SIGNING_ENABLED=true | ||
| 58 | # Instead, override in ~/.gradle/gradle.properties | 60 | # Instead, override in ~/.gradle/gradle.properties |
| 59 | livekitUrl= | 61 | livekitUrl= |
| 60 | livekitApiKey= | 62 | livekitApiKey= |
| 61 | -livekitApiSecret= | ||
| 63 | +livekitApiSecret= |
| @@ -105,12 +105,12 @@ afterEvaluate { project -> | @@ -105,12 +105,12 @@ afterEvaluate { project -> | ||
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { | 107 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { |
| 108 | - classifier = 'javadoc' | 108 | + archiveClassifier = 'javadoc' |
| 109 | from androidJavadocs.destinationDir | 109 | from androidJavadocs.destinationDir |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | task androidSourcesJar(type: Jar) { | 112 | task androidSourcesJar(type: Jar) { |
| 113 | - classifier = 'sources' | 113 | + archiveClassifier = 'sources' |
| 114 | from android.sourceSets.main.java.source | 114 | from android.sourceSets.main.java.source |
| 115 | } | 115 | } |
| 116 | } | 116 | } |
| @@ -131,13 +131,13 @@ afterEvaluate { project -> | @@ -131,13 +131,13 @@ afterEvaluate { project -> | ||
| 131 | } | 131 | } |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | - artifacts { | ||
| 135 | - if (project.getPlugins().hasPlugin('com.android.application') || | ||
| 136 | - project.getPlugins().hasPlugin('com.android.library')) { | ||
| 137 | - archives androidSourcesJar | ||
| 138 | - archives androidJavadocsJar | ||
| 139 | - } | ||
| 140 | - } | 134 | +// artifacts { |
| 135 | +// if (project.getPlugins().hasPlugin('com.android.application') || | ||
| 136 | +// project.getPlugins().hasPlugin('com.android.library')) { | ||
| 137 | +// archives androidSourcesJar | ||
| 138 | +// archives androidJavadocsJar | ||
| 139 | +// } | ||
| 140 | +// } | ||
| 141 | 141 | ||
| 142 | android.libraryVariants.all { variant -> | 142 | android.libraryVariants.all { variant -> |
| 143 | tasks.androidJavadocs.doFirst { | 143 | tasks.androidJavadocs.doFirst { |
| @@ -149,8 +149,8 @@ afterEvaluate { project -> | @@ -149,8 +149,8 @@ afterEvaluate { project -> | ||
| 149 | publication.groupId = GROUP | 149 | publication.groupId = GROUP |
| 150 | publication.version = VERSION_NAME | 150 | publication.version = VERSION_NAME |
| 151 | 151 | ||
| 152 | - publication.artifact androidSourcesJar | ||
| 153 | - publication.artifact androidJavadocsJar | 152 | +// publication.artifact androidSourcesJar |
| 153 | +// publication.artifact androidJavadocsJar | ||
| 154 | 154 | ||
| 155 | configurePom(publication.pom) | 155 | configurePom(publication.pom) |
| 156 | } | 156 | } |
| @@ -162,4 +162,4 @@ afterEvaluate { project -> | @@ -162,4 +162,4 @@ afterEvaluate { project -> | ||
| 162 | } | 162 | } |
| 163 | } | 163 | } |
| 164 | } | 164 | } |
| 165 | -} | ||
| 165 | +} |
| 1 | -#Mon May 22 01:18:06 JST 2023 | 1 | +#Mon May 01 22:58:53 JST 2023 |
| 2 | distributionBase=GRADLE_USER_HOME | 2 | distributionBase=GRADLE_USER_HOME |
| 3 | distributionPath=wrapper/dists | 3 | distributionPath=wrapper/dists |
| 4 | -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip | 4 | +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip |
| 5 | zipStoreBase=GRADLE_USER_HOME | 5 | zipStoreBase=GRADLE_USER_HOME |
| 6 | zipStorePath=wrapper/dists | 6 | zipStorePath=wrapper/dists |
livekit-android-sdk/README.md
0 → 100644
| @@ -6,13 +6,12 @@ plugins { | @@ -6,13 +6,12 @@ plugins { | ||
| 6 | id 'kotlinx-serialization' | 6 | id 'kotlinx-serialization' |
| 7 | id 'com.google.protobuf' | 7 | id 'com.google.protobuf' |
| 8 | id 'jacoco' | 8 | id 'jacoco' |
| 9 | - id 'com.dicedmelon.gradle.jacoco-android' | 9 | + id("com.mxalbert.gradle.jacoco-android") version "0.2.1" |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | android { | 12 | android { |
| 13 | + namespace 'io.livekit.android' | ||
| 13 | compileSdkVersion androidSdk.compileVersion | 14 | compileSdkVersion androidSdk.compileVersion |
| 14 | - buildToolsVersion "30.0.3" | ||
| 15 | - | ||
| 16 | 15 | ||
| 17 | defaultConfig { | 16 | defaultConfig { |
| 18 | minSdkVersion androidSdk.minVersion | 17 | minSdkVersion androidSdk.minVersion |
| @@ -58,15 +57,20 @@ android { | @@ -58,15 +57,20 @@ android { | ||
| 58 | } | 57 | } |
| 59 | 58 | ||
| 60 | buildFeatures { | 59 | buildFeatures { |
| 61 | - compose true | ||
| 62 | - } | ||
| 63 | - composeOptions { | ||
| 64 | - kotlinCompilerExtensionVersion compose_compiler_version | 60 | + buildConfig = true |
| 65 | } | 61 | } |
| 66 | kotlinOptions { | 62 | kotlinOptions { |
| 67 | freeCompilerArgs = ["-Xinline-classes", "-opt-in=kotlin.RequiresOptIn"] | 63 | freeCompilerArgs = ["-Xinline-classes", "-opt-in=kotlin.RequiresOptIn"] |
| 68 | jvmTarget = java_version | 64 | jvmTarget = java_version |
| 69 | } | 65 | } |
| 66 | + | ||
| 67 | + publishing { | ||
| 68 | + singleVariant("release") { | ||
| 69 | + withJavadocJar() | ||
| 70 | + withSourcesJar() | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | + | ||
| 70 | } | 74 | } |
| 71 | 75 | ||
| 72 | protobuf { | 76 | protobuf { |
| @@ -100,7 +104,7 @@ tasks.withType(Test) { | @@ -100,7 +104,7 @@ tasks.withType(Test) { | ||
| 100 | } | 104 | } |
| 101 | 105 | ||
| 102 | jacocoAndroidUnitTestReport { | 106 | jacocoAndroidUnitTestReport { |
| 103 | - excludes += ['livekit/**',] | 107 | + excludes.add('livekit/**') |
| 104 | } | 108 | } |
| 105 | 109 | ||
| 106 | dokkaHtml { | 110 | dokkaHtml { |
| @@ -116,7 +120,7 @@ dokkaHtml { | @@ -116,7 +120,7 @@ dokkaHtml { | ||
| 116 | 120 | ||
| 117 | // URL showing where the source code can be accessed through the web browser | 121 | // URL showing where the source code can be accessed through the web browser |
| 118 | remoteUrl.set(new URL( | 122 | remoteUrl.set(new URL( |
| 119 | - "https://github.com/livekit/client-sdk-android/tree/master/livekit-android-sdk/src/main/java")) | 123 | + "https://github.com/livekit/client-sdk-android/tree/master/livekit-android-sdk/src/main/java")) |
| 120 | // Suffix which is used to append the line number to the URL. Use #L for GitHub | 124 | // Suffix which is used to append the line number to the URL. Use #L for GitHub |
| 121 | remoteLineSuffix.set("#L") | 125 | remoteLineSuffix.set("#L") |
| 122 | } | 126 | } |
| @@ -138,14 +142,13 @@ dependencies { | @@ -138,14 +142,13 @@ dependencies { | ||
| 138 | //api fileTree(dir: 'libs', include: ['*.jar']) | 142 | //api fileTree(dir: 'libs', include: ['*.jar']) |
| 139 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | 143 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" |
| 140 | implementation deps.coroutines.lib | 144 | implementation deps.coroutines.lib |
| 141 | - implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0' | 145 | + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:${versions.serialization}" |
| 142 | api 'io.github.webrtc-sdk:android:114.5735.05' | 146 | api 'io.github.webrtc-sdk:android:114.5735.05' |
| 143 | api "com.squareup.okhttp3:okhttp:4.10.0" | 147 | api "com.squareup.okhttp3:okhttp:4.10.0" |
| 144 | api 'com.github.davidliu:audioswitch:d18e3e31d427c27f1593030e024b370bf24480fd' | 148 | api 'com.github.davidliu:audioswitch:d18e3e31d427c27f1593030e024b370bf24480fd' |
| 145 | - implementation "androidx.annotation:annotation:1.4.0" | 149 | + implementation deps.androidx.annotation |
| 146 | implementation "androidx.core:core:${versions.androidx_core}" | 150 | implementation "androidx.core:core:${versions.androidx_core}" |
| 147 | implementation "com.google.protobuf:protobuf-javalite:${versions.protobuf}" | 151 | implementation "com.google.protobuf:protobuf-javalite:${versions.protobuf}" |
| 148 | - implementation "androidx.compose.ui:ui:$compose_version" | ||
| 149 | 152 | ||
| 150 | implementation "com.google.dagger:dagger:${versions.dagger}" | 153 | implementation "com.google.dagger:dagger:${versions.dagger}" |
| 151 | kapt "com.google.dagger:dagger-compiler:${versions.dagger}" | 154 | kapt "com.google.dagger:dagger-compiler:${versions.dagger}" |
| @@ -156,15 +159,15 @@ dependencies { | @@ -156,15 +159,15 @@ dependencies { | ||
| 156 | lintChecks project(':livekit-lint') | 159 | lintChecks project(':livekit-lint') |
| 157 | lintPublish project(':livekit-lint') | 160 | lintPublish project(':livekit-lint') |
| 158 | 161 | ||
| 159 | - testImplementation 'junit:junit:4.13.2' | ||
| 160 | - testImplementation 'org.robolectric:robolectric:4.10.2' | ||
| 161 | - testImplementation 'org.mockito:mockito-core:4.0.0' | ||
| 162 | - testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0" | ||
| 163 | - testImplementation 'androidx.test:core:1.4.0' | 162 | + testImplementation deps.junit |
| 163 | + testImplementation deps.robolectric | ||
| 164 | + testImplementation deps.mockito.core | ||
| 165 | + testImplementation deps.mockito.kotlin | ||
| 166 | + testImplementation deps.androidx_test.core | ||
| 164 | testImplementation deps.coroutines.test | 167 | testImplementation deps.coroutines.test |
| 165 | kaptTest "com.google.dagger:dagger-compiler:${versions.dagger}" | 168 | kaptTest "com.google.dagger:dagger-compiler:${versions.dagger}" |
| 166 | - androidTestImplementation 'androidx.test.ext:junit:1.1.3' | ||
| 167 | - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' | 169 | + androidTestImplementation deps.androidx_test.junit |
| 170 | + androidTestImplementation deps.espresso | ||
| 168 | } | 171 | } |
| 169 | 172 | ||
| 170 | apply from: rootProject.file('gradle/gradle-mvn-push.gradle') | 173 | apply from: rootProject.file('gradle/gradle-mvn-push.gradle') |
| @@ -14,8 +14,7 @@ | @@ -14,8 +14,7 @@ | ||
| 14 | limitations under the License. | 14 | limitations under the License. |
| 15 | --> | 15 | --> |
| 16 | 16 | ||
| 17 | -<manifest package="io.livekit.android" | ||
| 18 | - xmlns:android="http://schemas.android.com/apk/res/android"> | 17 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
| 19 | 18 | ||
| 20 | <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | 19 | <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
| 21 | <uses-permission android:name="android.permission.INTERNET" /> | 20 | <uses-permission android:name="android.permission.INTERNET" /> |
| @@ -19,7 +19,6 @@ package io.livekit.android.room.track | @@ -19,7 +19,6 @@ package io.livekit.android.room.track | ||
| 19 | import android.view.View | 19 | import android.view.View |
| 20 | import io.livekit.android.dagger.InjectionNames | 20 | import io.livekit.android.dagger.InjectionNames |
| 21 | import io.livekit.android.events.TrackEvent | 21 | import io.livekit.android.events.TrackEvent |
| 22 | -import io.livekit.android.room.track.video.ComposeVisibility | ||
| 23 | import io.livekit.android.room.track.video.VideoSinkVisibility | 22 | import io.livekit.android.room.track.video.VideoSinkVisibility |
| 24 | import io.livekit.android.room.track.video.ViewVisibility | 23 | import io.livekit.android.room.track.video.ViewVisibility |
| 25 | import io.livekit.android.util.LKLog | 24 | import io.livekit.android.util.LKLog |
| @@ -55,7 +54,7 @@ class RemoteVideoTrack( | @@ -55,7 +54,7 @@ class RemoteVideoTrack( | ||
| 55 | 54 | ||
| 56 | /** | 55 | /** |
| 57 | * If `autoManageVideo` is enabled, a VideoSinkVisibility should be passed, using | 56 | * If `autoManageVideo` is enabled, a VideoSinkVisibility should be passed, using |
| 58 | - * [ViewVisibility] if using a traditional View layout, or [ComposeVisibility] | 57 | + * [ViewVisibility] if using a traditional View layout, or ComposeVisibility |
| 59 | * if using Jetpack Compose. | 58 | * if using Jetpack Compose. |
| 60 | * | 59 | * |
| 61 | * By default, any Views passed to this method will be added with a [ViewVisibility]. | 60 | * By default, any Views passed to this method will be added with a [ViewVisibility]. |
| @@ -22,10 +22,9 @@ import android.os.Looper | @@ -22,10 +22,9 @@ import android.os.Looper | ||
| 22 | import android.view.View | 22 | import android.view.View |
| 23 | import android.view.ViewTreeObserver | 23 | import android.view.ViewTreeObserver |
| 24 | import androidx.annotation.CallSuper | 24 | import androidx.annotation.CallSuper |
| 25 | -import androidx.compose.ui.layout.LayoutCoordinates | ||
| 26 | import io.livekit.android.room.track.Track | 25 | import io.livekit.android.room.track.Track |
| 27 | import io.livekit.android.room.track.video.ViewVisibility.Notifier | 26 | import io.livekit.android.room.track.video.ViewVisibility.Notifier |
| 28 | -import java.util.* | 27 | +import java.util.Observable |
| 29 | 28 | ||
| 30 | abstract class VideoSinkVisibility : Observable() { | 29 | abstract class VideoSinkVisibility : Observable() { |
| 31 | abstract fun isVisible(): Boolean | 30 | abstract fun isVisible(): Boolean |
| @@ -48,46 +47,6 @@ abstract class VideoSinkVisibility : Observable() { | @@ -48,46 +47,6 @@ abstract class VideoSinkVisibility : Observable() { | ||
| 48 | } | 47 | } |
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | -class ComposeVisibility : VideoSinkVisibility() { | ||
| 52 | - private var coordinates: LayoutCoordinates? = null | ||
| 53 | - | ||
| 54 | - private var lastVisible = isVisible() | ||
| 55 | - private var lastSize = size() | ||
| 56 | - override fun isVisible(): Boolean { | ||
| 57 | - return (coordinates?.isAttached == true && | ||
| 58 | - coordinates?.size?.width != 0 && | ||
| 59 | - coordinates?.size?.height != 0) | ||
| 60 | - } | ||
| 61 | - | ||
| 62 | - override fun size(): Track.Dimensions { | ||
| 63 | - val width = coordinates?.size?.width ?: 0 | ||
| 64 | - val height = coordinates?.size?.height ?: 0 | ||
| 65 | - return Track.Dimensions(width, height) | ||
| 66 | - } | ||
| 67 | - | ||
| 68 | - // Note, LayoutCoordinates are mutable and may be reused. | ||
| 69 | - fun onGloballyPositioned(layoutCoordinates: LayoutCoordinates) { | ||
| 70 | - coordinates = layoutCoordinates | ||
| 71 | - val visible = isVisible() | ||
| 72 | - val size = size() | ||
| 73 | - | ||
| 74 | - if (lastVisible != visible || lastSize != size) { | ||
| 75 | - notifyChanged() | ||
| 76 | - } | ||
| 77 | - | ||
| 78 | - lastVisible = visible | ||
| 79 | - lastSize = size | ||
| 80 | - } | ||
| 81 | - | ||
| 82 | - fun onDispose() { | ||
| 83 | - if (coordinates == null) { | ||
| 84 | - return | ||
| 85 | - } | ||
| 86 | - coordinates = null | ||
| 87 | - notifyChanged() | ||
| 88 | - } | ||
| 89 | -} | ||
| 90 | - | ||
| 91 | /** | 50 | /** |
| 92 | * A [VideoSinkVisibility] for views. If using a custom view other than the sdk provided renderers, | 51 | * A [VideoSinkVisibility] for views. If using a custom view other than the sdk provided renderers, |
| 93 | * you must implement [Notifier], override [View.onVisibilityChanged] and call through to [recalculate], or | 52 | * you must implement [Notifier], override [View.onVisibilityChanged] and call through to [recalculate], or |
| @@ -102,7 +61,6 @@ class ViewVisibility(private val view: View) : VideoSinkVisibility() { | @@ -102,7 +61,6 @@ class ViewVisibility(private val view: View) : VideoSinkVisibility() { | ||
| 102 | private val globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener { | 61 | private val globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener { |
| 103 | scheduleRecalculate() | 62 | scheduleRecalculate() |
| 104 | } | 63 | } |
| 105 | - | ||
| 106 | private val scrollListener = ViewTreeObserver.OnScrollChangedListener { | 64 | private val scrollListener = ViewTreeObserver.OnScrollChangedListener { |
| 107 | scheduleRecalculate() | 65 | scheduleRecalculate() |
| 108 | } | 66 | } |
| @@ -36,7 +36,11 @@ limitations under the License. | @@ -36,7 +36,11 @@ limitations under the License. | ||
| 36 | Original repo can be found at: https://github.com/ajalt/LKLogkt | 36 | Original repo can be found at: https://github.com/ajalt/LKLogkt |
| 37 | */ | 37 | */ |
| 38 | 38 | ||
| 39 | -internal class LKLog { | 39 | +/** |
| 40 | + * @suppress | ||
| 41 | + */ | ||
| 42 | +@Suppress("NOTHING_TO_INLINE", "unused") | ||
| 43 | +class LKLog { | ||
| 40 | 44 | ||
| 41 | companion object { | 45 | companion object { |
| 42 | var loggingLevel = OFF | 46 | var loggingLevel = OFF |
| @@ -90,7 +94,7 @@ internal class LKLog { | @@ -90,7 +94,7 @@ internal class LKLog { | ||
| 90 | inline fun wtf(t: Throwable?) = log(WTF) { Timber.wtf(t) } | 94 | inline fun wtf(t: Throwable?) = log(WTF) { Timber.wtf(t) } |
| 91 | 95 | ||
| 92 | /** @suppress */ | 96 | /** @suppress */ |
| 93 | - internal inline fun log(loggingLevel: LoggingLevel, block: () -> Unit) { | 97 | + inline fun log(loggingLevel: LoggingLevel, block: () -> Unit) { |
| 94 | if (loggingLevel >= LKLog.loggingLevel && Timber.treeCount() > 0) block() | 98 | if (loggingLevel >= LKLog.loggingLevel && Timber.treeCount() > 0) block() |
| 95 | } | 99 | } |
| 96 | } | 100 | } |
| @@ -159,6 +159,7 @@ class MockPeerConnection( | @@ -159,6 +159,7 @@ class MockPeerConnection( | ||
| 159 | return super.addTransceiver(mediaType, init) | 159 | return super.addTransceiver(mediaType, init) |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | + @Deprecated("Deprecated in Java") | ||
| 162 | override fun getStats(observer: StatsObserver?, track: MediaStreamTrack?): Boolean { | 163 | override fun getStats(observer: StatsObserver?, track: MediaStreamTrack?): Boolean { |
| 163 | observer?.onComplete(emptyArray()) | 164 | observer?.onComplete(emptyArray()) |
| 164 | return true | 165 | return true |
| @@ -5,8 +5,8 @@ plugins { | @@ -5,8 +5,8 @@ plugins { | ||
| 5 | } | 5 | } |
| 6 | 6 | ||
| 7 | java { | 7 | java { |
| 8 | - sourceCompatibility = JavaVersion.VERSION_1_8 | ||
| 9 | - targetCompatibility = JavaVersion.VERSION_1_8 | 8 | + sourceCompatibility = java_version |
| 9 | + targetCompatibility = java_version | ||
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | dependencies { | 12 | dependencies { |
| @@ -4,12 +4,13 @@ plugins { | @@ -4,12 +4,13 @@ plugins { | ||
| 4 | } | 4 | } |
| 5 | 5 | ||
| 6 | android { | 6 | android { |
| 7 | - compileSdk 32 | 7 | + namespace 'io.livekit.android.sample.basic' |
| 8 | + compileSdk 33 | ||
| 8 | 9 | ||
| 9 | defaultConfig { | 10 | defaultConfig { |
| 10 | applicationId "io.livekit.android.sample.basic" | 11 | applicationId "io.livekit.android.sample.basic" |
| 11 | minSdk 21 | 12 | minSdk 21 |
| 12 | - targetSdk 32 | 13 | + targetSdk 33 |
| 13 | versionCode 1 | 14 | versionCode 1 |
| 14 | versionName "1.0" | 15 | versionName "1.0" |
| 15 | 16 | ||
| @@ -23,11 +24,11 @@ android { | @@ -23,11 +24,11 @@ android { | ||
| 23 | } | 24 | } |
| 24 | } | 25 | } |
| 25 | compileOptions { | 26 | compileOptions { |
| 26 | - sourceCompatibility JavaVersion.VERSION_1_8 | ||
| 27 | - targetCompatibility JavaVersion.VERSION_1_8 | 27 | + sourceCompatibility java_version |
| 28 | + targetCompatibility java_version | ||
| 28 | } | 29 | } |
| 29 | kotlinOptions { | 30 | kotlinOptions { |
| 30 | - jvmTarget = '1.8' | 31 | + jvmTarget = java_version |
| 31 | } | 32 | } |
| 32 | } | 33 | } |
| 33 | 34 |
| @@ -15,6 +15,7 @@ final url = getDefaultUrl() | @@ -15,6 +15,7 @@ final url = getDefaultUrl() | ||
| 15 | final token = getDefaultToken() | 15 | final token = getDefaultToken() |
| 16 | 16 | ||
| 17 | android { | 17 | android { |
| 18 | + namespace "io.livekit.android.sample.common" | ||
| 18 | compileSdk androidSdk.compileVersion | 19 | compileSdk androidSdk.compileVersion |
| 19 | 20 | ||
| 20 | defaultConfig { | 21 | defaultConfig { |
| @@ -39,12 +40,15 @@ android { | @@ -39,12 +40,15 @@ android { | ||
| 39 | buildConfigField "String", "DEFAULT_TOKEN", "\"$token\"" | 40 | buildConfigField "String", "DEFAULT_TOKEN", "\"$token\"" |
| 40 | } | 41 | } |
| 41 | } | 42 | } |
| 43 | + buildFeatures { | ||
| 44 | + buildConfig = true | ||
| 45 | + } | ||
| 42 | compileOptions { | 46 | compileOptions { |
| 43 | - sourceCompatibility JavaVersion.VERSION_1_8 | ||
| 44 | - targetCompatibility JavaVersion.VERSION_1_8 | 47 | + sourceCompatibility java_version |
| 48 | + targetCompatibility java_version | ||
| 45 | } | 49 | } |
| 46 | kotlinOptions { | 50 | kotlinOptions { |
| 47 | - jvmTarget = '1.8' | 51 | + jvmTarget = java_version |
| 48 | } | 52 | } |
| 49 | } | 53 | } |
| 50 | 54 |
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | -<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | - package="io.livekit.android.sample.common"> | 2 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
| 4 | 3 | ||
| 5 | <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | 4 | <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
| 6 | <uses-permission android:name="android.permission.INTERNET" /> | 5 | <uses-permission android:name="android.permission.INTERNET" /> |
| @@ -111,6 +111,8 @@ class CallViewModel( | @@ -111,6 +111,8 @@ class CallViewModel( | ||
| 111 | private val mutablePermissionAllowed = MutableStateFlow(true) | 111 | private val mutablePermissionAllowed = MutableStateFlow(true) |
| 112 | val permissionAllowed = mutablePermissionAllowed.hide() | 112 | val permissionAllowed = mutablePermissionAllowed.hide() |
| 113 | 113 | ||
| 114 | + var messagesReceived = 0 | ||
| 115 | + | ||
| 114 | init { | 116 | init { |
| 115 | viewModelScope.launch { | 117 | viewModelScope.launch { |
| 116 | // Collect any errors. | 118 | // Collect any errors. |
| @@ -137,14 +139,9 @@ class CallViewModel( | @@ -137,14 +139,9 @@ class CallViewModel( | ||
| 137 | is RoomEvent.FailedToConnect -> mutableError.value = it.error | 139 | is RoomEvent.FailedToConnect -> mutableError.value = it.error |
| 138 | is RoomEvent.DataReceived -> { | 140 | is RoomEvent.DataReceived -> { |
| 139 | val identity = it.participant?.identity ?: "server" | 141 | val identity = it.participant?.identity ?: "server" |
| 140 | - val message = it.data.toString(Charsets.UTF_8) | ||
| 141 | - mutableDataReceived.emit("$identity: $message") | ||
| 142 | - } | ||
| 143 | - | ||
| 144 | - is RoomEvent.TrackSubscribed -> { | ||
| 145 | - launch { collectTrackStats(it) } | 142 | + messagesReceived++ |
| 143 | + Timber.e { "message received from $identity, count $messagesReceived" } | ||
| 146 | } | 144 | } |
| 147 | - | ||
| 148 | else -> { | 145 | else -> { |
| 149 | Timber.e { "Room event: $it" } | 146 | Timber.e { "Room event: $it" } |
| 150 | } | 147 | } |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.composesample | 17 | package io.livekit.android.composesample |
| 2 | 18 | ||
| 3 | import android.app.Activity | 19 | import android.app.Activity |
| @@ -31,6 +47,7 @@ import io.livekit.android.composesample.ui.theme.AppTheme | @@ -31,6 +47,7 @@ import io.livekit.android.composesample.ui.theme.AppTheme | ||
| 31 | import io.livekit.android.room.Room | 47 | import io.livekit.android.room.Room |
| 32 | import io.livekit.android.room.participant.Participant | 48 | import io.livekit.android.room.participant.Participant |
| 33 | import io.livekit.android.sample.CallViewModel | 49 | import io.livekit.android.sample.CallViewModel |
| 50 | +import io.livekit.android.sample.common.R | ||
| 34 | import kotlinx.coroutines.Dispatchers | 51 | import kotlinx.coroutines.Dispatchers |
| 35 | import kotlinx.coroutines.launch | 52 | import kotlinx.coroutines.launch |
| 36 | import kotlinx.parcelize.Parcelize | 53 | import kotlinx.parcelize.Parcelize |
| @@ -45,13 +62,13 @@ class CallActivity : AppCompatActivity() { | @@ -45,13 +62,13 @@ class CallActivity : AppCompatActivity() { | ||
| 45 | token = args.token, | 62 | token = args.token, |
| 46 | e2ee = args.e2eeOn, | 63 | e2ee = args.e2eeOn, |
| 47 | e2eeKey = args.e2eeKey, | 64 | e2eeKey = args.e2eeKey, |
| 48 | - application = application | 65 | + application = application, |
| 49 | ) | 66 | ) |
| 50 | } | 67 | } |
| 51 | 68 | ||
| 52 | private val screenCaptureIntentLauncher = | 69 | private val screenCaptureIntentLauncher = |
| 53 | registerForActivityResult( | 70 | registerForActivityResult( |
| 54 | - ActivityResultContracts.StartActivityForResult() | 71 | + ActivityResultContracts.StartActivityForResult(), |
| 55 | ) { result -> | 72 | ) { result -> |
| 56 | val resultCode = result.resultCode | 73 | val resultCode = result.resultCode |
| 57 | val data = result.data | 74 | val data = result.data |
| @@ -146,24 +163,26 @@ class CallActivity : AppCompatActivity() { | @@ -146,24 +163,26 @@ class CallActivity : AppCompatActivity() { | ||
| 146 | ConstraintLayout( | 163 | ConstraintLayout( |
| 147 | modifier = Modifier | 164 | modifier = Modifier |
| 148 | .fillMaxSize() | 165 | .fillMaxSize() |
| 149 | - .background(MaterialTheme.colors.background) | 166 | + .background(MaterialTheme.colors.background), |
| 150 | ) { | 167 | ) { |
| 151 | val (speakerView, audienceRow, buttonBar) = createRefs() | 168 | val (speakerView, audienceRow, buttonBar) = createRefs() |
| 152 | 169 | ||
| 153 | // Primary speaker view | 170 | // Primary speaker view |
| 154 | - Surface(modifier = Modifier.constrainAs(speakerView) { | ||
| 155 | - top.linkTo(parent.top) | ||
| 156 | - start.linkTo(parent.start) | ||
| 157 | - end.linkTo(parent.end) | ||
| 158 | - bottom.linkTo(audienceRow.top) | ||
| 159 | - width = Dimension.fillToConstraints | ||
| 160 | - height = Dimension.fillToConstraints | ||
| 161 | - }) { | 171 | + Surface( |
| 172 | + modifier = Modifier.constrainAs(speakerView) { | ||
| 173 | + top.linkTo(parent.top) | ||
| 174 | + start.linkTo(parent.start) | ||
| 175 | + end.linkTo(parent.end) | ||
| 176 | + bottom.linkTo(audienceRow.top) | ||
| 177 | + width = Dimension.fillToConstraints | ||
| 178 | + height = Dimension.fillToConstraints | ||
| 179 | + }, | ||
| 180 | + ) { | ||
| 162 | if (room != null && primarySpeaker != null) { | 181 | if (room != null && primarySpeaker != null) { |
| 163 | ParticipantItem( | 182 | ParticipantItem( |
| 164 | room = room, | 183 | room = room, |
| 165 | participant = primarySpeaker, | 184 | participant = primarySpeaker, |
| 166 | - isSpeaking = activeSpeakers.contains(primarySpeaker) | 185 | + isSpeaking = activeSpeakers.contains(primarySpeaker), |
| 167 | ) | 186 | ) |
| 168 | } | 187 | } |
| 169 | } | 188 | } |
| @@ -178,12 +197,12 @@ class CallActivity : AppCompatActivity() { | @@ -178,12 +197,12 @@ class CallActivity : AppCompatActivity() { | ||
| 178 | end.linkTo(parent.end) | 197 | end.linkTo(parent.end) |
| 179 | width = Dimension.fillToConstraints | 198 | width = Dimension.fillToConstraints |
| 180 | height = Dimension.value(120.dp) | 199 | height = Dimension.value(120.dp) |
| 181 | - } | 200 | + }, |
| 182 | ) { | 201 | ) { |
| 183 | if (room != null) { | 202 | if (room != null) { |
| 184 | items( | 203 | items( |
| 185 | count = participants.size, | 204 | count = participants.size, |
| 186 | - key = { index -> participants[index].sid } | 205 | + key = { index -> participants[index].sid }, |
| 187 | ) { index -> | 206 | ) { index -> |
| 188 | ParticipantItem( | 207 | ParticipantItem( |
| 189 | room = room, | 208 | room = room, |
| @@ -191,7 +210,7 @@ class CallActivity : AppCompatActivity() { | @@ -191,7 +210,7 @@ class CallActivity : AppCompatActivity() { | ||
| 191 | isSpeaking = activeSpeakers.contains(participants[index]), | 210 | isSpeaking = activeSpeakers.contains(participants[index]), |
| 192 | modifier = Modifier | 211 | modifier = Modifier |
| 193 | .fillMaxHeight() | 212 | .fillMaxHeight() |
| 194 | - .aspectRatio(1.0f, true) | 213 | + .aspectRatio(1.0f, true), |
| 195 | ) | 214 | ) |
| 196 | } | 215 | } |
| 197 | } | 216 | } |
| @@ -208,7 +227,7 @@ class CallActivity : AppCompatActivity() { | @@ -208,7 +227,7 @@ class CallActivity : AppCompatActivity() { | ||
| 208 | height = Dimension.wrapContent | 227 | height = Dimension.wrapContent |
| 209 | }, | 228 | }, |
| 210 | verticalArrangement = Arrangement.SpaceEvenly, | 229 | verticalArrangement = Arrangement.SpaceEvenly, |
| 211 | - horizontalAlignment = Alignment.CenterHorizontally | 230 | + horizontalAlignment = Alignment.CenterHorizontally, |
| 212 | ) { | 231 | ) { |
| 213 | val controlSize = 40.dp | 232 | val controlSize = 40.dp |
| 214 | val controlPadding = 4.dp | 233 | val controlPadding = 4.dp |
| @@ -221,7 +240,7 @@ class CallActivity : AppCompatActivity() { | @@ -221,7 +240,7 @@ class CallActivity : AppCompatActivity() { | ||
| 221 | onClick = { viewModel.setMicEnabled(!micEnabled) }, | 240 | onClick = { viewModel.setMicEnabled(!micEnabled) }, |
| 222 | modifier = Modifier | 241 | modifier = Modifier |
| 223 | .size(controlSize) | 242 | .size(controlSize) |
| 224 | - .padding(controlPadding) | 243 | + .padding(controlPadding), |
| 225 | ) { | 244 | ) { |
| 226 | val resource = | 245 | val resource = |
| 227 | if (micEnabled) R.drawable.outline_mic_24 else R.drawable.outline_mic_off_24 | 246 | if (micEnabled) R.drawable.outline_mic_24 else R.drawable.outline_mic_off_24 |
| @@ -235,7 +254,7 @@ class CallActivity : AppCompatActivity() { | @@ -235,7 +254,7 @@ class CallActivity : AppCompatActivity() { | ||
| 235 | onClick = { viewModel.setCameraEnabled(!videoEnabled) }, | 254 | onClick = { viewModel.setCameraEnabled(!videoEnabled) }, |
| 236 | modifier = Modifier | 255 | modifier = Modifier |
| 237 | .size(controlSize) | 256 | .size(controlSize) |
| 238 | - .padding(controlPadding) | 257 | + .padding(controlPadding), |
| 239 | ) { | 258 | ) { |
| 240 | val resource = | 259 | val resource = |
| 241 | if (videoEnabled) R.drawable.outline_videocam_24 else R.drawable.outline_videocam_off_24 | 260 | if (videoEnabled) R.drawable.outline_videocam_24 else R.drawable.outline_videocam_off_24 |
| @@ -249,7 +268,7 @@ class CallActivity : AppCompatActivity() { | @@ -249,7 +268,7 @@ class CallActivity : AppCompatActivity() { | ||
| 249 | onClick = { viewModel.flipCamera() }, | 268 | onClick = { viewModel.flipCamera() }, |
| 250 | modifier = Modifier | 269 | modifier = Modifier |
| 251 | .size(controlSize) | 270 | .size(controlSize) |
| 252 | - .padding(controlPadding) | 271 | + .padding(controlPadding), |
| 253 | ) { | 272 | ) { |
| 254 | Icon( | 273 | Icon( |
| 255 | painterResource(id = R.drawable.outline_flip_camera_android_24), | 274 | painterResource(id = R.drawable.outline_flip_camera_android_24), |
| @@ -267,7 +286,7 @@ class CallActivity : AppCompatActivity() { | @@ -267,7 +286,7 @@ class CallActivity : AppCompatActivity() { | ||
| 267 | }, | 286 | }, |
| 268 | modifier = Modifier | 287 | modifier = Modifier |
| 269 | .size(controlSize) | 288 | .size(controlSize) |
| 270 | - .padding(controlPadding) | 289 | + .padding(controlPadding), |
| 271 | ) { | 290 | ) { |
| 272 | val resource = | 291 | val resource = |
| 273 | if (screencastEnabled) R.drawable.baseline_cast_connected_24 else R.drawable.baseline_cast_24 | 292 | if (screencastEnabled) R.drawable.baseline_cast_connected_24 else R.drawable.baseline_cast_24 |
| @@ -284,7 +303,7 @@ class CallActivity : AppCompatActivity() { | @@ -284,7 +303,7 @@ class CallActivity : AppCompatActivity() { | ||
| 284 | onClick = { showMessageDialog = true }, | 303 | onClick = { showMessageDialog = true }, |
| 285 | modifier = Modifier | 304 | modifier = Modifier |
| 286 | .size(controlSize) | 305 | .size(controlSize) |
| 287 | - .padding(controlPadding) | 306 | + .padding(controlPadding), |
| 288 | ) { | 307 | ) { |
| 289 | Icon( | 308 | Icon( |
| 290 | painterResource(id = R.drawable.baseline_chat_24), | 309 | painterResource(id = R.drawable.baseline_chat_24), |
| @@ -316,7 +335,7 @@ class CallActivity : AppCompatActivity() { | @@ -316,7 +335,7 @@ class CallActivity : AppCompatActivity() { | ||
| 316 | onSendMessage(messageToSend) | 335 | onSendMessage(messageToSend) |
| 317 | showMessageDialog = false | 336 | showMessageDialog = false |
| 318 | messageToSend = "" | 337 | messageToSend = "" |
| 319 | - } | 338 | + }, |
| 320 | ) { Text("Send") } | 339 | ) { Text("Send") } |
| 321 | }, | 340 | }, |
| 322 | dismissButton = { | 341 | dismissButton = { |
| @@ -324,7 +343,7 @@ class CallActivity : AppCompatActivity() { | @@ -324,7 +343,7 @@ class CallActivity : AppCompatActivity() { | ||
| 324 | onClick = { | 343 | onClick = { |
| 325 | showMessageDialog = false | 344 | showMessageDialog = false |
| 326 | messageToSend = "" | 345 | messageToSend = "" |
| 327 | - } | 346 | + }, |
| 328 | ) { Text("Cancel") } | 347 | ) { Text("Cancel") } |
| 329 | }, | 348 | }, |
| 330 | backgroundColor = Color.Black, | 349 | backgroundColor = Color.Black, |
| @@ -334,7 +353,7 @@ class CallActivity : AppCompatActivity() { | @@ -334,7 +353,7 @@ class CallActivity : AppCompatActivity() { | ||
| 334 | onClick = { onExitClick() }, | 353 | onClick = { onExitClick() }, |
| 335 | modifier = Modifier | 354 | modifier = Modifier |
| 336 | .size(controlSize) | 355 | .size(controlSize) |
| 337 | - .padding(controlPadding) | 356 | + .padding(controlPadding), |
| 338 | ) { | 357 | ) { |
| 339 | Icon( | 358 | Icon( |
| 340 | painterResource(id = R.drawable.ic_baseline_cancel_24), | 359 | painterResource(id = R.drawable.ic_baseline_cancel_24), |
| @@ -356,7 +375,7 @@ class CallActivity : AppCompatActivity() { | @@ -356,7 +375,7 @@ class CallActivity : AppCompatActivity() { | ||
| 356 | onClick = { showAudioDeviceDialog = true }, | 375 | onClick = { showAudioDeviceDialog = true }, |
| 357 | modifier = Modifier | 376 | modifier = Modifier |
| 358 | .size(controlSize) | 377 | .size(controlSize) |
| 359 | - .padding(controlPadding) | 378 | + .padding(controlPadding), |
| 360 | ) { | 379 | ) { |
| 361 | val resource = R.drawable.volume_up_48px | 380 | val resource = R.drawable.volume_up_48px |
| 362 | Icon( | 381 | Icon( |
| @@ -370,14 +389,14 @@ class CallActivity : AppCompatActivity() { | @@ -370,14 +389,14 @@ class CallActivity : AppCompatActivity() { | ||
| 370 | onDismissRequest = { showAudioDeviceDialog = false }, | 389 | onDismissRequest = { showAudioDeviceDialog = false }, |
| 371 | selectDevice = { audioSwitchHandler?.selectDevice(it) }, | 390 | selectDevice = { audioSwitchHandler?.selectDevice(it) }, |
| 372 | currentDevice = audioSwitchHandler?.selectedAudioDevice, | 391 | currentDevice = audioSwitchHandler?.selectedAudioDevice, |
| 373 | - availableDevices = audioSwitchHandler?.availableAudioDevices ?: emptyList() | 392 | + availableDevices = audioSwitchHandler?.availableAudioDevices ?: emptyList(), |
| 374 | ) | 393 | ) |
| 375 | } | 394 | } |
| 376 | Surface( | 395 | Surface( |
| 377 | onClick = { viewModel.toggleSubscriptionPermissions() }, | 396 | onClick = { viewModel.toggleSubscriptionPermissions() }, |
| 378 | modifier = Modifier | 397 | modifier = Modifier |
| 379 | .size(controlSize) | 398 | .size(controlSize) |
| 380 | - .padding(controlPadding) | 399 | + .padding(controlPadding), |
| 381 | ) { | 400 | ) { |
| 382 | val resource = | 401 | val resource = |
| 383 | if (permissionAllowed) R.drawable.account_cancel_outline else R.drawable.account_cancel | 402 | if (permissionAllowed) R.drawable.account_cancel_outline else R.drawable.account_cancel |
| @@ -393,7 +412,7 @@ class CallActivity : AppCompatActivity() { | @@ -393,7 +412,7 @@ class CallActivity : AppCompatActivity() { | ||
| 393 | onClick = { showDebugDialog = true }, | 412 | onClick = { showDebugDialog = true }, |
| 394 | modifier = Modifier | 413 | modifier = Modifier |
| 395 | .size(controlSize) | 414 | .size(controlSize) |
| 396 | - .padding(controlPadding) | 415 | + .padding(controlPadding), |
| 397 | ) { | 416 | ) { |
| 398 | val resource = R.drawable.dots_horizontal_circle_outline | 417 | val resource = R.drawable.dots_horizontal_circle_outline |
| 399 | Icon( | 418 | Icon( |
| @@ -427,7 +446,7 @@ class CallActivity : AppCompatActivity() { | @@ -427,7 +446,7 @@ class CallActivity : AppCompatActivity() { | ||
| 427 | scope.launch { | 446 | scope.launch { |
| 428 | scaffoldState.snackbarHostState.showSnackbar(error?.toString() ?: "") | 447 | scaffoldState.snackbarHostState.showSnackbar(error?.toString() ?: "") |
| 429 | } | 448 | } |
| 430 | - } | 449 | + }, |
| 431 | ) | 450 | ) |
| 432 | }, | 451 | }, |
| 433 | content = { innerPadding -> | 452 | content = { innerPadding -> |
| @@ -436,9 +455,9 @@ class CallActivity : AppCompatActivity() { | @@ -436,9 +455,9 @@ class CallActivity : AppCompatActivity() { | ||
| 436 | modifier = Modifier | 455 | modifier = Modifier |
| 437 | .padding(innerPadding) | 456 | .padding(innerPadding) |
| 438 | .fillMaxSize() | 457 | .fillMaxSize() |
| 439 | - .wrapContentSize() | 458 | + .wrapContentSize(), |
| 440 | ) | 459 | ) |
| 441 | - } | 460 | + }, |
| 442 | ) | 461 | ) |
| 443 | } | 462 | } |
| 444 | } | 463 | } |
| @@ -454,6 +473,6 @@ class CallActivity : AppCompatActivity() { | @@ -454,6 +473,6 @@ class CallActivity : AppCompatActivity() { | ||
| 454 | val url: String, | 473 | val url: String, |
| 455 | val token: String, | 474 | val token: String, |
| 456 | val e2eeKey: String, | 475 | val e2eeKey: String, |
| 457 | - val e2eeOn: Boolean | 476 | + val e2eeOn: Boolean, |
| 458 | ) : Parcelable | 477 | ) : Parcelable |
| 459 | } | 478 | } |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.composesample | 17 | package io.livekit.android.composesample |
| 2 | 18 | ||
| 3 | import android.content.Intent | 19 | import android.content.Intent |
| @@ -38,6 +54,7 @@ import androidx.compose.ui.unit.dp | @@ -38,6 +54,7 @@ import androidx.compose.ui.unit.dp | ||
| 38 | import com.google.accompanist.pager.ExperimentalPagerApi | 54 | import com.google.accompanist.pager.ExperimentalPagerApi |
| 39 | import io.livekit.android.composesample.ui.theme.AppTheme | 55 | import io.livekit.android.composesample.ui.theme.AppTheme |
| 40 | import io.livekit.android.sample.MainViewModel | 56 | import io.livekit.android.sample.MainViewModel |
| 57 | +import io.livekit.android.sample.common.R | ||
| 41 | import io.livekit.android.sample.util.requestNeededPermissions | 58 | import io.livekit.android.sample.util.requestNeededPermissions |
| 42 | 59 | ||
| 43 | @ExperimentalPagerApi | 60 | @ExperimentalPagerApi |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.composesample | 17 | package io.livekit.android.composesample |
| 2 | 18 | ||
| 3 | import androidx.compose.foundation.background | 19 | import androidx.compose.foundation.background |
| @@ -20,6 +36,7 @@ import io.livekit.android.composesample.ui.theme.NoVideoBackground | @@ -20,6 +36,7 @@ import io.livekit.android.composesample.ui.theme.NoVideoBackground | ||
| 20 | import io.livekit.android.room.Room | 36 | import io.livekit.android.room.Room |
| 21 | import io.livekit.android.room.participant.ConnectionQuality | 37 | import io.livekit.android.room.participant.ConnectionQuality |
| 22 | import io.livekit.android.room.participant.Participant | 38 | import io.livekit.android.room.participant.Participant |
| 39 | +import io.livekit.android.sample.common.R | ||
| 23 | import io.livekit.android.util.flow | 40 | import io.livekit.android.util.flow |
| 24 | 41 | ||
| 25 | /** | 42 | /** |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.composesample | 17 | package io.livekit.android.composesample |
| 2 | 18 | ||
| 3 | import android.app.Application | 19 | import android.app.Application |
| @@ -9,5 +25,6 @@ class SampleApplication : Application() { | @@ -9,5 +25,6 @@ class SampleApplication : Application() { | ||
| 9 | override fun onCreate() { | 25 | override fun onCreate() { |
| 10 | super.onCreate() | 26 | super.onCreate() |
| 11 | LiveKit.loggingLevel = LoggingLevel.VERBOSE | 27 | LiveKit.loggingLevel = LoggingLevel.VERBOSE |
| 28 | + LiveKit.enableWebRTCLogging = true | ||
| 12 | } | 29 | } |
| 13 | } | 30 | } |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.composesample | 17 | package io.livekit.android.composesample |
| 2 | 18 | ||
| 3 | import androidx.compose.foundation.layout.Box | 19 | import androidx.compose.foundation.layout.Box |
| 4 | import androidx.compose.material.Icon | 20 | import androidx.compose.material.Icon |
| 5 | -import androidx.compose.runtime.* | 21 | +import androidx.compose.runtime.Composable |
| 22 | +import androidx.compose.runtime.LaunchedEffect | ||
| 23 | +import androidx.compose.runtime.collectAsState | ||
| 24 | +import androidx.compose.runtime.getValue | ||
| 25 | +import androidx.compose.runtime.mutableStateOf | ||
| 26 | +import androidx.compose.runtime.remember | ||
| 27 | +import androidx.compose.runtime.setValue | ||
| 6 | import androidx.compose.ui.Alignment | 28 | import androidx.compose.ui.Alignment |
| 7 | import androidx.compose.ui.Modifier | 29 | import androidx.compose.ui.Modifier |
| 8 | import androidx.compose.ui.graphics.Color | 30 | import androidx.compose.ui.graphics.Color |
| 9 | import androidx.compose.ui.res.painterResource | 31 | import androidx.compose.ui.res.painterResource |
| 10 | -import io.livekit.android.compose.VideoRenderer | 32 | +import io.livekit.android.composesample.ui.VideoRenderer |
| 11 | import io.livekit.android.room.Room | 33 | import io.livekit.android.room.Room |
| 12 | import io.livekit.android.room.participant.Participant | 34 | import io.livekit.android.room.participant.Participant |
| 13 | import io.livekit.android.room.track.CameraPosition | 35 | import io.livekit.android.room.track.CameraPosition |
| 14 | import io.livekit.android.room.track.LocalVideoTrack | 36 | import io.livekit.android.room.track.LocalVideoTrack |
| 15 | import io.livekit.android.room.track.Track | 37 | import io.livekit.android.room.track.Track |
| 16 | import io.livekit.android.room.track.VideoTrack | 38 | import io.livekit.android.room.track.VideoTrack |
| 39 | +import io.livekit.android.sample.common.R | ||
| 17 | import io.livekit.android.util.flow | 40 | import io.livekit.android.util.flow |
| 18 | 41 | ||
| 19 | /** | 42 | /** |
| @@ -60,7 +83,7 @@ fun VideoItemTrackSelector( | @@ -60,7 +83,7 @@ fun VideoItemTrackSelector( | ||
| 60 | room = room, | 83 | room = room, |
| 61 | videoTrack = videoTrack, | 84 | videoTrack = videoTrack, |
| 62 | mirror = room.localParticipant == participant && cameraFacingFront, | 85 | mirror = room.localParticipant == participant && cameraFacingFront, |
| 63 | - modifier = modifier | 86 | + modifier = modifier, |
| 64 | ) | 87 | ) |
| 65 | } else { | 88 | } else { |
| 66 | Box(modifier = modifier) { | 89 | Box(modifier = modifier) { |
| @@ -68,7 +91,7 @@ fun VideoItemTrackSelector( | @@ -68,7 +91,7 @@ fun VideoItemTrackSelector( | ||
| 68 | painter = painterResource(id = R.drawable.outline_videocam_off_24), | 91 | painter = painterResource(id = R.drawable.outline_videocam_off_24), |
| 69 | contentDescription = null, | 92 | contentDescription = null, |
| 70 | tint = Color.White, | 93 | tint = Color.White, |
| 71 | - modifier = Modifier.align(Alignment.Center) | 94 | + modifier = Modifier.align(Alignment.Center), |
| 72 | ) | 95 | ) |
| 73 | } | 96 | } |
| 74 | } | 97 | } |
sample-app-compose/src/main/java/io/livekit/android/composesample/ui/ComposeVisibility.kt
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package io.livekit.android.composesample.ui | ||
| 18 | + | ||
| 19 | +import androidx.compose.ui.layout.LayoutCoordinates | ||
| 20 | +import io.livekit.android.room.track.Track | ||
| 21 | +import io.livekit.android.room.track.video.VideoSinkVisibility | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | +class ComposeVisibility : VideoSinkVisibility() { | ||
| 27 | + private var coordinates: LayoutCoordinates? = null | ||
| 28 | + | ||
| 29 | + private var lastVisible = isVisible() | ||
| 30 | + private var lastSize = size() | ||
| 31 | + override fun isVisible(): Boolean { | ||
| 32 | + return (coordinates?.isAttached == true && | ||
| 33 | + coordinates?.size?.width != 0 && | ||
| 34 | + coordinates?.size?.height != 0) | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + override fun size(): Track.Dimensions { | ||
| 38 | + val width = coordinates?.size?.width ?: 0 | ||
| 39 | + val height = coordinates?.size?.height ?: 0 | ||
| 40 | + return Track.Dimensions(width, height) | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + // Note, LayoutCoordinates are mutable and may be reused. | ||
| 44 | + fun onGloballyPositioned(layoutCoordinates: LayoutCoordinates) { | ||
| 45 | + coordinates = layoutCoordinates | ||
| 46 | + val visible = isVisible() | ||
| 47 | + val size = size() | ||
| 48 | + | ||
| 49 | + if (lastVisible != visible || lastSize != size) { | ||
| 50 | + notifyChanged() | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + lastVisible = visible | ||
| 54 | + lastSize = size | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + fun onDispose() { | ||
| 58 | + if (coordinates == null) { | ||
| 59 | + return | ||
| 60 | + } | ||
| 61 | + coordinates = null | ||
| 62 | + notifyChanged() | ||
| 63 | + } | ||
| 64 | +} |
| @@ -14,17 +14,32 @@ | @@ -14,17 +14,32 @@ | ||
| 14 | * limitations under the License. | 14 | * limitations under the License. |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | -package io.livekit.android.compose | 17 | +package io.livekit.android.composesample.ui |
| 18 | 18 | ||
| 19 | -import androidx.compose.runtime.* | 19 | +import androidx.compose.foundation.background |
| 20 | +import androidx.compose.foundation.layout.Box | ||
| 21 | +import androidx.compose.runtime.Composable | ||
| 22 | +import androidx.compose.runtime.DisposableEffect | ||
| 23 | +import androidx.compose.runtime.currentCompositeKeyHash | ||
| 24 | +import androidx.compose.runtime.getValue | ||
| 25 | +import androidx.compose.runtime.mutableStateOf | ||
| 26 | +import androidx.compose.runtime.remember | ||
| 27 | +import androidx.compose.runtime.setValue | ||
| 20 | import androidx.compose.ui.Modifier | 28 | import androidx.compose.ui.Modifier |
| 29 | +import androidx.compose.ui.graphics.Color | ||
| 21 | import androidx.compose.ui.layout.onGloballyPositioned | 30 | import androidx.compose.ui.layout.onGloballyPositioned |
| 31 | +import androidx.compose.ui.platform.LocalView | ||
| 22 | import androidx.compose.ui.viewinterop.AndroidView | 32 | import androidx.compose.ui.viewinterop.AndroidView |
| 23 | import io.livekit.android.renderer.TextureViewRenderer | 33 | import io.livekit.android.renderer.TextureViewRenderer |
| 24 | import io.livekit.android.room.Room | 34 | import io.livekit.android.room.Room |
| 25 | import io.livekit.android.room.track.RemoteVideoTrack | 35 | import io.livekit.android.room.track.RemoteVideoTrack |
| 26 | import io.livekit.android.room.track.VideoTrack | 36 | import io.livekit.android.room.track.VideoTrack |
| 27 | -import io.livekit.android.room.track.video.ComposeVisibility | 37 | +import org.webrtc.RendererCommon |
| 38 | + | ||
| 39 | +enum class ScaleType { | ||
| 40 | + FitInside, | ||
| 41 | + Fill, | ||
| 42 | +} | ||
| 28 | 43 | ||
| 29 | /** | 44 | /** |
| 30 | * Widget for displaying a VideoTrack. Handles the Compose <-> AndroidView interop needed to use | 45 | * Widget for displaying a VideoTrack. Handles the Compose <-> AndroidView interop needed to use |
| @@ -33,10 +48,21 @@ import io.livekit.android.room.track.video.ComposeVisibility | @@ -33,10 +48,21 @@ import io.livekit.android.room.track.video.ComposeVisibility | ||
| 33 | @Composable | 48 | @Composable |
| 34 | fun VideoRenderer( | 49 | fun VideoRenderer( |
| 35 | room: Room, | 50 | room: Room, |
| 36 | - videoTrack: VideoTrack, | 51 | + videoTrack: VideoTrack?, |
| 37 | modifier: Modifier = Modifier, | 52 | modifier: Modifier = Modifier, |
| 38 | mirror: Boolean = false, | 53 | mirror: Boolean = false, |
| 54 | + scaleType: ScaleType = ScaleType.Fill, | ||
| 39 | ) { | 55 | ) { |
| 56 | + // Show a black box for preview. | ||
| 57 | + if (LocalView.current.isInEditMode) { | ||
| 58 | + Box( | ||
| 59 | + modifier = Modifier | ||
| 60 | + .background(Color.Black) | ||
| 61 | + .then(modifier) | ||
| 62 | + ) | ||
| 63 | + return | ||
| 64 | + } | ||
| 65 | + | ||
| 40 | val videoSinkVisibility = remember(room, videoTrack) { ComposeVisibility() } | 66 | val videoSinkVisibility = remember(room, videoTrack) { ComposeVisibility() } |
| 41 | var boundVideoTrack by remember { mutableStateOf<VideoTrack?>(null) } | 67 | var boundVideoTrack by remember { mutableStateOf<VideoTrack?>(null) } |
| 42 | var view: TextureViewRenderer? by remember { mutableStateOf(null) } | 68 | var view: TextureViewRenderer? by remember { mutableStateOf(null) } |
| @@ -46,7 +72,7 @@ fun VideoRenderer( | @@ -46,7 +72,7 @@ fun VideoRenderer( | ||
| 46 | boundVideoTrack = null | 72 | boundVideoTrack = null |
| 47 | } | 73 | } |
| 48 | 74 | ||
| 49 | - fun setupVideoIfNeeded(videoTrack: VideoTrack, view: TextureViewRenderer) { | 75 | + fun setupVideoIfNeeded(videoTrack: VideoTrack?, view: TextureViewRenderer) { |
| 50 | if (boundVideoTrack == videoTrack) { | 76 | if (boundVideoTrack == videoTrack) { |
| 51 | return | 77 | return |
| 52 | } | 78 | } |
| @@ -54,10 +80,12 @@ fun VideoRenderer( | @@ -54,10 +80,12 @@ fun VideoRenderer( | ||
| 54 | cleanupVideoTrack() | 80 | cleanupVideoTrack() |
| 55 | 81 | ||
| 56 | boundVideoTrack = videoTrack | 82 | boundVideoTrack = videoTrack |
| 57 | - if (videoTrack is RemoteVideoTrack) { | ||
| 58 | - videoTrack.addRenderer(view, videoSinkVisibility) | ||
| 59 | - } else { | ||
| 60 | - videoTrack.addRenderer(view) | 83 | + if (videoTrack != null) { |
| 84 | + if (videoTrack is RemoteVideoTrack) { | ||
| 85 | + videoTrack.addRenderer(view, videoSinkVisibility) | ||
| 86 | + } else { | ||
| 87 | + videoTrack.addRenderer(view) | ||
| 88 | + } | ||
| 61 | } | 89 | } |
| 62 | } | 90 | } |
| 63 | 91 | ||
| @@ -85,6 +113,15 @@ fun VideoRenderer( | @@ -85,6 +113,15 @@ fun VideoRenderer( | ||
| 85 | room.initVideoRenderer(this) | 113 | room.initVideoRenderer(this) |
| 86 | setupVideoIfNeeded(videoTrack, this) | 114 | setupVideoIfNeeded(videoTrack, this) |
| 87 | 115 | ||
| 116 | + when (scaleType) { | ||
| 117 | + ScaleType.FitInside -> { | ||
| 118 | + this.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + ScaleType.Fill -> { | ||
| 122 | + this.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL) | ||
| 123 | + } | ||
| 124 | + } | ||
| 88 | view = this | 125 | view = this |
| 89 | } | 126 | } |
| 90 | }, | 127 | }, |
| @@ -4,12 +4,14 @@ plugins { | @@ -4,12 +4,14 @@ plugins { | ||
| 4 | } | 4 | } |
| 5 | 5 | ||
| 6 | android { | 6 | android { |
| 7 | - compileSdk 32 | 7 | + namespace 'io.livekit.android.sample.record' |
| 8 | + compileSdk androidSdk.compileVersion | ||
| 9 | + | ||
| 8 | 10 | ||
| 9 | defaultConfig { | 11 | defaultConfig { |
| 10 | applicationId "io.livekit.android.sample.record" | 12 | applicationId "io.livekit.android.sample.record" |
| 11 | - minSdk 21 | ||
| 12 | - targetSdk 32 | 13 | + minSdk androidSdk.minVersion |
| 14 | + targetSdk androidSdk.targetVersion | ||
| 13 | versionCode 1 | 15 | versionCode 1 |
| 14 | versionName "1.0" | 16 | versionName "1.0" |
| 15 | 17 | ||
| @@ -26,11 +28,11 @@ android { | @@ -26,11 +28,11 @@ android { | ||
| 26 | } | 28 | } |
| 27 | } | 29 | } |
| 28 | compileOptions { | 30 | compileOptions { |
| 29 | - sourceCompatibility JavaVersion.VERSION_1_8 | ||
| 30 | - targetCompatibility JavaVersion.VERSION_1_8 | 31 | + sourceCompatibility java_version |
| 32 | + targetCompatibility java_version | ||
| 31 | } | 33 | } |
| 32 | kotlinOptions { | 34 | kotlinOptions { |
| 33 | - jvmTarget = '1.8' | 35 | + jvmTarget = java_version |
| 34 | } | 36 | } |
| 35 | buildFeatures { | 37 | buildFeatures { |
| 36 | compose true | 38 | compose true |
| @@ -5,8 +5,9 @@ plugins { | @@ -5,8 +5,9 @@ plugins { | ||
| 5 | } | 5 | } |
| 6 | 6 | ||
| 7 | android { | 7 | android { |
| 8 | + namespace "io.livekit.android.sample" | ||
| 8 | compileSdkVersion androidSdk.compileVersion | 9 | compileSdkVersion androidSdk.compileVersion |
| 9 | - buildToolsVersion "30.0.3" | 10 | + |
| 10 | defaultConfig { | 11 | defaultConfig { |
| 11 | applicationId "io.livekit.android" | 12 | applicationId "io.livekit.android" |
| 12 | minSdkVersion androidSdk.minVersion | 13 | minSdkVersion androidSdk.minVersion |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.sample | 17 | package io.livekit.android.sample |
| 2 | 18 | ||
| 3 | import android.app.Activity | 19 | import android.app.Activity |
| @@ -13,6 +29,7 @@ import androidx.appcompat.app.AppCompatActivity | @@ -13,6 +29,7 @@ import androidx.appcompat.app.AppCompatActivity | ||
| 13 | import androidx.lifecycle.lifecycleScope | 29 | import androidx.lifecycle.lifecycleScope |
| 14 | import androidx.recyclerview.widget.LinearLayoutManager | 30 | import androidx.recyclerview.widget.LinearLayoutManager |
| 15 | import com.xwray.groupie.GroupieAdapter | 31 | import com.xwray.groupie.GroupieAdapter |
| 32 | +import io.livekit.android.sample.common.R | ||
| 16 | import io.livekit.android.sample.databinding.CallActivityBinding | 33 | import io.livekit.android.sample.databinding.CallActivityBinding |
| 17 | import io.livekit.android.sample.dialog.showDebugMenuDialog | 34 | import io.livekit.android.sample.dialog.showDebugMenuDialog |
| 18 | import io.livekit.android.sample.dialog.showSelectAudioDeviceDialog | 35 | import io.livekit.android.sample.dialog.showSelectAudioDeviceDialog |
| @@ -29,7 +46,7 @@ class CallActivity : AppCompatActivity() { | @@ -29,7 +46,7 @@ class CallActivity : AppCompatActivity() { | ||
| 29 | lateinit var binding: CallActivityBinding | 46 | lateinit var binding: CallActivityBinding |
| 30 | private val screenCaptureIntentLauncher = | 47 | private val screenCaptureIntentLauncher = |
| 31 | registerForActivityResult( | 48 | registerForActivityResult( |
| 32 | - ActivityResultContracts.StartActivityForResult() | 49 | + ActivityResultContracts.StartActivityForResult(), |
| 33 | ) { result -> | 50 | ) { result -> |
| 34 | val resultCode = result.resultCode | 51 | val resultCode = result.resultCode |
| 35 | val data = result.data | 52 | val data = result.data |
| @@ -83,7 +100,7 @@ class CallActivity : AppCompatActivity() { | @@ -83,7 +100,7 @@ class CallActivity : AppCompatActivity() { | ||
| 83 | R.drawable.outline_videocam_24 | 100 | R.drawable.outline_videocam_24 |
| 84 | } else { | 101 | } else { |
| 85 | R.drawable.outline_videocam_off_24 | 102 | R.drawable.outline_videocam_off_24 |
| 86 | - } | 103 | + }, |
| 87 | ) | 104 | ) |
| 88 | binding.flipCamera.isEnabled = enabled | 105 | binding.flipCamera.isEnabled = enabled |
| 89 | } | 106 | } |
| @@ -94,7 +111,7 @@ class CallActivity : AppCompatActivity() { | @@ -94,7 +111,7 @@ class CallActivity : AppCompatActivity() { | ||
| 94 | R.drawable.outline_mic_24 | 111 | R.drawable.outline_mic_24 |
| 95 | } else { | 112 | } else { |
| 96 | R.drawable.outline_mic_off_24 | 113 | R.drawable.outline_mic_off_24 |
| 97 | - } | 114 | + }, |
| 98 | ) | 115 | ) |
| 99 | } | 116 | } |
| 100 | 117 | ||
| @@ -112,7 +129,7 @@ class CallActivity : AppCompatActivity() { | @@ -112,7 +129,7 @@ class CallActivity : AppCompatActivity() { | ||
| 112 | R.drawable.baseline_cast_connected_24 | 129 | R.drawable.baseline_cast_connected_24 |
| 113 | } else { | 130 | } else { |
| 114 | R.drawable.baseline_cast_24 | 131 | R.drawable.baseline_cast_24 |
| 115 | - } | 132 | + }, |
| 116 | ) | 133 | ) |
| 117 | } | 134 | } |
| 118 | 135 |
| @@ -4,7 +4,7 @@ pluginManagement { | @@ -4,7 +4,7 @@ pluginManagement { | ||
| 4 | } | 4 | } |
| 5 | } | 5 | } |
| 6 | include ':sample-app', ':sample-app-compose', ':livekit-android-sdk' | 6 | include ':sample-app', ':sample-app-compose', ':livekit-android-sdk' |
| 7 | -rootProject.name='livekit-android' | 7 | +rootProject.name = 'livekit-android' |
| 8 | include ':sample-app-common' | 8 | include ':sample-app-common' |
| 9 | include ':livekit-lint' | 9 | include ':livekit-lint' |
| 10 | include ':video-encode-decode-test' | 10 | include ':video-encode-decode-test' |
| @@ -21,10 +21,11 @@ final apiKey = getApiKey() | @@ -21,10 +21,11 @@ final apiKey = getApiKey() | ||
| 21 | final apiSecret = getApiSecret() | 21 | final apiSecret = getApiSecret() |
| 22 | 22 | ||
| 23 | android { | 23 | android { |
| 24 | + namespace "io.livekit.android.videoencodedecode" | ||
| 24 | compileSdkVersion androidSdk.compileVersion | 25 | compileSdkVersion androidSdk.compileVersion |
| 25 | 26 | ||
| 26 | defaultConfig { | 27 | defaultConfig { |
| 27 | - applicationId "io.livekit.android.videoencodedecodetest" | 28 | + applicationId "io.livekit.android.videoencodedecode" |
| 28 | minSdkVersion androidSdk.minVersion | 29 | minSdkVersion androidSdk.minVersion |
| 29 | targetSdkVersion androidSdk.targetVersion | 30 | targetSdkVersion androidSdk.targetVersion |
| 30 | versionCode 1 | 31 | versionCode 1 |
| @@ -60,7 +61,8 @@ android { | @@ -60,7 +61,8 @@ android { | ||
| 60 | freeCompilerArgs += '-opt-in=kotlin.RequiresOptIn' | 61 | freeCompilerArgs += '-opt-in=kotlin.RequiresOptIn' |
| 61 | } | 62 | } |
| 62 | buildFeatures { | 63 | buildFeatures { |
| 63 | - compose true | 64 | + buildConfig = true |
| 65 | + compose = true | ||
| 64 | } | 66 | } |
| 65 | composeOptions { | 67 | composeOptions { |
| 66 | kotlinCompilerExtensionVersion compose_compiler_version | 68 | kotlinCompilerExtensionVersion compose_compiler_version |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.videoencodedecode | 17 | package io.livekit.android.videoencodedecode |
| 2 | 18 | ||
| 3 | import android.Manifest | 19 | import android.Manifest |
| @@ -21,6 +37,7 @@ import androidx.compose.ui.unit.dp | @@ -21,6 +37,7 @@ import androidx.compose.ui.unit.dp | ||
| 21 | import androidx.core.content.ContextCompat | 37 | import androidx.core.content.ContextCompat |
| 22 | import com.google.accompanist.pager.ExperimentalPagerApi | 38 | import com.google.accompanist.pager.ExperimentalPagerApi |
| 23 | import io.livekit.android.composesample.ui.theme.AppTheme | 39 | import io.livekit.android.composesample.ui.theme.AppTheme |
| 40 | +import io.livekit.android.sample.common.R | ||
| 24 | 41 | ||
| 25 | @ExperimentalPagerApi | 42 | @ExperimentalPagerApi |
| 26 | class MainActivity : ComponentActivity() { | 43 | class MainActivity : ComponentActivity() { |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.videoencodedecode | 17 | package io.livekit.android.videoencodedecode |
| 2 | 18 | ||
| 3 | import androidx.compose.foundation.background | 19 | import androidx.compose.foundation.background |
| @@ -20,6 +36,7 @@ import io.livekit.android.composesample.ui.theme.NoVideoBackground | @@ -20,6 +36,7 @@ import io.livekit.android.composesample.ui.theme.NoVideoBackground | ||
| 20 | import io.livekit.android.room.Room | 36 | import io.livekit.android.room.Room |
| 21 | import io.livekit.android.room.participant.ConnectionQuality | 37 | import io.livekit.android.room.participant.ConnectionQuality |
| 22 | import io.livekit.android.room.participant.Participant | 38 | import io.livekit.android.room.participant.Participant |
| 39 | +import io.livekit.android.sample.common.R | ||
| 23 | import io.livekit.android.util.flow | 40 | import io.livekit.android.util.flow |
| 24 | 41 | ||
| 25 | /** | 42 | /** |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.videoencodedecode | 17 | package io.livekit.android.videoencodedecode |
| 2 | 18 | ||
| 3 | import androidx.compose.foundation.layout.Box | 19 | import androidx.compose.foundation.layout.Box |
| @@ -21,8 +37,8 @@ import io.livekit.android.room.participant.Participant | @@ -21,8 +37,8 @@ import io.livekit.android.room.participant.Participant | ||
| 21 | import io.livekit.android.room.track.RemoteVideoTrack | 37 | import io.livekit.android.room.track.RemoteVideoTrack |
| 22 | import io.livekit.android.room.track.Track | 38 | import io.livekit.android.room.track.Track |
| 23 | import io.livekit.android.room.track.VideoTrack | 39 | import io.livekit.android.room.track.VideoTrack |
| 24 | -import io.livekit.android.room.track.video.ComposeVisibility | ||
| 25 | import io.livekit.android.util.flow | 40 | import io.livekit.android.util.flow |
| 41 | +import io.livekit.android.videoencodedecode.ui.ComposeVisibility | ||
| 26 | import kotlinx.coroutines.flow.* | 42 | import kotlinx.coroutines.flow.* |
| 27 | 43 | ||
| 28 | /** | 44 | /** |
| @@ -137,7 +153,7 @@ fun VideoItemTrackSelector( | @@ -137,7 +153,7 @@ fun VideoItemTrackSelector( | ||
| 137 | } else { | 153 | } else { |
| 138 | Box(modifier = modifier) { | 154 | Box(modifier = modifier) { |
| 139 | Icon( | 155 | Icon( |
| 140 | - painter = painterResource(id = R.drawable.outline_videocam_off_24), | 156 | + painter = painterResource(id = io.livekit.android.sample.common.R.drawable.outline_videocam_off_24), |
| 141 | contentDescription = null, | 157 | contentDescription = null, |
| 142 | tint = Color.White, | 158 | tint = Color.White, |
| 143 | modifier = Modifier.align(Alignment.Center) | 159 | modifier = Modifier.align(Alignment.Center) |
video-encode-decode-test/src/main/java/io/livekit/android/videoencodedecode/ui/ComposeVisibility.kt
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package io.livekit.android.videoencodedecode.ui | ||
| 18 | + | ||
| 19 | +import androidx.compose.ui.layout.LayoutCoordinates | ||
| 20 | +import io.livekit.android.room.track.Track | ||
| 21 | +import io.livekit.android.room.track.video.VideoSinkVisibility | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | +class ComposeVisibility : VideoSinkVisibility() { | ||
| 27 | + private var coordinates: LayoutCoordinates? = null | ||
| 28 | + | ||
| 29 | + private var lastVisible = isVisible() | ||
| 30 | + private var lastSize = size() | ||
| 31 | + override fun isVisible(): Boolean { | ||
| 32 | + return (coordinates?.isAttached == true && | ||
| 33 | + coordinates?.size?.width != 0 && | ||
| 34 | + coordinates?.size?.height != 0) | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + override fun size(): Track.Dimensions { | ||
| 38 | + val width = coordinates?.size?.width ?: 0 | ||
| 39 | + val height = coordinates?.size?.height ?: 0 | ||
| 40 | + return Track.Dimensions(width, height) | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + // Note, LayoutCoordinates are mutable and may be reused. | ||
| 44 | + fun onGloballyPositioned(layoutCoordinates: LayoutCoordinates) { | ||
| 45 | + coordinates = layoutCoordinates | ||
| 46 | + val visible = isVisible() | ||
| 47 | + val size = size() | ||
| 48 | + | ||
| 49 | + if (lastVisible != visible || lastSize != size) { | ||
| 50 | + notifyChanged() | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + lastVisible = visible | ||
| 54 | + lastSize = size | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + fun onDispose() { | ||
| 58 | + if (coordinates == null) { | ||
| 59 | + return | ||
| 60 | + } | ||
| 61 | + coordinates = null | ||
| 62 | + notifyChanged() | ||
| 63 | + } | ||
| 64 | +} |
video-encode-decode-test/src/main/java/io/livekit/android/videoencodedecode/ui/VideoRenderer.kt
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package io.livekit.android.videoencodedecode.ui | ||
| 18 | + | ||
| 19 | +import androidx.compose.foundation.background | ||
| 20 | +import androidx.compose.foundation.layout.Box | ||
| 21 | +import androidx.compose.runtime.Composable | ||
| 22 | +import androidx.compose.runtime.DisposableEffect | ||
| 23 | +import androidx.compose.runtime.currentCompositeKeyHash | ||
| 24 | +import androidx.compose.runtime.getValue | ||
| 25 | +import androidx.compose.runtime.mutableStateOf | ||
| 26 | +import androidx.compose.runtime.remember | ||
| 27 | +import androidx.compose.runtime.setValue | ||
| 28 | +import androidx.compose.ui.Modifier | ||
| 29 | +import androidx.compose.ui.graphics.Color | ||
| 30 | +import androidx.compose.ui.layout.onGloballyPositioned | ||
| 31 | +import androidx.compose.ui.platform.LocalView | ||
| 32 | +import androidx.compose.ui.viewinterop.AndroidView | ||
| 33 | +import io.livekit.android.renderer.TextureViewRenderer | ||
| 34 | +import io.livekit.android.room.Room | ||
| 35 | +import io.livekit.android.room.track.RemoteVideoTrack | ||
| 36 | +import io.livekit.android.room.track.VideoTrack | ||
| 37 | +import org.webrtc.RendererCommon | ||
| 38 | + | ||
| 39 | +enum class ScaleType { | ||
| 40 | + FitInside, | ||
| 41 | + Fill, | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +/** | ||
| 45 | + * Widget for displaying a VideoTrack. Handles the Compose <-> AndroidView interop needed to use | ||
| 46 | + * [TextureViewRenderer]. | ||
| 47 | + */ | ||
| 48 | +@Composable | ||
| 49 | +fun VideoRenderer( | ||
| 50 | + room: Room, | ||
| 51 | + videoTrack: VideoTrack?, | ||
| 52 | + modifier: Modifier = Modifier, | ||
| 53 | + mirror: Boolean = false, | ||
| 54 | + scaleType: ScaleType = ScaleType.Fill, | ||
| 55 | +) { | ||
| 56 | + // Show a black box for preview. | ||
| 57 | + if (LocalView.current.isInEditMode) { | ||
| 58 | + Box( | ||
| 59 | + modifier = Modifier | ||
| 60 | + .background(Color.Black) | ||
| 61 | + .then(modifier) | ||
| 62 | + ) | ||
| 63 | + return | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + val videoSinkVisibility = remember(room, videoTrack) { ComposeVisibility() } | ||
| 67 | + var boundVideoTrack by remember { mutableStateOf<VideoTrack?>(null) } | ||
| 68 | + var view: TextureViewRenderer? by remember { mutableStateOf(null) } | ||
| 69 | + | ||
| 70 | + fun cleanupVideoTrack() { | ||
| 71 | + view?.let { boundVideoTrack?.removeRenderer(it) } | ||
| 72 | + boundVideoTrack = null | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + fun setupVideoIfNeeded(videoTrack: VideoTrack?, view: TextureViewRenderer) { | ||
| 76 | + if (boundVideoTrack == videoTrack) { | ||
| 77 | + return | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + cleanupVideoTrack() | ||
| 81 | + | ||
| 82 | + boundVideoTrack = videoTrack | ||
| 83 | + if (videoTrack != null) { | ||
| 84 | + if (videoTrack is RemoteVideoTrack) { | ||
| 85 | + videoTrack.addRenderer(view, videoSinkVisibility) | ||
| 86 | + } else { | ||
| 87 | + videoTrack.addRenderer(view) | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + DisposableEffect(view, mirror) { | ||
| 93 | + view?.setMirror(mirror) | ||
| 94 | + onDispose { } | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + DisposableEffect(room, videoTrack) { | ||
| 98 | + onDispose { | ||
| 99 | + videoSinkVisibility.onDispose() | ||
| 100 | + cleanupVideoTrack() | ||
| 101 | + } | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + DisposableEffect(currentCompositeKeyHash.toString()) { | ||
| 105 | + onDispose { | ||
| 106 | + view?.release() | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + AndroidView( | ||
| 111 | + factory = { context -> | ||
| 112 | + TextureViewRenderer(context).apply { | ||
| 113 | + room.initVideoRenderer(this) | ||
| 114 | + setupVideoIfNeeded(videoTrack, this) | ||
| 115 | + | ||
| 116 | + when (scaleType) { | ||
| 117 | + ScaleType.FitInside -> { | ||
| 118 | + this.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + ScaleType.Fill -> { | ||
| 122 | + this.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL) | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + view = this | ||
| 126 | + } | ||
| 127 | + }, | ||
| 128 | + update = { v -> setupVideoIfNeeded(videoTrack, v) }, | ||
| 129 | + modifier = modifier | ||
| 130 | + .onGloballyPositioned { videoSinkVisibility.onGloballyPositioned(it) }, | ||
| 131 | + ) | ||
| 132 | +} |
-
请 注册 或 登录 后发表评论