diff --git a/Advanced-Audio-Android/.gitignore b/Advanced-Audio-Android/.gitignore new file mode 100644 index 0000000..603b140 --- /dev/null +++ b/Advanced-Audio-Android/.gitignore @@ -0,0 +1,14 @@ +*.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 diff --git a/Advanced-Audio-Android/.idea/codeStyles/Project.xml b/Advanced-Audio-Android/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..681f41a --- /dev/null +++ b/Advanced-Audio-Android/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + 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/Advanced-Audio-Android/.idea/gradle.xml b/Advanced-Audio-Android/.idea/gradle.xml new file mode 100644 index 0000000..d291b3d --- /dev/null +++ b/Advanced-Audio-Android/.idea/gradle.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/Advanced-Audio-Android/.idea/misc.xml b/Advanced-Audio-Android/.idea/misc.xml new file mode 100644 index 0000000..be0cc41 --- /dev/null +++ b/Advanced-Audio-Android/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/Advanced-Audio-Android/.idea/runConfigurations.xml b/Advanced-Audio-Android/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/Advanced-Audio-Android/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/Advanced-Audio-Android/.idea/vcs.xml b/Advanced-Audio-Android/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/Advanced-Audio-Android/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Advanced-Audio-Android/build-template/build-android.yml b/Advanced-Audio-Android/build-template/build-android.yml new file mode 100644 index 0000000..e92da54 --- /dev/null +++ b/Advanced-Audio-Android/build-template/build-android.yml @@ -0,0 +1,34 @@ +parameters: + project: '' + module: '' + name: '' + +jobs: + +- job: ${{ parameters.name }}_Build + displayName: ${{ parameters.mame }} + pool: + vmImage: 'macos-latest' + steps: + - script: cd ${{ parameters.project }} && ls && python ci.env.py + env: + AGORA_APP_ID: $(agora.appId) + + - task: Gradle@2 + inputs: + workingDirectory: ${{ parameters.project }}/${{ parameters.module }} + gradleWrapperFile: ${{ parameters.project }}/gradlew + gradleOptions: '-Xmx3072m' + publishJUnitResults: false + testResultsFiles: '**/TEST-*.xml' + tasks: 'assembleDebug' + + - task: CopyFiles@2 + inputs: + Contents: '**/*.apk' + TargetFolder: '$(Build.ArtifactStagingDirectory)' + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: ${{ parameters.name }} \ No newline at end of file diff --git a/Advanced-Audio-Android/build.gradle b/Advanced-Audio-Android/build.gradle new file mode 100644 index 0000000..a5bb815 --- /dev/null +++ b/Advanced-Audio-Android/build.gradle @@ -0,0 +1,27 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + jcenter() + + } + dependencies { + classpath 'com.android.tools.build:gradle:3.5.3' + + // 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 +} diff --git a/Advanced-Audio-Android/ci.env.py b/Advanced-Audio-Android/ci.env.py new file mode 100644 index 0000000..8c1a2dd --- /dev/null +++ b/Advanced-Audio-Android/ci.env.py @@ -0,0 +1,22 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import re +import os + +def main(): + appId = "" + if "AGORA_APP_ID" in os.environ: + appId = os.environ["AGORA_APP_ID"] + token = "" + + f = open("./lib-component/src/main/res/values/strings_config.xml", 'r+') + content = f.read() + contentNew = re.sub(r'<#YOUR APP ID#>', appId, content) + contentNew = re.sub(r'<#YOUR ACCESS TOKEN#>', token, contentNew) + f.seek(0) + f.write(contentNew) + f.truncate() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Advanced-Audio-Android/gradle.properties b/Advanced-Audio-Android/gradle.properties new file mode 100644 index 0000000..199d16e --- /dev/null +++ b/Advanced-Audio-Android/gradle.properties @@ -0,0 +1,20 @@ +# 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=-Xmx1536m +# 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 + diff --git a/Advanced-Audio-Android/gradle/wrapper/gradle-wrapper.jar b/Advanced-Audio-Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/Advanced-Audio-Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Advanced-Audio-Android/gradle/wrapper/gradle-wrapper.properties b/Advanced-Audio-Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9ffcaba --- /dev/null +++ b/Advanced-Audio-Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Dec 13 15:32:56 CST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/Advanced-Audio-Android/gradlew b/Advanced-Audio-Android/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/Advanced-Audio-Android/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/Advanced-Audio-Android/gradlew.bat b/Advanced-Audio-Android/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/Advanced-Audio-Android/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/Advanced-Audio-Android/lib-component/build.gradle b/Advanced-Audio-Android/lib-component/build.gradle new file mode 100644 index 0000000..18018f0 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + defaultConfig { + minSdkVersion 16 + 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' + api 'io.agora.rtc:voice-sdk:2.9.2' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/Advanced-Audio-Android/lib-component/proguard-rules.pro b/Advanced-Audio-Android/lib-component/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/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 diff --git a/Advanced-Audio-Android/lib-component/src/androidTest/java/io/agora/advancedaudio/ExampleInstrumentedTest.java b/Advanced-Audio-Android/lib-component/src/androidTest/java/io/agora/advancedaudio/ExampleInstrumentedTest.java new file mode 100644 index 0000000..04022a4 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/androidTest/java/io/agora/advancedaudio/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package io.agora.advancedaudio; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("io.agora.advancedaudio", appContext.getPackageName()); + } +} diff --git a/Advanced-Audio-Android/lib-component/src/main/AndroidManifest.xml b/Advanced-Audio-Android/lib-component/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1b5d0b4 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/AgoraApplication.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/AgoraApplication.java new file mode 100644 index 0000000..d77bb74 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/AgoraApplication.java @@ -0,0 +1,38 @@ +package io.agora.advancedaudio; + +import android.app.Application; + +import io.agora.advancedaudio.component.R; +import io.agora.advancedaudio.component.rtc.AgoraEventHandler; +import io.agora.advancedaudio.component.rtc.EventHandler; +import io.agora.advancedaudio.component.utils.FileUtil; +import io.agora.rtc.RtcEngine; + +public class AgoraApplication extends Application { + private RtcEngine mRtcEngine; + private AgoraEventHandler mHandler = new AgoraEventHandler(); + + @Override + public void onCreate() { + super.onCreate(); + try { + mRtcEngine = RtcEngine.create(getApplicationContext(), getString(R.string.agora_app_id), mHandler); + mRtcEngine.setChannelProfile(io.agora.rtc.Constants.CHANNEL_PROFILE_LIVE_BROADCASTING); + mRtcEngine.setLogFile(FileUtil.initializeLogFile(this)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public RtcEngine rtcEngine() { return mRtcEngine; } + + public void registerEventHandler(EventHandler handler) { mHandler.addHandler(handler); } + + public void removeEventHandler(EventHandler handler) { mHandler.removeHandler(handler); } + + @Override + public void onTerminate() { + super.onTerminate(); + RtcEngine.destroy(); + } +} diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/Constants.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/Constants.java new file mode 100644 index 0000000..5a3f95b --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/Constants.java @@ -0,0 +1,5 @@ +package io.agora.advancedaudio; + +public class Constants { + public static final String KEY_CHANNEL_NAME = "channel-name"; +} diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/activities/BaseActivity.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/activities/BaseActivity.java new file mode 100644 index 0000000..37c0e16 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/activities/BaseActivity.java @@ -0,0 +1,127 @@ +package io.agora.advancedaudio.component.activities; + +import android.os.Bundle; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.Window; + +import androidx.appcompat.app.AppCompatActivity; + +import io.agora.advancedaudio.AgoraApplication; +import io.agora.advancedaudio.component.annotations.DisplayActivity; +import io.agora.advancedaudio.component.rtc.EventHandler; +import io.agora.advancedaudio.component.utils.WindowUtil; +import io.agora.rtc.IRtcEngineEventHandler; +import io.agora.rtc.RtcEngine; + +@DisplayActivity( +SubClasses = { + "io.agora.advancedaudio.customrecorder.CustomRecorderActivity" +}) +public abstract class BaseActivity extends AppCompatActivity implements EventHandler { + protected DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + protected int mStatusBarHeight; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + WindowUtil.hideWindowStatusBar(getWindow()); + setGlobalLayoutListener(); + getDisplayMetrics(); + initStatusBarHeight(); + } + + private void setGlobalLayoutListener() { + final View layout = findViewById(Window.ID_ANDROID_CONTENT); + ViewTreeObserver observer = layout.getViewTreeObserver(); + observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + layout.getViewTreeObserver().removeOnGlobalLayoutListener(this); + onGlobalLayoutCompleted(); + } + }); + } + + /** + * Give a chance to obtain view layout attributes when the + * content view layout process is completed. + * Some layout attributes will be available here but not + * in onCreate(), like measured width/height. + * This callback will be called ONLY ONCE before the whole + * window content is ready to be displayed for first time. + */ + protected void onGlobalLayoutCompleted() { + + } + + private void getDisplayMetrics() { + getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics); + } + + private void initStatusBarHeight() { + mStatusBarHeight = WindowUtil.getSystemStatusBarHeight(this); + } + + protected AgoraApplication application() { + return (AgoraApplication) getApplication(); + } + + protected RtcEngine rtcEngine() { + return application().rtcEngine(); + } + + protected void registerRtcEventHandler(EventHandler handler) { + application().registerEventHandler(handler); + } + + protected void removeRtcEventHandler(EventHandler handler) { + application().removeEventHandler(handler); + } + + @Override + public void onLeaveChannel(IRtcEngineEventHandler.RtcStats stats) { + + } + + @Override + public void onJoinChannelSuccess(String channel, int uid, int elapsed) { + + } + + @Override + public void onUserOffline(int uid, int reason) { + + } + + @Override + public void onUserJoined(int uid, int elapsed) { + + } + + @Override + public void onLastmileQuality(int quality) { + + } + + @Override + public void onLastmileProbeResult(IRtcEngineEventHandler.LastmileProbeResult result) { + + } + + @Override + public void onRtcStats(IRtcEngineEventHandler.RtcStats stats) { + + } + + @Override + public void onNetworkQuality(int uid, int txQuality, int rxQuality) { + + } + + @Override + public void onRemoteAudioStats(IRtcEngineEventHandler.RemoteAudioStats stats) { + + } +} diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/activities/MainActivity.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/activities/MainActivity.java new file mode 100644 index 0000000..361dd3b --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/activities/MainActivity.java @@ -0,0 +1,302 @@ +package io.agora.advancedaudio.component.activities; + +import android.Manifest; +import android.animation.Animator; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Rect; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import java.lang.annotation.Annotation; + +import io.agora.advancedaudio.Constants; +import io.agora.advancedaudio.component.R; +import io.agora.advancedaudio.component.annotations.DisplayActivity; + +public class MainActivity extends BaseActivity { + private static final String TAG = MainActivity.class.getSimpleName(); + private static final int MIN_INPUT_METHOD_HEIGHT = 200; + private static final int ANIM_DURATION = 200; + + // Permission request code of any integer value + private static final int PERMISSION_REQ_CODE = 1 << 4; + + private String[] PERMISSIONS = { + Manifest.permission.RECORD_AUDIO, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + + private Rect mVisibleRect = new Rect(); + private int mLastVisibleHeight = 0; + private RelativeLayout mBodyLayout; + private int mBodyDefaultMarginTop; + private EditText mTopicEdit; + private TextView mStartBtn; + private ImageView mLogo; + + private Animator.AnimatorListener mLogoAnimListener = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + // Do nothing + } + + @Override + public void onAnimationEnd(Animator animator) { + mLogo.setVisibility(View.VISIBLE); + } + + @Override + public void onAnimationCancel(Animator animator) { + mLogo.setVisibility(View.VISIBLE); + } + + @Override + public void onAnimationRepeat(Animator animator) { + // Do nothing + } + }; + + private TextWatcher mTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + // Do nothing + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + // Do nothing + } + + @Override + public void afterTextChanged(Editable editable) { + mStartBtn.setEnabled(!TextUtils.isEmpty(editable)); + } + }; + + private ViewTreeObserver.OnGlobalLayoutListener mLayoutObserverListener = + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + checkInputMethodWindowState(); + } + }; + + private void checkInputMethodWindowState() { + getWindow().getDecorView().getRootView().getWindowVisibleDisplayFrame(mVisibleRect); + int visibleHeight = mVisibleRect.bottom - mVisibleRect.top; + if (visibleHeight == mLastVisibleHeight) return; + + boolean inputShown = mDisplayMetrics.heightPixels - visibleHeight > MIN_INPUT_METHOD_HEIGHT; + mLastVisibleHeight = visibleHeight; + + // Log.i(TAG, "onGlobalLayout:" + inputShown + + // "|" + getWindow().getDecorView().getRootView().getViewTreeObserver()); + + // There is no official way to determine whether the + // input method dialog has already shown. + // This is a workaround, and if the visible content + // height is significantly less than the screen height, + // we should know that the input method dialog takes + // up some screen space. + if (inputShown) { + if (mLogo.getVisibility() == View.VISIBLE) { + mBodyLayout.animate().translationYBy(-mLogo.getMeasuredHeight()) + .setDuration(ANIM_DURATION).setListener(null).start(); + mLogo.setVisibility(View.INVISIBLE); + } + } else if (mLogo.getVisibility() != View.VISIBLE) { + mBodyLayout.animate().translationYBy(mLogo.getMeasuredHeight()) + .setDuration(ANIM_DURATION).setListener(mLogoAnimListener).start(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + initUI(); + } + + private void initUI() { + mBodyLayout = findViewById(R.id.middle_layout); + mLogo = findViewById(R.id.main_logo); + + mTopicEdit = findViewById(R.id.topic_edit); + mTopicEdit.addTextChangedListener(mTextWatcher); + + mStartBtn = findViewById(R.id.start_broadcast_button); + if (TextUtils.isEmpty(mTopicEdit.getText())) mStartBtn.setEnabled(false); + } + + @Override + protected void onGlobalLayoutCompleted() { + adjustViewPositions(); + } + + private void adjustViewPositions() { + RelativeLayout.LayoutParams param; + + // Logo is 0.48 times the screen width + param = (RelativeLayout.LayoutParams) mLogo.getLayoutParams(); + int size = (int) (mDisplayMetrics.widthPixels * 0.48); + param.width = size; + param.height = size; + mLogo.setLayoutParams(param); + + // Bottom margin of the main body should be two times it's top margin. + param = (RelativeLayout.LayoutParams) mBodyLayout.getLayoutParams(); + param.topMargin = (mDisplayMetrics.heightPixels - + mBodyLayout.getMeasuredHeight() - mStatusBarHeight) / 3; + mBodyLayout.setLayoutParams(param); + mBodyDefaultMarginTop = param.topMargin; + + // The width of the start button is roughly 0.72 + // times the width of the screen + mStartBtn = findViewById(R.id.start_broadcast_button); + param = (RelativeLayout.LayoutParams) mStartBtn.getLayoutParams(); + param.width = (int) (mDisplayMetrics.widthPixels * 0.72); + mStartBtn.setLayoutParams(param); + } + + public void onStartBroadcastClicked(View view) { + checkPermission(); + } + + private void checkPermission() { + boolean granted = true; + for (String per : PERMISSIONS) { + if (!permissionGranted(per)) { + granted = false; + break; + } + } + + if (granted) { + resetLayoutAndForward(); + } else { + requestPermissions(); + } + } + + private boolean permissionGranted(String permission) { + return ContextCompat.checkSelfPermission( + this, permission) == PackageManager.PERMISSION_GRANTED; + } + + private void requestPermissions() { + ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_REQ_CODE); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + if (requestCode == PERMISSION_REQ_CODE) { + boolean granted = true; + for (int result : grantResults) { + granted = (result == PackageManager.PERMISSION_GRANTED); + if (!granted) break; + } + + if (granted) { + resetLayoutAndForward(); + } else { + toastNeedPermissions(); + } + } + } + + private void resetLayoutAndForward() { + closeImeDialogIfNeeded(); + gotoActivity(); + } + + private void closeImeDialogIfNeeded() { + InputMethodManager manager = (InputMethodManager) + getSystemService(Context.INPUT_METHOD_SERVICE); + manager.hideSoftInputFromWindow(mTopicEdit.getWindowToken(), + InputMethodManager.HIDE_NOT_ALWAYS); + } + + public void gotoActivity() { + Annotation[] annotations = BaseActivity.class.getDeclaredAnnotations(); + Class targetActivity = null; + for (Annotation annotation : annotations) { + if (annotation instanceof DisplayActivity) { + boolean found; + String[] targets = ((DisplayActivity) annotation).SubClasses(); + for (String className : targets) { + try { + targetActivity = Class.forName(className); + found = true; + } catch (ClassNotFoundException e) { + found = false; + } + + if (targetActivity != null && found) { + break; + } + } + } + } + + if (targetActivity != null) { + Intent intent = new Intent(this, targetActivity); + intent.putExtra(Constants.KEY_CHANNEL_NAME, + mTopicEdit.getText().toString()); + startActivity(intent); + } + } + + private void toastNeedPermissions() { + Toast.makeText(this, R.string.need_necessary_permissions, Toast.LENGTH_LONG).show(); + } + + @Override + protected void onResume() { + super.onResume(); + resetUI(); + registerLayoutObserverForSoftKeyboard(); + } + + private void resetUI() { + resetLogo(); + closeImeDialogIfNeeded(); + } + + private void resetLogo() { + mLogo.setVisibility(View.VISIBLE); + mBodyLayout.setY(mBodyDefaultMarginTop); + } + + private void registerLayoutObserverForSoftKeyboard() { + View view = getWindow().getDecorView().getRootView(); + ViewTreeObserver observer = view.getViewTreeObserver(); + observer.addOnGlobalLayoutListener(mLayoutObserverListener); + } + + @Override + protected void onPause() { + super.onPause(); + removeLayoutObserverForSoftKeyboard(); + } + + private void removeLayoutObserverForSoftKeyboard() { + View view = getWindow().getDecorView().getRootView(); + view.getViewTreeObserver().removeOnGlobalLayoutListener(mLayoutObserverListener); + } +} diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/annotations/DisplayActivity.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/annotations/DisplayActivity.java new file mode 100644 index 0000000..f54eae3 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/annotations/DisplayActivity.java @@ -0,0 +1,9 @@ +package io.agora.advancedaudio.component.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface DisplayActivity { + String[] SubClasses(); +} diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/rtc/AgoraEventHandler.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/rtc/AgoraEventHandler.java new file mode 100644 index 0000000..6bb78eb --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/rtc/AgoraEventHandler.java @@ -0,0 +1,80 @@ +package io.agora.advancedaudio.component.rtc; + +import java.util.ArrayList; + +import io.agora.rtc.IRtcEngineEventHandler; + +public class AgoraEventHandler extends IRtcEngineEventHandler { + private ArrayList mHandler = new ArrayList<>(); + + public void addHandler(EventHandler handler) { + mHandler.add(handler); + } + + public void removeHandler(EventHandler handler) { + mHandler.remove(handler); + } + + @Override + public void onJoinChannelSuccess(String channel, int uid, int elapsed) { + for (EventHandler handler : mHandler) { + handler.onJoinChannelSuccess(channel, uid, elapsed); + } + } + + @Override + public void onLeaveChannel(RtcStats stats) { + for (EventHandler handler : mHandler) { + handler.onLeaveChannel(stats); + } + } + + @Override + public void onUserJoined(int uid, int elapsed) { + for (EventHandler handler : mHandler) { + handler.onUserJoined(uid, elapsed); + } + } + + @Override + public void onUserOffline(int uid, int reason) { + for (EventHandler handler : mHandler) { + handler.onUserOffline(uid, reason); + } + } + + @Override + public void onRtcStats(IRtcEngineEventHandler.RtcStats stats) { + for (EventHandler handler : mHandler) { + handler.onRtcStats(stats); + } + } + + @Override + public void onNetworkQuality(int uid, int txQuality, int rxQuality) { + for (EventHandler handler : mHandler) { + handler.onNetworkQuality(uid, txQuality, rxQuality); + } + } + + @Override + public void onRemoteAudioStats(IRtcEngineEventHandler.RemoteAudioStats stats) { + for (EventHandler handler : mHandler) { + handler.onRemoteAudioStats(stats); + } + } + + @Override + public void onLastmileQuality(int quality) { + for (EventHandler handler : mHandler) { + handler.onLastmileQuality(quality); + } + } + + @Override + public void onLastmileProbeResult(IRtcEngineEventHandler.LastmileProbeResult result) { + for (EventHandler handler : mHandler) { + handler.onLastmileProbeResult(result); + } + } +} diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/rtc/EventHandler.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/rtc/EventHandler.java new file mode 100644 index 0000000..8a856de --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/rtc/EventHandler.java @@ -0,0 +1,23 @@ +package io.agora.advancedaudio.component.rtc; + +import io.agora.rtc.IRtcEngineEventHandler; + +public interface EventHandler { + void onLeaveChannel(IRtcEngineEventHandler.RtcStats stats); + + void onJoinChannelSuccess(String channel, int uid, int elapsed); + + void onUserOffline(int uid, int reason); + + void onUserJoined(int uid, int elapsed); + + void onLastmileQuality(int quality); + + void onLastmileProbeResult(IRtcEngineEventHandler.LastmileProbeResult result); + + void onRtcStats(IRtcEngineEventHandler.RtcStats stats); + + void onNetworkQuality(int uid, int txQuality, int rxQuality); + + void onRemoteAudioStats(IRtcEngineEventHandler.RemoteAudioStats stats); +} diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/utils/FileUtil.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/utils/FileUtil.java new file mode 100644 index 0000000..1e820bb --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/utils/FileUtil.java @@ -0,0 +1,34 @@ +package io.agora.advancedaudio.component.utils; + +import android.content.Context; +import android.os.Build; +import android.os.Environment; + +import java.io.File; + +public class FileUtil { + private static final String LOG_FOLDER_NAME = "log"; + private static final String LOG_FILE_NAME = "agora-rtc.log"; + + /** + * Initialize the log folder + * @param context Context to find the accessible file folder + * @return the absolute path of the log file + */ + public static String initializeLogFile(Context context) { + File folder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + folder = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), LOG_FOLDER_NAME); + } else { + String path = Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separator + + context.getPackageName() + File.separator + + LOG_FOLDER_NAME; + folder = new File(path); + if (!folder.exists() && !folder.mkdir()) folder = null; + } + + if (folder != null && !folder.exists() && !folder.mkdir()) return ""; + else return new File(folder, LOG_FILE_NAME).getAbsolutePath(); + } +} diff --git a/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/utils/WindowUtil.java b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/utils/WindowUtil.java new file mode 100644 index 0000000..b5e4f63 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/java/io/agora/advancedaudio/component/utils/WindowUtil.java @@ -0,0 +1,28 @@ +package io.agora.advancedaudio.component.utils; + +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +public class WindowUtil { + public static void hideWindowStatusBar(Window window) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(Color.TRANSPARENT); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + public static int getSystemStatusBarHeight(Context context) { + int id = context.getResources().getIdentifier( + "status_bar_height", "dimen", "android"); + return id > 0 ? context.getResources().getDimensionPixelSize(id) : id; + } +} diff --git a/Advanced-Audio-Android/lib-component/src/main/res/color/start_broadcast_text_color.xml b/Advanced-Audio-Android/lib-component/src/main/res/color/start_broadcast_text_color.xml new file mode 100644 index 0000000..956e309 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/color/start_broadcast_text_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/drawable-xxhdpi/live_room_bg_logo.png b/Advanced-Audio-Android/lib-component/src/main/res/drawable-xxhdpi/live_room_bg_logo.png new file mode 100644 index 0000000..ca3defc Binary files /dev/null and b/Advanced-Audio-Android/lib-component/src/main/res/drawable-xxhdpi/live_room_bg_logo.png differ diff --git a/Advanced-Audio-Android/lib-component/src/main/res/drawable-xxhdpi/main_logo.png b/Advanced-Audio-Android/lib-component/src/main/res/drawable-xxhdpi/main_logo.png new file mode 100644 index 0000000..3c2d267 Binary files /dev/null and b/Advanced-Audio-Android/lib-component/src/main/res/drawable-xxhdpi/main_logo.png differ diff --git a/Advanced-Audio-Android/lib-component/src/main/res/drawable/ic_launcher_background.xml b/Advanced-Audio-Android/lib-component/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/drawable/main_background.xml b/Advanced-Audio-Android/lib-component/src/main/res/drawable/main_background.xml new file mode 100644 index 0000000..68f88bf --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/drawable/main_background.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg.xml b/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg.xml new file mode 100644 index 0000000..51d749b --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg_clicked.xml b/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg_clicked.xml new file mode 100644 index 0000000..4a02ac5 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg_clicked.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg_normal.xml b/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg_normal.xml new file mode 100644 index 0000000..0c24ade --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/drawable/start_broadcast_bg_normal.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/layout/activity_main.xml b/Advanced-Audio-Android/lib-component/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d16c12b --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/layout/activity_main.xml @@ -0,0 +1,71 @@ + + + + + + + + + + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b0907ca Binary files /dev/null and b/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..d8ae031 Binary files /dev/null and b/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..2c18de9 Binary files /dev/null and b/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..beed3cd Binary files /dev/null and b/Advanced-Audio-Android/lib-component/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/Advanced-Audio-Android/lib-component/src/main/res/values-zh/strings.xml b/Advanced-Audio-Android/lib-component/src/main/res/values-zh/strings.xml new file mode 100644 index 0000000..dc5bf79 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/values-zh/strings.xml @@ -0,0 +1,9 @@ + + Advanced-Audio-Android + + + 开始直播 + 输入一个话题 + Powered by agora.io + 缺少必要权限 + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/values/colors.xml b/Advanced-Audio-Android/lib-component/src/main/res/values/colors.xml new file mode 100644 index 0000000..8afc89d --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/values/colors.xml @@ -0,0 +1,21 @@ + + + #008577 + #00574B + #D81B60 + + #DEDEDE + #CCCCCC + #989898 + #666666 + #333333 + + #007AFF + #44A2FC + #0ECFFF + #9CD9FF + + #CC262626 + #66000000 + #22000000 + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/values/dimens.xml b/Advanced-Audio-Android/lib-component/src/main/res/values/dimens.xml new file mode 100644 index 0000000..240bf8b --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/values/dimens.xml @@ -0,0 +1,27 @@ + + + + 16dp + 16dp + + 240dp + 224dp + + 12sp + 14sp + 16sp + 18sp + + + 12dp + 18dp + 36dp + 158dp + 48dp + 42dp + 16dp + 48dp + 25dp + 45dp + 48dp + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/values/strings.xml b/Advanced-Audio-Android/lib-component/src/main/res/values/strings.xml new file mode 100644 index 0000000..b682cbf --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/values/strings.xml @@ -0,0 +1,12 @@ + + Advanced-Audio-Android + + + Start Live Broadcast + Pick a topic to chat + Powered by agora.io + Necessary permissions acquired + + +  abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&()+,-:;<=.>?@[]^_`{|}~ + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/values/strings_config.xml b/Advanced-Audio-Android/lib-component/src/main/res/values/strings_config.xml new file mode 100644 index 0000000..9b04438 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/values/strings_config.xml @@ -0,0 +1,10 @@ + + + + + <#YOUR APP ID#> + + + + <#YOUR ACCESS TOKEN#> + diff --git a/Advanced-Audio-Android/lib-component/src/main/res/values/styles.xml b/Advanced-Audio-Android/lib-component/src/main/res/values/styles.xml new file mode 100644 index 0000000..28aab42 --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/Advanced-Audio-Android/lib-component/src/test/java/io/agora/advancedaudio/ExampleUnitTest.java b/Advanced-Audio-Android/lib-component/src/test/java/io/agora/advancedaudio/ExampleUnitTest.java new file mode 100644 index 0000000..307c9fb --- /dev/null +++ b/Advanced-Audio-Android/lib-component/src/test/java/io/agora/advancedaudio/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package io.agora.advancedaudio; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/Advanced-Audio-Android/sample-custom-recorder/.gitignore b/Advanced-Audio-Android/sample-custom-recorder/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/.gitignore @@ -0,0 +1 @@ +/build diff --git a/Advanced-Audio-Android/sample-custom-recorder/build.gradle b/Advanced-Audio-Android/sample-custom-recorder/build.gradle new file mode 100644 index 0000000..37fcfea --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + + + defaultConfig { + applicationId "io.agora.advancedaudio.customrecorder" + minSdkVersion 16 + 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' + implementation project(path: ':lib-component') +} diff --git a/Advanced-Audio-Android/sample-custom-recorder/proguard-rules.pro b/Advanced-Audio-Android/sample-custom-recorder/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/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 diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/androidTest/java/io/agora/advancedaudio/ExampleInstrumentedTest.java b/Advanced-Audio-Android/sample-custom-recorder/src/androidTest/java/io/agora/advancedaudio/ExampleInstrumentedTest.java new file mode 100644 index 0000000..04022a4 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/androidTest/java/io/agora/advancedaudio/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package io.agora.advancedaudio; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("io.agora.advancedaudio", appContext.getPackageName()); + } +} diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/AndroidManifest.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7688cea --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorder.java b/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorder.java new file mode 100644 index 0000000..658636c --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorder.java @@ -0,0 +1,45 @@ +package io.agora.advancedaudio.customrecorder; + +import android.media.AudioRecord; +import android.media.MediaRecorder; + +class CustomRecorder { + private AudioRecord mAudioRecord; + private CustomRecorderConfig mConfig; + + CustomRecorder() { + mConfig = CustomRecorderConfig.getDefaultConfig(); + mAudioRecord = new AudioRecord( + MediaRecorder.AudioSource.MIC, + mConfig.getSampleRate(), + mConfig.getChannelCount(), + mConfig.getAudioFormat(), + mConfig.getBufferSize()); + } + + void start() { + if (mAudioRecord != null) { + try { + mAudioRecord.startRecording(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + void stop() { + if (mAudioRecord != null) { + mAudioRecord.stop(); + mAudioRecord.release(); + mAudioRecord = null; + } + } + + CustomRecorderConfig getConfig() { + return mConfig; + } + + int read(byte[] buffer, int offset, int size) { + return mAudioRecord.read(buffer, offset, size); + } +} diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderActivity.java b/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderActivity.java new file mode 100644 index 0000000..2e1e918 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderActivity.java @@ -0,0 +1,90 @@ +package io.agora.advancedaudio.customrecorder; + +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; + +import io.agora.advancedaudio.Constants; +import io.agora.advancedaudio.R; +import io.agora.advancedaudio.component.activities.BaseActivity; + +public class CustomRecorderActivity extends BaseActivity { + private static final String TAG = CustomRecorderActivity.class.getSimpleName(); + + private Intent mServiceIntent; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + initUI(); + registerRtcEventHandler(this); + joinChannel(); + } + + private void initUI() { + setContentView(R.layout.activity_audio_room); + findViewById(R.id.live_btn_mute_audio).setActivated(true); + } + + private void joinChannel() { + String channelName = getIntent(). + getStringExtra(Constants.KEY_CHANNEL_NAME); + rtcEngine().setClientRole(io.agora.rtc.Constants.CLIENT_ROLE_BROADCASTER); + + // Notify the Rtc Engine that we want to use external + // audio sources, instead of creating a recorder inside + // the engine. + // This must be called before joining a channel. So + // there must be a way to obtain the recording + // parameters globally. + // Here we take the default recording configuration + // as an example. Developers should implement the + // mechanism of their own. + CustomRecorderConfig config = + CustomRecorderConfig.getDefaultConfig(); + rtcEngine().setExternalAudioSource(true, + config.getSampleRate(), + config.getChannelCount()); + rtcEngine().joinChannel(null, channelName, null, 0); + } + + private void leaveChannel() { + rtcEngine().leaveChannel(); + } + + @Override + public void onJoinChannelSuccess(String channel, int uid, int elapsed) { + Log.i(TAG, "onJoinChannelSuccess " + (uid & 0xFFFFFFFFL)); + startRecordService(); + } + + private void startRecordService() { + mServiceIntent = new Intent(this, CustomRecorderService.class); + startService(mServiceIntent); + } + + private void stopRecordService() { + if (mServiceIntent != null) { + stopService(mServiceIntent); + } + } + + @Override + public void finish() { + stopRecordService(); + removeRtcEventHandler(this); + leaveChannel(); + super.finish(); + } + + public void onLeaveClicked(View view) { + finish(); + } + + public void onMuteAudioClicked(View view) { + boolean activated = view.isActivated(); + rtcEngine().muteLocalAudioStream(activated); + view.setActivated(!activated); + } +} diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderConfig.java b/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderConfig.java new file mode 100644 index 0000000..4d2701d --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderConfig.java @@ -0,0 +1,87 @@ +package io.agora.advancedaudio.customrecorder; + +import android.media.AudioFormat; +import android.media.AudioRecord; + +class CustomRecorderConfig { + private static final int DEFAULT_SAMPLE_RATE = 16000; + + private int mSampleRate; + private int mChannel; + private int mAudioFormat; + private int mBufferSize; + + private static CustomRecorderConfig sDefaultConfig; + + int getSampleRate() { + return mSampleRate; + } + + void setSampleRate(int mSampleRate) { + this.mSampleRate = mSampleRate; + } + + int getChannelCount() { + switch (mChannel) { + case AudioFormat.CHANNEL_IN_MONO: + return 1; + case AudioFormat.CHANNEL_IN_STEREO: + return 2; + default: + return 0; + } + } + + /** + * Set audio channel count + * @param channel One of AudioFormat.CHANNEL_IN_MONO + * or AudioFormat.CHANNEL_IN_STEREO + */ + void setChannel(int channel) { + mChannel = channel; + } + + int getChannelType() { + return mChannel; + } + + int getAudioFormat() { + return mAudioFormat; + } + + void setAudioFormat(int mAudioFormat) { + this.mAudioFormat = mAudioFormat; + } + + int getBufferSize() { + return mBufferSize; + } + + void setBufferSize(int mBufferSize) { + this.mBufferSize = mBufferSize; + } + + /** + * Get a default audio recording configuration with: + * 1) Sample rate: 16KHz + * 2) Channel count: mono (1) + * 3) Audio format: AudioFormat.ENCODING_PCM_16BIT + * 4) Buffer size: twice the minimum buffer size + * @return + */ + static CustomRecorderConfig getDefaultConfig() { + if (sDefaultConfig == null) { + sDefaultConfig = new CustomRecorderConfig(); + sDefaultConfig.setSampleRate(DEFAULT_SAMPLE_RATE); + sDefaultConfig.setChannel(AudioFormat.CHANNEL_IN_MONO); + sDefaultConfig.setAudioFormat(AudioFormat.ENCODING_PCM_16BIT); + int bufSize = AudioRecord.getMinBufferSize( + sDefaultConfig.getSampleRate(), + sDefaultConfig.getChannelType(), + sDefaultConfig.getAudioFormat()); + sDefaultConfig.setBufferSize(bufSize * 2); + } + + return sDefaultConfig; + } +} diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderService.java b/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderService.java new file mode 100644 index 0000000..e8eeb45 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/java/io/agora/advancedaudio/customrecorder/CustomRecorderService.java @@ -0,0 +1,138 @@ +package io.agora.advancedaudio.customrecorder; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.media.AudioRecord; +import android.os.Build; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; + +import io.agora.advancedaudio.AgoraApplication; +import io.agora.rtc.RtcEngine; + +public class CustomRecorderService extends Service { + private static final String TAG = CustomRecorderService.class.getSimpleName(); + + private RecordThread mThread; + private volatile boolean mStopped; + + public AgoraApplication application() { + return (AgoraApplication) getApplication(); + } + + public RtcEngine rtcEngine() { + return application().rtcEngine(); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + startForeground(); + startRecording(); + return Service.START_STICKY; + } + + private void startForeground() { + createNotificationChannel(); + Intent notificationIntent = new Intent(this, CustomRecorderActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, + 0, notificationIntent, 0); + + Notification notification = new NotificationCompat.Builder(this, TAG) + .setContentTitle(TAG) + .setContentIntent(pendingIntent) + .build(); + + startForeground(1, notification); + } + + private void createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel serviceChannel = new NotificationChannel( + TAG, TAG, NotificationManager.IMPORTANCE_DEFAULT + ); + + NotificationManager manager = getSystemService(NotificationManager.class); + manager.createNotificationChannel(serviceChannel); + } + } + + private void startRecording() { + mThread = new RecordThread(); + mThread.start(); + } + + private void stopRecording() { + mStopped = true; + } + + @Override + public void onDestroy() { + stopRecording(); + super.onDestroy(); + } + + private class RecordThread extends Thread { + private CustomRecorder mRecorder; + private byte[] mBuffer; + + RecordThread() { + mRecorder = new CustomRecorder(); + CustomRecorderConfig config = mRecorder.getConfig(); + mBuffer = new byte[config.getBufferSize()]; + } + + @Override + public void run() { + mRecorder.start(); + while (!mStopped) { + int result = mRecorder.read(mBuffer, 0, mBuffer.length); + if (result >= 0) { + rtcEngine().pushExternalAudioFrame( + mBuffer, System.currentTimeMillis()); + } else { + logRecordError(result); + } + } + release(); + } + + private void logRecordError(int error) { + String message = ""; + switch (error) { + case AudioRecord.ERROR: + message = "generic operation failure"; + break; + case AudioRecord.ERROR_BAD_VALUE: + message = "failure due to the use of an invalid value"; + break; + case AudioRecord.ERROR_DEAD_OBJECT: + message = "object is no longer valid and needs to be recreated"; + break; + case AudioRecord.ERROR_INVALID_OPERATION: + message = "failure due to the improper use of method"; + break; + } + Log.e(TAG, message); + } + + private void release() { + if (mRecorder != null) { + mRecorder.stop(); + mBuffer = null; + } + } + } +} diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_audio_disabled.png b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_audio_disabled.png new file mode 100644 index 0000000..49dda96 Binary files /dev/null and b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_audio_disabled.png differ diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_audio_enabled.png b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_audio_enabled.png new file mode 100644 index 0000000..0e3dae8 Binary files /dev/null and b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_audio_enabled.png differ diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_leave.png b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_leave.png new file mode 100644 index 0000000..c8f42c3 Binary files /dev/null and b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/btn_leave.png differ diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/live_room_bg_logo.png b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/live_room_bg_logo.png new file mode 100644 index 0000000..ca3defc Binary files /dev/null and b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable-xxhdpi/live_room_bg_logo.png differ diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/btn_mute_audio.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/btn_mute_audio.xml new file mode 100644 index 0000000..b5a2b3c --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/btn_mute_audio.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/ic_launcher_background.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/live_room_bg.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/live_room_bg.xml new file mode 100644 index 0000000..c736ef8 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/live_room_bg.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/rounded_bg_full_transparent.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/rounded_bg_full_transparent.xml new file mode 100644 index 0000000..8e47806 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/rounded_bg_full_transparent.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/rounded_bg_half_transparent.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/rounded_bg_half_transparent.xml new file mode 100644 index 0000000..8e47806 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/drawable/rounded_bg_half_transparent.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/layout/activity_audio_room.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/layout/activity_audio_room.xml new file mode 100644 index 0000000..31c2d73 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/layout/activity_audio_room.xml @@ -0,0 +1,47 @@ + + + + + + + + + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b0907ca Binary files /dev/null and b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..d8ae031 Binary files /dev/null and b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..2c18de9 Binary files /dev/null and b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..beed3cd Binary files /dev/null and b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values-zh/strings.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values-zh/strings.xml new file mode 100644 index 0000000..432cedb --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values-zh/strings.xml @@ -0,0 +1,3 @@ + + Custom-Device + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/colors.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/colors.xml new file mode 100644 index 0000000..69b2233 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #008577 + #00574B + #D81B60 + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/dimens.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/dimens.xml new file mode 100644 index 0000000..62c1e0a --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/dimens.xml @@ -0,0 +1,21 @@ + + + 76dp + 36dp + 15dp + 22dp + 186dp + 48dp + 44dp + 2dp + 6dp + 36dp + 36dp + 24dp + 8dp + 44dp + 18dp + 12dp + 68dp + 180dp + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/strings.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/strings.xml new file mode 100644 index 0000000..403c3ba --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + 自定义音频源 + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/styles.xml b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/styles.xml new file mode 100644 index 0000000..28aab42 --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/Advanced-Audio-Android/sample-custom-recorder/src/test/java/io/agora/advancedaudio/ExampleUnitTest.java b/Advanced-Audio-Android/sample-custom-recorder/src/test/java/io/agora/advancedaudio/ExampleUnitTest.java new file mode 100644 index 0000000..307c9fb --- /dev/null +++ b/Advanced-Audio-Android/sample-custom-recorder/src/test/java/io/agora/advancedaudio/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package io.agora.advancedaudio; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/Advanced-Audio-Android/settings.gradle b/Advanced-Audio-Android/settings.gradle new file mode 100644 index 0000000..08f95ae --- /dev/null +++ b/Advanced-Audio-Android/settings.gradle @@ -0,0 +1,2 @@ +include ':lib-component', ':sample-custom-recorder' +rootProject.name='Advanced-Audio-Android' diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..3d66931 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,17 @@ +trigger: +- master +- dev/* + +pool: + vmImage: 'macos-latest' + +variables: +- group: AgoraKeys + +jobs: + +- template: Advanced-Audio-Android/build-template/build-android.yml + parameters: + project: 'Advanced-Audio-Android' + module: 'sample-custom-recorder' + name: 'CustomRecorder' \ No newline at end of file