├── .gitignore ├── .pullapprove.yml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── leancloudlivekit ├── .gitignore ├── build.gradle ├── libs │ ├── pldroid-camera-streaming-1.7.1.jar │ └── pldroid-player-1.3.0.jar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── leancloud │ │ └── leancloudlivekit │ │ ├── LCLKUser.java │ │ ├── LCLiveKit.java │ │ ├── LCLiveKitProvider.java │ │ ├── barrage │ │ └── LCLKBarrageLayout.java │ │ ├── handler │ │ ├── LCLKClientEventHandler.java │ │ ├── LCLKConversationHandler.java │ │ └── LCLKMessageHandler.java │ │ ├── im │ │ ├── LCLKChatAdapter.java │ │ ├── LCLKChatItemStatusHolder.java │ │ ├── LCLKChatItemTextHolder.java │ │ ├── LCLKCommonListAdapter.java │ │ ├── LCLKCommonViewHolder.java │ │ ├── LCLKGiftDialogFragment.java │ │ ├── LCLKGiftItem.java │ │ ├── LCLKGiftManager.java │ │ ├── LCLKGiftMessage.java │ │ ├── LCLKGiftPagerAdapter.java │ │ ├── LCLKIMBarrageMessage.java │ │ ├── LCLKIMFragment.java │ │ ├── LCLKIMMessage.java │ │ ├── LCLKIMMessageEvent.java │ │ ├── LCLKIMStatusMessage.java │ │ └── LCLKRoundCornerImageView.java │ │ ├── play │ │ ├── LCLKPlayActivity.java │ │ └── LCLKPlayFragment.java │ │ ├── record │ │ ├── LCLKRecordActivity.java │ │ └── LCLKRecordFragment.java │ │ └── utils │ │ ├── LCLKConstants.java │ │ └── LCLKProfileCache.java │ ├── jniLibs │ ├── arm64-v8a │ │ ├── libpldroid_mmprocessing.so │ │ ├── libpldroid_streaming_aac_encoder.so │ │ ├── libpldroid_streaming_core.so │ │ ├── libpldroid_streaming_h264_encoder.so │ │ └── libpldroidplayer.so │ ├── armeabi-v7a │ │ ├── libpldroid_mmprocessing.so │ │ ├── libpldroid_streaming_aac_encoder.so │ │ ├── libpldroid_streaming_core.so │ │ ├── libpldroid_streaming_h264_encoder.so │ │ └── libpldroidplayer.so │ └── armeabi │ │ ├── libpldroid_mmprocessing.so │ │ ├── libpldroid_streaming_aac_encoder.so │ │ ├── libpldroid_streaming_core.so │ │ ├── libpldroid_streaming_h264_encoder.so │ │ └── libpldroidplayer.so │ └── res │ ├── drawable │ ├── lclk_barrage_item_bg.xml │ ├── lclk_im_gift_bg.xml │ ├── lclk_im_input_bg.xml │ ├── lclk_im_item_content_bg.9.png │ └── lclk_praise_bg.xml │ ├── layout │ ├── lclk_barrage_gift_item.xml │ ├── lclk_barrage_item.xml │ ├── lclk_gift_dialog.xml │ ├── lclk_gift_item.xml │ ├── lclk_im_barrage_layout.xml │ ├── lclk_im_chat_item_text_layout.xml │ ├── lclk_im_chat_status_item.xml │ ├── lclk_im_fragment.xml │ ├── lclk_im_gift_layout.xml │ ├── lclk_im_im_layout.xml │ ├── lclk_im_input_layout.xml │ ├── lclk_im_personal_layout.xml │ ├── lclk_play_activity.xml │ ├── lclk_play_fragment.xml │ ├── lclk_record_activity.xml │ └── lclk_record_fragment.xml │ ├── mipmap-xxhdpi │ ├── lclk_camera_switch.png │ ├── lclk_default_avatar_icon.png │ ├── lclk_im_close.png │ ├── lclk_im_gift_1.png │ ├── lclk_im_gift_2.png │ ├── lclk_im_gift_3.png │ ├── lclk_im_gift_4.png │ ├── lclk_im_gift_5.png │ ├── lclk_im_gift_6.png │ ├── lclk_im_gift_7.png │ ├── lclk_im_gift_8.png │ └── lclk_star.png │ └── values │ ├── lclk_colors.xml │ ├── lclk_dimens.xml │ ├── lclk_strings.xml │ └── lclk_styles.xml ├── livekitapplication ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── leancloud │ │ └── livekitapplication │ │ ├── LCLKAppProvider.java │ │ ├── LCLKApplication.java │ │ ├── LCLKLiveRoomCreateActivity.java │ │ ├── LCLKLiveRoomItemClickEvent.java │ │ ├── LCLKLiveRoomItemHolder.java │ │ ├── LCLKLiveRoomListActivity.java │ │ ├── LCLKLoginActivity.java │ │ └── LCLiveRoom.java │ └── res │ ├── drawable │ ├── edittext_bg.xml │ ├── start_channel_btn_bg.xml │ └── start_channel_image_bg.xml │ ├── layout │ ├── lclk_live_room_create_activity.xml │ ├── lclk_live_room_item.xml │ ├── lclk_live_room_list_activity.xml │ └── lclk_login_activity.xml │ ├── menu │ └── lclk_menu_live_create.xml │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── lclk_live_add_image.png │ └── values │ ├── lclk_colors.xml │ ├── lclk_dimens.xml │ ├── lclk_strings.xml │ └── lclk_styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # ADT 16 | .classpath 17 | .project 18 | .settings 19 | local.properties 20 | bin 21 | gen 22 | _layouts 23 | proguard.cfg 24 | 25 | # OSX 26 | .DS_Store 27 | 28 | # Gradle files 29 | .gradle/ 30 | build/ 31 | 32 | # Local configuration file (sdk path, etc) 33 | local.properties 34 | 35 | # Proguard folder generated by Eclipse 36 | proguard/ 37 | 38 | # IDEA 39 | *.iml 40 | *.ipr 41 | *.iws 42 | out 43 | .idea 44 | 45 | # Maven 46 | target 47 | release.properties 48 | pom.xml.* 49 | 50 | # VIM 51 | *~ 52 | *.swp 53 | -------------------------------------------------------------------------------- /.pullapprove.yml: -------------------------------------------------------------------------------- 1 | approve_by_comment: true 2 | approve_regex: '^(Reviewed|Approved|LGTM)' 3 | reject_regex: '^Rejected' 4 | reset_on_push: false 5 | reviewers: 6 | required: 1 7 | members: 8 | - daweibayu 9 | - jwfing 10 | name: default 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LiveKit 直播+聊天 UI 套件 · Android 2 | 3 | LiveKit 是由 [LeanCloud](http://leancloud.cn) 官方推出的基于 [LeanCloud 实时通信](https://leancloud.cn/docs/realtime_v2.html) 与 [七牛直播服务](http://developer.qiniu.com/article/pili/sdk/server-sdk.html) 的 UI 套件。它包含直播、文字聊天、弹幕、送礼物等界面,方便开发者为自己的项目快速植入直播与聊天功能,并灵活定制界面。直播与聊天的数据都保存在云端,支持 CDN 加速,开发者无需为数据的存储和访问速度而担忧。 4 | 5 | 具体使用方法请阅读 [官方文档](https://leancloud.cn/docs/livekit-android.html)。 6 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | google() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:3.0.1' 8 | } 9 | } 10 | 11 | allprojects { 12 | repositories { 13 | jcenter() 14 | maven { 15 | url "http://mvn.leancloud.cn/nexus/content/repositories/releases" 16 | } 17 | google() 18 | } 19 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /leancloudlivekit/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /leancloudlivekit/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion '26.0.2' 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | multiDexEnabled true 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | lintOptions { 21 | abortOnError false 22 | } 23 | } 24 | 25 | dependencies { 26 | compile fileTree(dir: 'libs', include: ['*.jar']) 27 | 28 | compile 'com.android.support:support-v4:23.4.0' 29 | compile 'com.android.support:appcompat-v7:23.4.0' 30 | compile 'com.android.support:gridlayout-v7:23.4.0' 31 | compile 'com.android.support:recyclerview-v7:23.4.0' 32 | 33 | //avoscloud-sdk 为 LeanCloud基础包 34 | compile ('cn.leancloud.android:avoscloud-sdk:v4.+') 35 | 36 | //avoscloud-push 为推送与实时聊天需要的包 37 | compile ('cn.leancloud.android:avoscloud-push:v4.+@aar'){transitive = true} 38 | 39 | 40 | compile 'com.qiniu:happy-dns:0.2.7' 41 | compile 'com.squareup.okhttp:okhttp:2.5.0' 42 | compile 'com.google.code.gson:gson:2.3.1' 43 | 44 | compile 'com.squareup.picasso:picasso:2.5.2' 45 | compile 'de.greenrobot:eventbus:2.4.0' 46 | } 47 | -------------------------------------------------------------------------------- /leancloudlivekit/libs/pldroid-camera-streaming-1.7.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/libs/pldroid-camera-streaming-1.7.1.jar -------------------------------------------------------------------------------- /leancloudlivekit/libs/pldroid-player-1.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/libs/pldroid-player-1.3.0.jar -------------------------------------------------------------------------------- /leancloudlivekit/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/wli/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/LCLKUser.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit; 2 | 3 | /** 4 | * Created by wli on 16/8/4. 5 | * * LeanCloudLiveKit 中的用户类,仅包含三个变量,暂不支持继承扩展 6 | */ 7 | public final class LCLKUser { 8 | 9 | /** 10 | * 用户 id 11 | */ 12 | private String userId; 13 | 14 | /** 15 | * 用户头像地址 16 | */ 17 | private String avatarUrl; 18 | 19 | /** 20 | * 用户别名 21 | */ 22 | private String name; 23 | 24 | public LCLKUser(String userId, String userName, String avatarUrl) { 25 | this.userId = userId; 26 | this.avatarUrl = avatarUrl; 27 | this.name = userName; 28 | } 29 | 30 | public String getUserId() { 31 | return userId; 32 | } 33 | 34 | public String getAvatarUrl() { 35 | return avatarUrl; 36 | } 37 | 38 | public String getUserName() { 39 | return name; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/LCLiveKit.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit; 2 | 3 | 4 | import android.content.Context; 5 | import android.text.TextUtils; 6 | 7 | import com.avos.avoscloud.AVOSCloud; 8 | import com.avos.avoscloud.SignatureFactory; 9 | import com.avos.avoscloud.im.v2.AVIMClient; 10 | import com.avos.avoscloud.im.v2.AVIMException; 11 | import com.avos.avoscloud.im.v2.AVIMMessageManager; 12 | import com.avos.avoscloud.im.v2.AVIMTypedMessage; 13 | import com.avos.avoscloud.im.v2.callback.AVIMClientCallback; 14 | import com.pili.pldroid.streaming.StreamingEnv; 15 | 16 | import cn.leancloud.leancloudlivekit.handler.LCLKClientEventHandler; 17 | import cn.leancloud.leancloudlivekit.handler.LCLKConversationHandler; 18 | import cn.leancloud.leancloudlivekit.handler.LCLKMessageHandler; 19 | import cn.leancloud.leancloudlivekit.im.LCLKGiftMessage; 20 | import cn.leancloud.leancloudlivekit.im.LCLKIMBarrageMessage; 21 | import cn.leancloud.leancloudlivekit.im.LCLKIMMessage; 22 | import cn.leancloud.leancloudlivekit.im.LCLKIMStatusMessage; 23 | 24 | /** 25 | * Created by wli on 16/8/4. 26 | * * LeanCloudLiveKit 的管理类 27 | */ 28 | public class LCLiveKit { 29 | 30 | private static LCLiveKit lcLiveKit; 31 | private LCLiveKitProvider liveKitProvider; 32 | private String currentUserId; 33 | 34 | private LCLiveKit() { 35 | } 36 | 37 | public static synchronized LCLiveKit getInstance() { 38 | if (null == lcLiveKit) { 39 | lcLiveKit = new LCLiveKit(); 40 | } 41 | return lcLiveKit; 42 | } 43 | 44 | /** 45 | * 初始化 LeanCloudLiveKit,此函数要在 Application 的 onCreate 中调用 46 | * 47 | * @param context 48 | * @param appId 49 | * @param appKey 50 | */ 51 | public void init(Context context, String appId, String appKey) { 52 | if (TextUtils.isEmpty(appId)) { 53 | throw new IllegalArgumentException("appId can not be empty!"); 54 | } 55 | if (TextUtils.isEmpty(appKey)) { 56 | throw new IllegalArgumentException("appKey can not be empty!"); 57 | } 58 | 59 | StreamingEnv.init(context); 60 | 61 | AVOSCloud.initialize(context.getApplicationContext(), appId, appKey); 62 | 63 | // 消息处理 handler 64 | AVIMMessageManager.registerMessageHandler(AVIMTypedMessage.class, new LCLKMessageHandler(context)); 65 | 66 | // 与网络相关的 handler 67 | AVIMClient.setClientEventHandler(LCLKClientEventHandler.getInstance()); 68 | 69 | // 和 Conversation 相关的事件的 handler 70 | AVIMMessageManager.setConversationEventHandler(LCLKConversationHandler.getInstance()); 71 | 72 | // 默认设置为离线消息仅推送数量 73 | AVIMClient.setOfflineMessagePush(true); 74 | 75 | AVIMMessageManager.registerAVIMMessageType(LCLKIMMessage.class); 76 | AVIMMessageManager.registerAVIMMessageType(LCLKIMBarrageMessage.class); 77 | AVIMMessageManager.registerAVIMMessageType(LCLKGiftMessage.class); 78 | AVIMMessageManager.registerAVIMMessageType(LCLKIMStatusMessage.class); 79 | } 80 | public void setProfileProvider(LCLiveKitProvider profileProvider) { 81 | this.liveKitProvider = profileProvider; 82 | } 83 | 84 | /** 85 | * 获取当前的用户体系 86 | * 87 | * @return 88 | */ 89 | public LCLiveKitProvider getProfileProvider() { 90 | return liveKitProvider; 91 | } 92 | 93 | /** 94 | * 设置签名工厂 95 | * 96 | * @param signatureFactory 97 | */ 98 | public void setSignatureFactory(SignatureFactory signatureFactory) { 99 | AVIMClient.setSignatureFactory(signatureFactory); 100 | } 101 | 102 | /** 103 | * 开启实时聊天 104 | * 105 | * @param userId 106 | * @param callback 107 | */ 108 | public void open(final String userId, final AVIMClientCallback callback) { 109 | if (TextUtils.isEmpty(userId)) { 110 | throw new IllegalArgumentException("userId can not be empty!"); 111 | } 112 | if (null == callback) { 113 | throw new IllegalArgumentException("callback can not be null!"); 114 | } 115 | 116 | AVIMClient.getInstance(userId).open(new AVIMClientCallback() { 117 | @Override 118 | public void done(final AVIMClient avimClient, AVIMException e) { 119 | if (null == e) { 120 | currentUserId = userId; 121 | } 122 | callback.internalDone(avimClient, e); 123 | } 124 | }); 125 | } 126 | 127 | /** 128 | * 关闭实时聊天 129 | * 130 | * @param callback 131 | */ 132 | public void close(final AVIMClientCallback callback) { 133 | AVIMClient.getInstance(currentUserId).close(new AVIMClientCallback() { 134 | @Override 135 | public void done(AVIMClient avimClient, AVIMException e) { 136 | currentUserId = null; 137 | if (null != callback) { 138 | callback.internalDone(avimClient, e); 139 | } 140 | } 141 | }); 142 | } 143 | 144 | /** 145 | * 获取当前的实时聊天的用户 146 | * 147 | * @return 148 | */ 149 | public String getCurrentUserId() { 150 | return currentUserId; 151 | } 152 | 153 | /** 154 | * 获取当前的 AVIMClient 实例 155 | * 156 | * @return 157 | */ 158 | public AVIMClient getClient() { 159 | if (!TextUtils.isEmpty(currentUserId)) { 160 | return AVIMClient.getInstance(currentUserId); 161 | } 162 | return null; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/LCLiveKitProvider.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit; 2 | 3 | import com.avos.avoscloud.AVCallback; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by wli on 16/8/4. 9 | * LeanCloudLiveKit 用户体系的接口 10 | * 开发者需要实现此接口并调用 LCLiveKit.getInstance().setProfiProvider(LCLiveKitProvider) 11 | * 来接入 LeanCloudLiveKit 12 | */ 13 | public interface LCLiveKitProvider { 14 | 15 | /** 16 | * 根据用户 id 获取用户的详细信息 17 | * @param userIdList 18 | * @param profilesCallBack 19 | */ 20 | void fetchProfiles(List userIdList, AVCallback> profilesCallBack); 21 | 22 | /** 23 | * 根据 LiveId 拉取推流地址 24 | * @param liveId 25 | * @param streamCallback 26 | */ 27 | void fetchRecordStream(String liveId, AVCallback streamCallback); 28 | 29 | /** 30 | * 根据 LiveId 拉取播放地址 31 | * @param liveId 32 | * @param streamCallback 33 | */ 34 | void fetchPlayStream(String liveId, AVCallback streamCallback); 35 | } 36 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/barrage/LCLKBarrageLayout.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.barrage; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.view.animation.Animation; 9 | import android.view.animation.LinearInterpolator; 10 | import android.view.animation.TranslateAnimation; 11 | import android.widget.RelativeLayout; 12 | import android.widget.TextView; 13 | 14 | import java.util.Random; 15 | 16 | import cn.leancloud.leancloudlivekit.R; 17 | 18 | /** 19 | * Created by wli on 16/7/21. 20 | */ 21 | public class LCLKBarrageLayout extends RelativeLayout { 22 | 23 | public LCLKBarrageLayout(Context context) { 24 | super(context); 25 | } 26 | 27 | public LCLKBarrageLayout(Context context, AttributeSet attrs) { 28 | super(context, attrs); 29 | } 30 | 31 | public LCLKBarrageLayout(Context context, AttributeSet attrs, int defStyleAttr) { 32 | super(context, attrs, defStyleAttr); 33 | } 34 | 35 | public void sendView(final View view) { 36 | int viewHeight = getResources().getDimensionPixelSize(R.dimen.lclk_im_barrage_item_width); 37 | int y = (getHeight() - viewHeight) * new Random().nextInt(100) / 100; 38 | Animation anim = createTranslateAnim(getWidth(), -1 * getWidth(), y); 39 | anim.setAnimationListener(new Animation.AnimationListener() { 40 | @Override 41 | public void onAnimationStart(Animation animation) { 42 | 43 | } 44 | 45 | @Override 46 | public void onAnimationEnd(Animation animation) { 47 | removeView(view); 48 | } 49 | 50 | @Override 51 | public void onAnimationRepeat(Animation animation) { 52 | } 53 | }); 54 | view.startAnimation(anim); 55 | 56 | addView(view); 57 | } 58 | 59 | @Override 60 | protected void onDetachedFromWindow() { 61 | super.onDetachedFromWindow(); 62 | } 63 | 64 | @Override 65 | protected void onVisibilityChanged(View changedView, int visibility) { 66 | super.onVisibilityChanged(changedView, visibility); 67 | } 68 | 69 | private static Animation createTranslateAnim(int fromX, int toX, int y) { 70 | TranslateAnimation tlAnim = new TranslateAnimation(fromX, toX, y, y); 71 | long duration = 3000; 72 | tlAnim.setDuration(duration); 73 | tlAnim.setFillEnabled(false); 74 | tlAnim.setInterpolator(new LinearInterpolator()); 75 | return tlAnim; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/handler/LCLKClientEventHandler.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.handler; 2 | 3 | import com.avos.avoscloud.im.v2.AVIMClient; 4 | import com.avos.avoscloud.im.v2.AVIMClientEventHandler; 5 | 6 | /** 7 | * Created by wli on 16/8/4. 8 | */ 9 | public class LCLKClientEventHandler extends AVIMClientEventHandler { 10 | 11 | private static LCLKClientEventHandler eventHandler; 12 | 13 | public static synchronized LCLKClientEventHandler getInstance() { 14 | if (null == eventHandler) { 15 | eventHandler = new LCLKClientEventHandler(); 16 | } 17 | return eventHandler; 18 | } 19 | 20 | private LCLKClientEventHandler() { 21 | } 22 | 23 | 24 | private volatile boolean connect = false; 25 | 26 | /** 27 | * 是否连上聊天服务 28 | * 29 | * @return 30 | */ 31 | public boolean isConnect() { 32 | return connect; 33 | } 34 | 35 | public void setConnectAndNotify(boolean isConnect) { 36 | connect = isConnect; 37 | // EventBus.getDefault().post(new LCIMConnectionChangeEvent(connect)); 38 | } 39 | 40 | @Override 41 | public void onConnectionPaused(AVIMClient avimClient) { 42 | setConnectAndNotify(false); 43 | } 44 | 45 | @Override 46 | public void onConnectionResume(AVIMClient avimClient) { 47 | setConnectAndNotify(true); 48 | } 49 | 50 | @Override 51 | public void onClientOffline(AVIMClient avimClient, int i) { 52 | // LCIMLogUtils.d("client " + avimClient.getClientId() + " is offline!"); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/handler/LCLKConversationHandler.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.handler; 2 | 3 | import com.avos.avoscloud.im.v2.AVIMClient; 4 | import com.avos.avoscloud.im.v2.AVIMConversation; 5 | import com.avos.avoscloud.im.v2.AVIMConversationEventHandler; 6 | 7 | import java.util.List; 8 | 9 | 10 | /** 11 | * Created by wli on 16/8/4. 12 | */ 13 | public class LCLKConversationHandler extends AVIMConversationEventHandler { 14 | 15 | private static LCLKConversationHandler eventHandler; 16 | 17 | public static synchronized LCLKConversationHandler getInstance() { 18 | if (null == eventHandler) { 19 | eventHandler = new LCLKConversationHandler(); 20 | } 21 | return eventHandler; 22 | } 23 | 24 | private LCLKConversationHandler() { 25 | } 26 | 27 | // @Override 28 | public void onOfflineMessagesUnread(AVIMClient client, AVIMConversation conversation, int unreadCount) { 29 | } 30 | 31 | @Override 32 | public void onMemberLeft(AVIMClient client, AVIMConversation conversation, List members, String kickedBy) { 33 | // 因为不同用户需求不同,此处暂不做默认处理,如有需要,用户可以通过自定义 Handler 实现 34 | } 35 | 36 | @Override 37 | public void onMemberJoined(AVIMClient client, AVIMConversation conversation, List members, String invitedBy) { 38 | } 39 | 40 | @Override 41 | public void onKicked(AVIMClient client, AVIMConversation conversation, String kickedBy) { 42 | } 43 | 44 | @Override 45 | public void onInvited(AVIMClient client, AVIMConversation conversation, String operator) { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/handler/LCLKMessageHandler.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.handler; 2 | 3 | import android.content.Context; 4 | import com.avos.avoscloud.im.v2.AVIMClient; 5 | import com.avos.avoscloud.im.v2.AVIMConversation; 6 | import com.avos.avoscloud.im.v2.AVIMTypedMessage; 7 | import com.avos.avoscloud.im.v2.AVIMTypedMessageHandler; 8 | 9 | import cn.leancloud.leancloudlivekit.LCLiveKit; 10 | import cn.leancloud.leancloudlivekit.im.LCLKIMMessageEvent; 11 | import de.greenrobot.event.EventBus; 12 | 13 | /** 14 | * Created by wli on 16/8/4. 15 | */ 16 | public class LCLKMessageHandler extends AVIMTypedMessageHandler { 17 | 18 | private Context context; 19 | 20 | public LCLKMessageHandler(Context context) { 21 | this.context = context.getApplicationContext(); 22 | } 23 | 24 | @Override 25 | public void onMessage(AVIMTypedMessage message, AVIMConversation conversation, AVIMClient client) { 26 | if (message == null || message.getMessageId() == null) { 27 | return; 28 | } 29 | 30 | if (LCLiveKit.getInstance().getCurrentUserId() == null) { 31 | client.close(null); 32 | } else { 33 | if (!client.getClientId().equals(LCLiveKit.getInstance().getCurrentUserId())) { 34 | client.close(null); 35 | } else { 36 | if (!message.getFrom().equals(client.getClientId())) { 37 | sendEvent(message, conversation); 38 | } 39 | } 40 | } 41 | } 42 | 43 | @Override 44 | public void onMessageReceipt(AVIMTypedMessage message, AVIMConversation conversation, AVIMClient client) { 45 | super.onMessageReceipt(message, conversation, client); 46 | } 47 | 48 | /** 49 | * 发送消息到来的通知事件 50 | * 51 | * @param message 52 | * @param conversation 53 | */ 54 | private void sendEvent(AVIMTypedMessage message, AVIMConversation conversation) { 55 | LCLKIMMessageEvent event = new LCLKIMMessageEvent(); 56 | event.message = message; 57 | event.conversation = conversation; 58 | EventBus.getDefault().post(event); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKChatAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.ViewGroup; 5 | 6 | import com.avos.avoscloud.im.v2.AVIMMessage; 7 | import com.avos.avoscloud.im.v2.AVIMTypedMessage; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by wli on 16/7/22. 14 | */ 15 | public class LCLKChatAdapter extends RecyclerView.Adapter { 16 | 17 | private final int CHAT_ITEM_IM_TYPE = 100; 18 | private final int CHAT_ITEM_STATUS_TYPE = 200; 19 | 20 | int maxMessageCount = 120; 21 | int removeCountTime = 50; 22 | 23 | protected List messageList = new ArrayList(); 24 | 25 | public LCLKChatAdapter() { 26 | super(); 27 | } 28 | 29 | /** 30 | * 添加多条消息记录 31 | * 32 | * @param messages 33 | */ 34 | public void addDataList(List messages) { 35 | messageList.addAll(messages); 36 | if (messageList.size() > maxMessageCount) { 37 | messageList = messageList.subList(removeCountTime, messageList.size()); 38 | } 39 | } 40 | 41 | @Override 42 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 43 | switch (viewType) { 44 | case CHAT_ITEM_IM_TYPE: 45 | return new LCLKChatItemTextHolder(parent.getContext(), parent); 46 | case CHAT_ITEM_STATUS_TYPE: 47 | return new LCLKChatItemStatusHolder(parent.getContext(), parent); 48 | } 49 | return new LCLKChatItemStatusHolder(parent.getContext(), parent); 50 | } 51 | 52 | @Override 53 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 54 | ((LCLKCommonViewHolder) holder).bindData(messageList.get(position)); 55 | } 56 | 57 | @Override 58 | public int getItemViewType(int position) { 59 | AVIMMessage message = messageList.get(position); 60 | if (null != message && message instanceof LCLKIMStatusMessage) { 61 | return CHAT_ITEM_STATUS_TYPE; 62 | } 63 | return CHAT_ITEM_IM_TYPE; 64 | } 65 | 66 | @Override 67 | public int getItemCount() { 68 | return messageList.size(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKChatItemStatusHolder.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.content.Context; 4 | import android.view.ViewGroup; 5 | import android.widget.TextView; 6 | import cn.leancloud.leancloudlivekit.R; 7 | 8 | /** 9 | * Created by wli on 16/8/18. 10 | */ 11 | public class LCLKChatItemStatusHolder extends LCLKCommonViewHolder { 12 | 13 | private TextView contentView; 14 | 15 | public LCLKChatItemStatusHolder(Context context, ViewGroup root) { 16 | super(context, root, R.layout.lclk_im_chat_status_item); 17 | contentView = (TextView) itemView.findViewById(R.id.lclk_im_status_item_view); 18 | } 19 | 20 | public static ViewHolderCreator HOLDER_CREATOR = new ViewHolderCreator() { 21 | @Override 22 | public LCLKChatItemStatusHolder createByViewGroupAndType(ViewGroup parent, int viewType) { 23 | return new LCLKChatItemStatusHolder(parent.getContext(), parent); 24 | } 25 | }; 26 | 27 | @Override 28 | public void bindData(LCLKIMStatusMessage statusMessage) { 29 | contentView.setText(statusMessage.getStatusContent()); 30 | } 31 | } -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKChatItemTextHolder.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | 9 | import com.squareup.picasso.Picasso; 10 | 11 | import cn.leancloud.leancloudlivekit.R; 12 | 13 | /** 14 | * Created by wli on 16/8/4. 15 | */ 16 | public class LCLKChatItemTextHolder extends LCLKCommonViewHolder { 17 | 18 | private ImageView avatarView; 19 | private TextView nameView; 20 | private TextView contentView; 21 | 22 | public LCLKChatItemTextHolder(Context context, ViewGroup root) { 23 | super(context, root, R.layout.lclk_im_chat_item_text_layout); 24 | initView(); 25 | } 26 | 27 | public void initView() { 28 | avatarView = (ImageView) itemView.findViewById(R.id.im_chat_item_text_avatar_view); 29 | contentView = (TextView) itemView.findViewById(R.id.im_chat_item_text_content_view); 30 | nameView = (TextView) itemView.findViewById(R.id.im_chat_item_text_name_view); 31 | } 32 | 33 | public static ViewHolderCreator HOLDER_CREATOR = new ViewHolderCreator() { 34 | @Override 35 | public LCLKChatItemTextHolder createByViewGroupAndType(ViewGroup parent, int viewType) { 36 | return new LCLKChatItemTextHolder(parent.getContext(), parent); 37 | } 38 | }; 39 | 40 | @Override 41 | public void bindData(LCLKIMMessage LCLKIMMessage) { 42 | if (null != LCLKIMMessage) { 43 | if (!TextUtils.isEmpty(LCLKIMMessage.getName())) { 44 | nameView.setText(LCLKIMMessage.getName()); 45 | } 46 | if (!TextUtils.isEmpty(LCLKIMMessage.getAvatar())) { 47 | Picasso.with(getContext()).load(LCLKIMMessage.getAvatar()).placeholder(R.mipmap.lclk_default_avatar_icon).into(avatarView); 48 | } 49 | contentView.setText(LCLKIMMessage.getMessageContent()); 50 | } else { 51 | nameView.setText(""); 52 | avatarView.setImageResource(R.mipmap.lclk_default_avatar_icon); 53 | contentView.setText(""); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKCommonListAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.ViewGroup; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by wli on 16/8/4. 12 | */ 13 | public class LCLKCommonListAdapter extends RecyclerView.Adapter { 14 | 15 | private static HashMap creatorHashMap = new HashMap<>(); 16 | 17 | private Class vhClass; 18 | 19 | protected List dataList = new ArrayList(); 20 | 21 | public LCLKCommonListAdapter() { 22 | super(); 23 | } 24 | 25 | public LCLKCommonListAdapter(Class vhClass) { 26 | this.vhClass = vhClass; 27 | } 28 | 29 | /** 30 | * 获取该 Adapter 中存的数据 31 | * 32 | * @return 33 | */ 34 | public List getDataList() { 35 | return dataList; 36 | } 37 | 38 | /** 39 | * 设置数据,会清空以前数据 40 | * 41 | * @param datas 42 | */ 43 | public void setDataList(List datas) { 44 | dataList.clear(); 45 | if (null != datas) { 46 | dataList.addAll(datas); 47 | } 48 | } 49 | 50 | /** 51 | * 添加数据,默认在最后插入,以前数据保留 52 | * 53 | * @param datas 54 | */ 55 | public void addDataList(List datas) { 56 | dataList.addAll(datas); 57 | } 58 | 59 | @Override 60 | public LCLKCommonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 61 | if (null == vhClass) { 62 | try { 63 | throw new IllegalArgumentException("please use CommonListAdapter(Class vhClass)"); 64 | } catch (Exception e) { 65 | } 66 | } 67 | 68 | LCLKCommonViewHolder.ViewHolderCreator creator = null; 69 | if (creatorHashMap.containsKey(vhClass.getName())) { 70 | creator = creatorHashMap.get(vhClass.getName()); 71 | } else { 72 | try { 73 | creator = (LCLKCommonViewHolder.ViewHolderCreator) vhClass.getField("HOLDER_CREATOR").get(null); 74 | creatorHashMap.put(vhClass.getName(), creator); 75 | } catch (IllegalAccessException e) { 76 | } catch (NoSuchFieldException e) { 77 | } 78 | } 79 | if (null != creator) { 80 | return creator.createByViewGroupAndType(parent, viewType); 81 | } else { 82 | throw new IllegalArgumentException(vhClass.getName() + " HOLDER_CREATOR should be instantiated"); 83 | } 84 | } 85 | 86 | @Override 87 | public void onBindViewHolder(LCLKCommonViewHolder holder, int position) { 88 | if (position >= 0 && position < dataList.size()) { 89 | holder.bindData(dataList.get(position)); 90 | } 91 | } 92 | 93 | @Override 94 | public int getItemCount() { 95 | return dataList.size(); 96 | } 97 | } -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKCommonViewHolder.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.ViewGroup; 7 | 8 | /** 9 | * Created by wli on 16/8/4. 10 | */ 11 | public abstract class LCLKCommonViewHolder extends RecyclerView.ViewHolder { 12 | 13 | public LCLKCommonViewHolder(Context context, ViewGroup root, int layoutRes) { 14 | super(LayoutInflater.from(context).inflate(layoutRes, root, false)); 15 | } 16 | 17 | public Context getContext() { 18 | return itemView.getContext(); 19 | } 20 | 21 | /** 22 | * 用给定的 data 对 holder 的 view 进行赋值 23 | */ 24 | public abstract void bindData(T t); 25 | 26 | public void setData(T t) { 27 | bindData(t); 28 | } 29 | 30 | /** 31 | * 因为 CommonListAdapter 里边无法对于未知类型的 Class 进行实例化 32 | * 所以需要如果想用 CommonListAdapter,必须要在对应的 CommonViewHolder 实例化一个 HOLDER_CREATOR 33 | * 注意:public static ViewHolderCreator HOLDER_CREATOR,名字与修饰符都不能更改,否则有可能引发失败 34 | * 具体样例可参见 DiscoverItemHolder 35 | * 如果不使用 CommonListAdapter,则不需要实例化 ViewHolderCreator 36 | * 37 | * @param 38 | */ 39 | public interface ViewHolderCreator { 40 | public VH createByViewGroupAndType(ViewGroup parent, int viewType); 41 | } 42 | } -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKGiftDialogFragment.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.app.Dialog; 4 | import android.content.DialogInterface; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.ColorDrawable; 7 | import android.os.Bundle; 8 | import android.support.v4.app.DialogFragment; 9 | import android.support.v4.view.ViewPager; 10 | import android.support.v7.widget.GridLayout; 11 | import android.view.Gravity; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.Window; 15 | import android.view.WindowManager; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | import cn.leancloud.leancloudlivekit.R; 21 | 22 | /** 23 | * Created by wli on 16/7/22. 24 | */ 25 | public class LCLKGiftDialogFragment extends DialogFragment { 26 | 27 | GridLayout currentLayout = null; 28 | 29 | ViewPager viewPager; 30 | 31 | LCLKGiftPagerAdapter pagerAdapter; 32 | 33 | List giftViewList = new ArrayList<>(); 34 | 35 | DialogInterface.OnDismissListener onDismissListener; 36 | 37 | 38 | int pageWidth = 4; 39 | int pagerHeight = 2; 40 | 41 | @Override 42 | public void onDismiss(DialogInterface dialog) { 43 | super.onDismiss(dialog); 44 | if (null != onDismissListener) { 45 | onDismissListener.onDismiss(dialog); 46 | } 47 | } 48 | 49 | @Override 50 | public Dialog onCreateDialog(Bundle savedInstanceState) { 51 | // 使用不带Theme的构造器, 获得的dialog边框距离屏幕仍有几毫米的缝隙。 52 | Dialog dialog = new Dialog(getActivity(), R.style.LCLKBottomDialog); 53 | 54 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); // 设置Content前设定 55 | dialog.setContentView(R.layout.lclk_gift_dialog); 56 | dialog.setCanceledOnTouchOutside(true); // 外部点击取消 57 | dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 58 | 59 | viewPager = (ViewPager) dialog.findViewById(R.id.lift_gift_dialog_pager); 60 | 61 | pagerAdapter = new LCLKGiftPagerAdapter(); 62 | viewPager.setAdapter(pagerAdapter); 63 | 64 | // 设置宽度为屏宽, 靠近屏幕底部。 65 | Window window = dialog.getWindow(); 66 | window.getWindowManager().getDefaultDisplay().getWidth(); 67 | WindowManager.LayoutParams lp = window.getAttributes(); 68 | lp.gravity = Gravity.BOTTOM; // 紧贴底部 69 | lp.width = WindowManager.LayoutParams.MATCH_PARENT; // 宽度持平 70 | window.setAttributes(lp); 71 | 72 | initGridView(); 73 | return dialog; 74 | } 75 | 76 | public void setViews(List viewList) { 77 | giftViewList.clear(); 78 | if (null != viewList) { 79 | giftViewList.addAll(viewList); 80 | } 81 | initGridView(); 82 | } 83 | 84 | public void setOnDismissListener(final DialogInterface.OnDismissListener listener) { 85 | onDismissListener = listener; 86 | } 87 | 88 | private void initGridView() { 89 | if (null != pagerAdapter) { 90 | final int screenWidth = getResources().getDisplayMetrics().widthPixels; 91 | final int dialogHeight = getResources().getDimensionPixelOffset(R.dimen.lclk_live_gift_dialog_height); 92 | if(currentLayout != null) { 93 | currentLayout.removeAllViews(); 94 | currentLayout = null; 95 | } 96 | for (View view : giftViewList) { 97 | if (null == currentLayout || currentLayout.getChildCount() >= pageWidth * pagerHeight) { 98 | currentLayout = new GridLayout(view.getContext()); 99 | currentLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 100 | currentLayout.setColumnCount(pageWidth); 101 | currentLayout.setRowCount(pagerHeight); 102 | currentLayout.setUseDefaultMargins(true); 103 | pagerAdapter.addView(currentLayout); 104 | } 105 | view.setLayoutParams(new ViewGroup.LayoutParams(screenWidth / pageWidth, dialogHeight / pagerHeight)); 106 | currentLayout.addView(view); 107 | } 108 | pagerAdapter.notifyDataSetChanged(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKGiftItem.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | /** 4 | * Created by wli on 16/9/12. 5 | */ 6 | class LCLKGiftItem { 7 | int giftResource; 8 | String giftName; 9 | int giftMessageIndex; 10 | 11 | public LCLKGiftItem(int giftMessageIndex, String giftName, int giftResource) { 12 | this.giftMessageIndex = giftMessageIndex; 13 | this.giftName = giftName; 14 | this.giftResource = giftResource; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKGiftManager.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by wli on 16/9/12. 10 | */ 11 | public class LCLKGiftManager { 12 | 13 | private static LCLKGiftManager giftManager; 14 | 15 | private HashMap giftItemHashMap = new HashMap<>(); 16 | 17 | public synchronized static LCLKGiftManager getInstance() { 18 | if (null == giftManager) { 19 | giftManager = new LCLKGiftManager(); 20 | } 21 | return giftManager; 22 | } 23 | 24 | private LCLKGiftManager() { 25 | } 26 | 27 | public List getGiftList() { 28 | ArrayList giftItemList = new ArrayList<>(); 29 | giftItemList.addAll(giftItemHashMap.values()); 30 | return giftItemList; 31 | } 32 | 33 | public void addGiftItem(LCLKGiftItem giftItem) { 34 | giftItemHashMap.put(giftItem.giftMessageIndex, giftItem); 35 | } 36 | 37 | public LCLKGiftItem getGiftItem(int messageIdex) { 38 | if (giftItemHashMap.containsKey(messageIdex)) { 39 | return giftItemHashMap.get(messageIdex); 40 | } 41 | return null; 42 | } 43 | 44 | public void clearGiftList() { 45 | giftItemHashMap.clear(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKGiftMessage.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import com.avos.avoscloud.im.v2.AVIMMessageCreator; 4 | import com.avos.avoscloud.im.v2.AVIMMessageField; 5 | import com.avos.avoscloud.im.v2.AVIMMessageType; 6 | 7 | /** 8 | * Created by wli on 16/7/26. 9 | */ 10 | @AVIMMessageType(type = LCLKIMMessage.LIVE_IM_GIFT_MESSAGE_TYPE) 11 | public class LCLKGiftMessage extends LCLKIMMessage { 12 | 13 | public static final String LIVE_GIFT_MESSAGE_NUMBER_KEY = "number"; 14 | public static final String LIVE_GIFT_MESSAGE_INDEX_KEY = "index"; 15 | 16 | @AVIMMessageField(name = LIVE_GIFT_MESSAGE_NUMBER_KEY) 17 | private int number; 18 | 19 | @AVIMMessageField(name = LIVE_GIFT_MESSAGE_INDEX_KEY) 20 | private int giftIndex; 21 | 22 | public LCLKGiftMessage() { 23 | super(); 24 | } 25 | 26 | public static final Creator CREATOR = new AVIMMessageCreator(LCLKGiftMessage.class); 27 | 28 | public int getNumber() { 29 | return number; 30 | } 31 | 32 | public void setNumber(int number) { 33 | this.number = number; 34 | } 35 | 36 | 37 | public int getGiftIndex() { 38 | return giftIndex; 39 | } 40 | 41 | public void setGiftIndex(int giftIndex) { 42 | this.giftIndex = giftIndex; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKGiftPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.support.v4.view.PagerAdapter; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import com.avos.avoscloud.im.v2.AVIMClient; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | 13 | /** 14 | * Created by wli on 16/8/3. 15 | */ 16 | public class LCLKGiftPagerAdapter extends PagerAdapter { 17 | 18 | protected List viewList = new ArrayList(); 19 | 20 | public LCLKGiftPagerAdapter() { 21 | } 22 | 23 | public void setViewList(List views) { 24 | viewList.clear(); 25 | if (null != views && views.size() > 0) { 26 | viewList.addAll(views); 27 | } 28 | } 29 | 30 | public void addView(View view) { 31 | if (null != view) { 32 | viewList.add(view); 33 | } 34 | } 35 | 36 | @Override 37 | public int getCount() { 38 | return viewList.size(); 39 | } 40 | 41 | @Override 42 | public boolean isViewFromObject(View view, Object object) { 43 | return view == object; 44 | } 45 | 46 | @Override 47 | public Object instantiateItem(ViewGroup container, int position) { 48 | if (position >=0 && position < viewList.size()) { 49 | View view = viewList.get(position); 50 | container.addView(view); 51 | return view; 52 | } 53 | return null; 54 | } 55 | 56 | @Override 57 | public void destroyItem(ViewGroup container, int position, Object object) { 58 | container.removeView(viewList.get(position)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKIMBarrageMessage.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import com.avos.avoscloud.im.v2.AVIMMessageCreator; 4 | import com.avos.avoscloud.im.v2.AVIMMessageType; 5 | 6 | /** 7 | * Created by wli on 16/7/26. 8 | */ 9 | @AVIMMessageType(type = LCLKIMMessage.LIVE_IM_BARRAGE_MESSAGE_TYPE) 10 | public class LCLKIMBarrageMessage extends LCLKIMMessage { 11 | 12 | public LCLKIMBarrageMessage() { 13 | super(); 14 | } 15 | 16 | public static final Creator CREATOR = new AVIMMessageCreator(LCLKIMBarrageMessage.class); 17 | } 18 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKIMFragment.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.content.DialogInterface; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.support.v7.widget.SwitchCompat; 10 | import android.text.Editable; 11 | import android.text.TextUtils; 12 | import android.text.TextWatcher; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.view.WindowManager; 17 | import android.widget.EditText; 18 | import android.widget.ImageView; 19 | import android.widget.LinearLayout; 20 | import android.widget.TextView; 21 | 22 | import com.avos.avoscloud.AVCallback; 23 | import com.avos.avoscloud.AVException; 24 | import com.avos.avoscloud.im.v2.AVIMConversation; 25 | import com.avos.avoscloud.im.v2.AVIMException; 26 | import com.avos.avoscloud.im.v2.AVIMTypedMessage; 27 | import com.avos.avoscloud.im.v2.callback.AVIMConversationCallback; 28 | import com.squareup.picasso.Picasso; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Arrays; 32 | import java.util.List; 33 | 34 | import cn.leancloud.leancloudlivekit.LCLiveKit; 35 | import cn.leancloud.leancloudlivekit.LCLKUser; 36 | import cn.leancloud.leancloudlivekit.R; 37 | import cn.leancloud.leancloudlivekit.barrage.LCLKBarrageLayout; 38 | import cn.leancloud.leancloudlivekit.utils.LCLKConstants; 39 | import de.greenrobot.event.EventBus; 40 | 41 | /** 42 | * Created by wli on 16/8/4. 43 | */ 44 | public class LCLKIMFragment extends Fragment { 45 | 46 | /** 47 | * recyclerView 对应的 Adapter 48 | */ 49 | protected LCLKChatAdapter itemAdapter; 50 | protected LinearLayoutManager layoutManager; 51 | protected AVIMConversation imConversation; 52 | 53 | ImageView avatarView; 54 | TextView nameView; 55 | ImageView closeView; 56 | LCLKBarrageLayout giftLayout; 57 | LCLKBarrageLayout barrageLayout; 58 | EditText inputView; 59 | TextView sendButton; 60 | TextView giftView; 61 | LinearLayout inputLayout; 62 | SwitchCompat barrageSwitch; 63 | RecyclerView recyclerView; 64 | TextView newMessageTipView; 65 | LCLKGiftDialogFragment giftDialogFragment; 66 | 67 | private boolean isBottom = true; 68 | 69 | 70 | @Nullable 71 | @Override 72 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 73 | View view = inflater.inflate(R.layout.lclk_im_fragment, container, false); 74 | initView(view); 75 | EventBus.getDefault().register(this); 76 | getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); 77 | return view; 78 | } 79 | 80 | private void initView(View view) { 81 | avatarView = (ImageView) view.findViewById(R.id.lclk_live_im_personal_avatar_view); 82 | nameView = (TextView) view.findViewById(R.id.lclk_live_im_personal_name_view); 83 | closeView = (ImageView) view.findViewById(R.id.lclk_live_im_personal_close_view); 84 | giftLayout = (LCLKBarrageLayout) view.findViewById(R.id.live_im_gift_barrage_view); 85 | barrageLayout = (LCLKBarrageLayout) view.findViewById(R.id.live_im_barrage_barrage_view); 86 | inputView = (EditText) view.findViewById(R.id.live_im_input_input_view); 87 | sendButton = (TextView) view.findViewById(R.id.live_im_input_send_view); 88 | giftView = (TextView) view.findViewById(R.id.live_im_input_gift_view); 89 | inputLayout = (LinearLayout) view.findViewById(R.id.live_im_input_layout); 90 | barrageSwitch = (SwitchCompat) view.findViewById(R.id.live_im_input_barrage_switch); 91 | recyclerView = (RecyclerView) view.findViewById(R.id.fragment_chat_rv_chat); 92 | newMessageTipView = (TextView) view.findViewById(R.id.fragment_chat_new_message_tip_view); 93 | 94 | sendButton.setOnClickListener(new View.OnClickListener() { 95 | @Override 96 | public void onClick(View v) { 97 | onSendClick(); 98 | } 99 | }); 100 | 101 | giftView.setOnClickListener(new View.OnClickListener() { 102 | @Override 103 | public void onClick(View v) { 104 | onGiftClick(); 105 | } 106 | }); 107 | 108 | newMessageTipView.setOnClickListener(new View.OnClickListener() { 109 | @Override 110 | public void onClick(View v) { 111 | onNewMessageClick(); 112 | } 113 | }); 114 | 115 | closeView.setOnClickListener(new View.OnClickListener() { 116 | @Override 117 | public void onClick(View v) { 118 | onCloseClick(); 119 | } 120 | }); 121 | 122 | initGift(); 123 | initGiftDialog(); 124 | } 125 | 126 | @Override 127 | public void onDestroyView() { 128 | EventBus.getDefault().unregister(this); 129 | super.onDestroyView(); 130 | } 131 | 132 | @Override 133 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 134 | super.onViewCreated(view, savedInstanceState); 135 | 136 | initAnchorInfo(); 137 | initIMView(); 138 | 139 | inputView.addTextChangedListener(new TextWatcher() { 140 | @Override 141 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 142 | } 143 | 144 | @Override 145 | public void onTextChanged(CharSequence s, int start, int before, int count) { 146 | if (TextUtils.isEmpty(s)) { 147 | barrageSwitch.setVisibility(View.INVISIBLE); 148 | giftView.setVisibility(View.VISIBLE); 149 | } else { 150 | barrageSwitch.setVisibility(View.VISIBLE); 151 | giftView.setVisibility(View.INVISIBLE); 152 | } 153 | } 154 | 155 | @Override 156 | public void afterTextChanged(Editable s) { 157 | } 158 | }); 159 | } 160 | 161 | private void initGift() { 162 | LCLKGiftManager giftManager = LCLKGiftManager.getInstance(); 163 | giftManager.clearGiftList(); 164 | giftManager.addGiftItem(new LCLKGiftItem(1, "", R.mipmap.lclk_im_gift_1)); 165 | giftManager.addGiftItem(new LCLKGiftItem(2, "", R.mipmap.lclk_im_gift_2)); 166 | giftManager.addGiftItem(new LCLKGiftItem(3, "", R.mipmap.lclk_im_gift_3)); 167 | giftManager.addGiftItem(new LCLKGiftItem(4, "", R.mipmap.lclk_im_gift_4)); 168 | giftManager.addGiftItem(new LCLKGiftItem(5, "", R.mipmap.lclk_im_gift_5)); 169 | giftManager.addGiftItem(new LCLKGiftItem(6, "", R.mipmap.lclk_im_gift_6)); 170 | giftManager.addGiftItem(new LCLKGiftItem(7, "", R.mipmap.lclk_im_gift_7)); 171 | giftManager.addGiftItem(new LCLKGiftItem(8, "", R.mipmap.lclk_im_gift_8)); 172 | } 173 | 174 | private void initGiftDialog() { 175 | giftDialogFragment = new LCLKGiftDialogFragment(); 176 | giftDialogFragment.setOnDismissListener(new DialogInterface.OnDismissListener() { 177 | @Override 178 | public void onDismiss(DialogInterface dialog) { 179 | inputLayout.setVisibility(View.VISIBLE); 180 | } 181 | }); 182 | 183 | LCLKGiftManager giftManager = LCLKGiftManager.getInstance(); 184 | List giftItemList = giftManager.getGiftList(); 185 | List viewList = new ArrayList<>(); 186 | for (LCLKGiftItem giftItem : giftItemList) { 187 | viewList.add(getGiftItem(giftItem)); 188 | } 189 | giftDialogFragment.setViews(viewList); 190 | } 191 | 192 | public void setConversation(final AVIMConversation conversation) { 193 | imConversation = conversation; 194 | imConversation.join(new AVIMConversationCallback() { 195 | @Override 196 | public void done(AVIMException e) { 197 | if (null == e) { 198 | sendJoinMessage(); 199 | } 200 | } 201 | }); 202 | } 203 | 204 | private void sendJoinMessage() { 205 | String clientId = LCLiveKit.getInstance().getCurrentUserId(); 206 | 207 | LCLiveKit.getInstance().getProfileProvider().fetchProfiles(Arrays.asList(clientId), new AVCallback>() { 208 | @Override 209 | protected void internalDone0(List lclkUsers, AVException e) { 210 | String userName = ""; 211 | if (null != lclkUsers && lclkUsers.size() > 0) { 212 | userName = lclkUsers.get(0).getUserName(); 213 | } 214 | if (TextUtils.isEmpty(userName)) { 215 | userName = "游客"; 216 | } 217 | LCLKIMStatusMessage message = new LCLKIMStatusMessage(); 218 | message.setStatusContent(userName + "来了"); 219 | imConversation.sendMessage(message, null); 220 | } 221 | }); 222 | } 223 | 224 | private void initAnchorInfo() { 225 | String anchorId = getActivity().getIntent().getStringExtra(LCLKConstants.ANCHOR_ID); 226 | LCLiveKit.getInstance().getProfileProvider().fetchProfiles(Arrays.asList(anchorId), new AVCallback>() { 227 | @Override 228 | public void internalDone0(List userList, AVException exception) { 229 | if (null == exception && null != userList && userList.size() > 0) { 230 | final String avatar = userList.get(0).getAvatarUrl(); 231 | if (!TextUtils.isEmpty(avatar)) { 232 | Picasso.with(getContext()).load(avatar).into(avatarView); 233 | } 234 | nameView.setText(userList.get(0).getUserName()); 235 | } 236 | } 237 | }); 238 | } 239 | 240 | private void initIMView() { 241 | layoutManager = new LinearLayoutManager(getContext()); 242 | recyclerView.setLayoutManager(layoutManager); 243 | 244 | itemAdapter = new LCLKChatAdapter(); 245 | recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 246 | @Override 247 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 248 | super.onScrollStateChanged(recyclerView, newState); 249 | } 250 | 251 | @Override 252 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 253 | super.onScrolled(recyclerView, dx, dy); 254 | if (null != layoutManager && layoutManager.getItemCount() > 0) { 255 | isBottom = (layoutManager.getItemCount() == layoutManager.findLastVisibleItemPosition() + 1); 256 | if (isBottom) { 257 | newMessageTipView.setVisibility(View.GONE); 258 | } 259 | } 260 | } 261 | }); 262 | recyclerView.setAdapter(itemAdapter); 263 | } 264 | 265 | private void onSendClick() { 266 | if (barrageSwitch.isChecked()) { 267 | sendBarrage(inputView.getText().toString()); 268 | } else { 269 | sendText(inputView.getText().toString()); 270 | } 271 | } 272 | 273 | private void onGiftClick() { 274 | inputLayout.setVisibility(View.INVISIBLE); 275 | giftDialogFragment.show(getFragmentManager(), "dialog"); 276 | } 277 | 278 | private View getGiftItem(final LCLKGiftItem giftItem) { 279 | View giftItemView = getActivity().getLayoutInflater().inflate(R.layout.lclk_gift_item, null); 280 | ImageView iconView = (ImageView) giftItemView.findViewById(R.id.live_gift_item_icon_view); 281 | TextView nameView = (TextView) giftItemView.findViewById(R.id.live_gift_item_name_view); 282 | iconView.setImageResource(giftItem.giftResource); 283 | nameView.setText(giftItem.giftName); 284 | giftItemView.setOnClickListener(new View.OnClickListener() { 285 | @Override 286 | public void onClick(View v) { 287 | sendGift(giftItem); 288 | } 289 | }); 290 | return giftItemView; 291 | } 292 | 293 | private void onNewMessageClick() { 294 | scrollToBottom(); 295 | } 296 | 297 | /** 298 | * 处理推送过来的消息 299 | * 同理,避免无效消息,此处加了 conversation id 判断 300 | */ 301 | public void onEvent(LCLKIMMessageEvent messageEvent) { 302 | if (null != imConversation && null != messageEvent && 303 | imConversation.getConversationId().equals(messageEvent.conversation.getConversationId())) { 304 | if (messageEvent.message instanceof LCLKIMBarrageMessage) { 305 | addBarrage((LCLKIMBarrageMessage) messageEvent.message); 306 | } else if (messageEvent.message instanceof LCLKGiftMessage) { 307 | addGiftBarrage((LCLKGiftMessage) messageEvent.message); 308 | } else if (messageEvent.message instanceof LCLKIMMessage 309 | || messageEvent.message instanceof LCLKIMStatusMessage) { 310 | itemAdapter.addDataList(Arrays.asList(messageEvent.message)); 311 | itemAdapter.notifyDataSetChanged(); 312 | if (isBottom) { 313 | scrollToBottom(); 314 | } else { 315 | newMessageTipView.setVisibility(View.VISIBLE); 316 | } 317 | } 318 | } 319 | } 320 | 321 | /** 322 | * 滚动 recyclerView 到底部 323 | */ 324 | private void scrollToBottom() { 325 | layoutManager.scrollToPositionWithOffset(itemAdapter.getItemCount() - 1, 0); 326 | } 327 | 328 | private void sendGift(final LCLKGiftItem giftItem) { 329 | LCLiveKit.getInstance().getProfileProvider().fetchProfiles( 330 | Arrays.asList(LCLiveKit.getInstance().getCurrentUserId()), new AVCallback>() { 331 | @Override 332 | protected void internalDone0(List lclkUsers, AVException e) { 333 | if (null == e && null != lclkUsers && lclkUsers.size() > 0) { 334 | final LCLKGiftMessage message = new LCLKGiftMessage(); 335 | message.setMessageContent(giftItem.giftName); 336 | message.setName(lclkUsers.get(0).getUserName()); 337 | message.setAvatar(lclkUsers.get(0).getAvatarUrl()); 338 | message.setGiftIndex(giftItem.giftMessageIndex); 339 | 340 | imConversation.sendMessage(message, new AVIMConversationCallback() { 341 | @Override 342 | public void done(AVIMException e) { 343 | addGiftBarrage(message); 344 | } 345 | }); 346 | } 347 | } 348 | }); 349 | } 350 | 351 | private void sendBarrage(final String content) { 352 | LCLiveKit.getInstance().getProfileProvider().fetchProfiles( 353 | Arrays.asList(LCLiveKit.getInstance().getCurrentUserId()), new AVCallback>() { 354 | @Override 355 | protected void internalDone0(List lclkUsers, AVException e) { 356 | if (null == e && null != lclkUsers && lclkUsers.size() > 0) { 357 | final LCLKIMBarrageMessage message = new LCLKIMBarrageMessage(); 358 | message.setMessageContent(content); 359 | message.setName(lclkUsers.get(0).getUserName()); 360 | message.setAvatar(lclkUsers.get(0).getAvatarUrl()); 361 | imConversation.sendMessage(message, new AVIMConversationCallback() { 362 | @Override 363 | public void done(AVIMException e) { 364 | addBarrage(message); 365 | } 366 | }); 367 | } 368 | } 369 | }); 370 | } 371 | 372 | private void addBarrage(LCLKIMBarrageMessage message) { 373 | View view = getActivity().getLayoutInflater().inflate(R.layout.lclk_barrage_item, null); 374 | ImageView avatarView = (ImageView) view.findViewById(R.id.live_barrage_item_avatar_view); 375 | TextView nameView = (TextView) view.findViewById(R.id.live_barrage_item_name_view); 376 | TextView contentView = (TextView) view.findViewById(R.id.live_barrage_item_content_view); 377 | 378 | Picasso.with(getContext()).load(message.getAvatar()).into(avatarView); 379 | nameView.setText(message.getName()); 380 | contentView.setText(message.getMessageContent()); 381 | barrageLayout.sendView(view); 382 | } 383 | 384 | private void addGiftBarrage(LCLKGiftMessage message) { 385 | View view = getActivity().getLayoutInflater().inflate(R.layout.lclk_barrage_gift_item, null); 386 | ImageView avatarView = (ImageView) view.findViewById(R.id.live_barrage_gift_icon_view); 387 | TextView nameView = (TextView) view.findViewById(R.id.live_barrage_gift_name_view); 388 | TextView numberView = (TextView) view.findViewById(R.id.live_barrage_gift_number_view); 389 | 390 | avatarView.setImageResource(LCLKGiftManager.getInstance().getGiftItem(message.getGiftIndex()).giftResource); 391 | nameView.setText(message.getName()); 392 | if (message.getNumber() > 0) { 393 | numberView.setText(message.getNumber() + ""); 394 | numberView.setVisibility(View.VISIBLE); 395 | } 396 | giftLayout.sendView(view); 397 | } 398 | 399 | /** 400 | * 发送文本消息 401 | * 402 | * @param content 403 | */ 404 | protected void sendText(final String content) { 405 | LCLiveKit.getInstance().getProfileProvider().fetchProfiles( 406 | Arrays.asList(LCLiveKit.getInstance().getCurrentUserId()), new AVCallback>() { 407 | @Override 408 | protected void internalDone0(List lclkUsers, AVException e) { 409 | if (null == e && null != lclkUsers && lclkUsers.size() > 0) { 410 | LCLKIMMessage message = new LCLKIMMessage(); 411 | message.setMessageContent(content); 412 | message.setName(lclkUsers.get(0).getUserName()); 413 | message.setAvatar(lclkUsers.get(0).getAvatarUrl()); 414 | itemAdapter.addDataList(Arrays.asList((AVIMTypedMessage) message)); 415 | itemAdapter.notifyDataSetChanged(); 416 | scrollToBottom(); 417 | imConversation.sendMessage(message, new AVIMConversationCallback() { 418 | @Override 419 | public void done(AVIMException e) { 420 | itemAdapter.notifyDataSetChanged(); 421 | } 422 | }); 423 | } 424 | } 425 | }); 426 | } 427 | 428 | private void onCloseClick() { 429 | getActivity().finish(); 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKIMMessage.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import com.avos.avoscloud.im.v2.AVIMMessageCreator; 4 | import com.avos.avoscloud.im.v2.AVIMMessageField; 5 | import com.avos.avoscloud.im.v2.AVIMMessageType; 6 | import com.avos.avoscloud.im.v2.AVIMTypedMessage; 7 | 8 | /** 9 | * Created by wli on 16/7/22. 10 | */ 11 | @AVIMMessageType(type = LCLKIMMessage.LIVE_IM_TEXT_MESSAGE_TYPE) 12 | public class LCLKIMMessage extends AVIMTypedMessage { 13 | 14 | public static final int LIVE_IM_TEXT_MESSAGE_TYPE = 5001; 15 | public static final int LIVE_IM_STATUS_MESSAGE_TYPE = 5002; 16 | public static final int LIVE_IM_GIFT_MESSAGE_TYPE = 5003; 17 | public static final int LIVE_IM_BARRAGE_MESSAGE_TYPE = 5004; 18 | 19 | public static final String LIVE_MESSAGE_AVATAR_KEY = "avatar"; 20 | public static final String LIVE_MESSAGE_NAME_KEY = "name"; 21 | public static final String LIVE_MESSAGE_CONTENT_KEY = "message_content"; 22 | 23 | @AVIMMessageField(name = LIVE_MESSAGE_AVATAR_KEY) 24 | private String avatar; 25 | 26 | @AVIMMessageField(name = LIVE_MESSAGE_NAME_KEY) 27 | private String name; 28 | 29 | @AVIMMessageField(name = LIVE_MESSAGE_CONTENT_KEY) 30 | private String messageContent; 31 | 32 | public static final Creator CREATOR = new AVIMMessageCreator(LCLKIMMessage.class); 33 | 34 | public String getAvatar() { 35 | return avatar; 36 | } 37 | 38 | public void setAvatar(String avatar) { 39 | this.avatar = avatar; 40 | } 41 | 42 | public String getName() { 43 | return name; 44 | } 45 | 46 | public void setName(String name) { 47 | this.name = name; 48 | } 49 | 50 | public String getMessageContent() { 51 | return messageContent; 52 | } 53 | 54 | public void setMessageContent(String messageContent) { 55 | this.messageContent = messageContent; 56 | } 57 | } -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKIMMessageEvent.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import com.avos.avoscloud.im.v2.AVIMConversation; 4 | import com.avos.avoscloud.im.v2.AVIMTypedMessage; 5 | 6 | /** 7 | * Created by wli on 16/8/4. 8 | */ 9 | public class LCLKIMMessageEvent { 10 | public AVIMTypedMessage message; 11 | public AVIMConversation conversation; 12 | } 13 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKIMStatusMessage.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import com.avos.avoscloud.im.v2.AVIMMessageCreator; 4 | import com.avos.avoscloud.im.v2.AVIMMessageField; 5 | import com.avos.avoscloud.im.v2.AVIMMessageType; 6 | import com.avos.avoscloud.im.v2.AVIMTypedMessage; 7 | 8 | /** 9 | * Created by wli on 16/8/18. 10 | */ 11 | @AVIMMessageType(type = LCLKIMMessage.LIVE_IM_STATUS_MESSAGE_TYPE) 12 | public class LCLKIMStatusMessage extends AVIMTypedMessage { 13 | 14 | public static final String LIVE_IM_STASUT_MESSAGE_KEY = "status_content"; 15 | 16 | @AVIMMessageField(name = LIVE_IM_STASUT_MESSAGE_KEY) 17 | private String statusContent; 18 | 19 | public static final Creator CREATOR = new AVIMMessageCreator(LCLKIMMessage.class); 20 | 21 | public String getStatusContent() { 22 | return statusContent; 23 | } 24 | 25 | public void setStatusContent(String statusContent) { 26 | this.statusContent = statusContent; 27 | } 28 | } -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/im/LCLKRoundCornerImageView.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.im; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Path; 6 | import android.graphics.RectF; 7 | import android.util.AttributeSet; 8 | import android.widget.ImageView; 9 | 10 | /** 11 | * Created by wli on 16/7/26. 12 | */ 13 | public class LCLKRoundCornerImageView extends ImageView { 14 | public LCLKRoundCornerImageView(Context context) { 15 | super(context); 16 | } 17 | public LCLKRoundCornerImageView(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | public LCLKRoundCornerImageView(Context context, AttributeSet attrs, int defStyle) { 21 | super(context, attrs, defStyle); 22 | } 23 | @Override 24 | protected void onDraw(Canvas canvas) { 25 | Path clipPath = new Path(); 26 | int w = this.getWidth(); 27 | int h = this.getHeight(); 28 | clipPath.addRoundRect(new RectF(0, 0, w, h), w/2, h/2, Path.Direction.CW); 29 | canvas.clipPath(clipPath); 30 | super.onDraw(canvas); 31 | } 32 | } -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/play/LCLKPlayActivity.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.play; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.text.TextUtils; 7 | 8 | import com.avos.avoscloud.AVCallback; 9 | import com.avos.avoscloud.AVException; 10 | import com.avos.avoscloud.im.v2.AVIMClient; 11 | import com.avos.avoscloud.im.v2.AVIMConversation; 12 | import com.avos.avoscloud.im.v2.AVIMConversationQuery; 13 | import com.avos.avoscloud.im.v2.AVIMException; 14 | import com.avos.avoscloud.im.v2.callback.AVIMConversationQueryCallback; 15 | 16 | import java.util.List; 17 | 18 | import cn.leancloud.leancloudlivekit.LCLiveKit; 19 | import cn.leancloud.leancloudlivekit.LCLiveKitProvider; 20 | import cn.leancloud.leancloudlivekit.R; 21 | import cn.leancloud.leancloudlivekit.im.LCLKIMFragment; 22 | import cn.leancloud.leancloudlivekit.utils.LCLKConstants; 23 | 24 | /** 25 | * Created by wli on 16/8/5. 26 | */ 27 | public class LCLKPlayActivity extends AppCompatActivity { 28 | 29 | LCLKIMFragment lclkimFragment; 30 | LCLKPlayFragment lclkPlayFragment; 31 | 32 | @Override 33 | protected void onCreate(@Nullable Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.lclk_play_activity); 36 | 37 | lclkimFragment = (LCLKIMFragment) getSupportFragmentManager().findFragmentById(R.id.lclk_im_fragment); 38 | lclkPlayFragment = (LCLKPlayFragment) getSupportFragmentManager().findFragmentById(R.id.lclk_play_fragment); 39 | 40 | final String liveId = getIntent().getStringExtra(LCLKConstants.LIVE_ID); 41 | initConversation(liveId); 42 | initPlayFragment(liveId); 43 | } 44 | 45 | public void initPlayFragment(final String liveId) { 46 | LCLiveKitProvider provider = LCLiveKit.getInstance().getProfileProvider(); 47 | if (null != provider) { 48 | provider.fetchPlayStream(liveId, new AVCallback() { 49 | @Override 50 | protected void internalDone0(String s, AVException e) { 51 | if (null == e && !TextUtils.isEmpty(s)) { 52 | lclkPlayFragment.startLive(s); 53 | } 54 | } 55 | }); 56 | } 57 | } 58 | 59 | private void initConversation(String liveId) { 60 | AVIMClient client = LCLiveKit.getInstance().getClient(); 61 | AVIMConversationQuery conversationQuery = client.getQuery(); 62 | conversationQuery.whereEqualTo("name", liveId); 63 | conversationQuery.findInBackground(new AVIMConversationQueryCallback() { 64 | @Override 65 | public void done(List list, AVIMException e) { 66 | if (null != list && list.size() > 0) { 67 | lclkimFragment.setConversation(list.get(0)); 68 | } 69 | } 70 | }); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/play/LCLKPlayFragment.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.play; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.app.Fragment; 6 | import android.text.TextUtils; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.view.WindowManager; 11 | import android.widget.LinearLayout; 12 | import android.widget.Toast; 13 | 14 | import com.avos.avoscloud.AVCallback; 15 | import com.avos.avoscloud.AVException; 16 | import com.pili.pldroid.player.AVOptions; 17 | import com.pili.pldroid.player.PLMediaPlayer; 18 | import com.pili.pldroid.player.widget.PLVideoTextureView; 19 | import com.pili.pldroid.player.widget.PLVideoView; 20 | 21 | import cn.leancloud.leancloudlivekit.LCLiveKit; 22 | import cn.leancloud.leancloudlivekit.LCLiveKitProvider; 23 | import cn.leancloud.leancloudlivekit.R; 24 | 25 | /** 26 | * Created by wli on 16/7/26. 27 | */ 28 | public class LCLKPlayFragment extends Fragment { 29 | 30 | PLVideoTextureView videoTextureView; 31 | 32 | LinearLayout loadingLayout; 33 | 34 | private String livePath; 35 | 36 | @Nullable 37 | @Override 38 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 39 | return inflater.inflate(R.layout.lclk_play_fragment, container, false); 40 | } 41 | 42 | 43 | @Override 44 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 45 | super.onViewCreated(view, savedInstanceState); 46 | videoTextureView = (PLVideoTextureView) view.findViewById(R.id.live_play_video_textrue_view); 47 | loadingLayout = (LinearLayout) view.findViewById(R.id.live_play_loading_layout); 48 | 49 | getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 50 | videoTextureView.setBufferingIndicator(loadingLayout); 51 | startLive(livePath); 52 | } 53 | 54 | public void startLive(String livePath) { 55 | this.livePath = livePath; 56 | if (!TextUtils.isEmpty(livePath) && null != videoTextureView) { 57 | AVOptions options = new AVOptions(); 58 | 59 | // the unit of timeout is ms 60 | options.setInteger(AVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000); 61 | options.setInteger(AVOptions.KEY_GET_AV_FRAME_TIMEOUT, 10 * 1000); 62 | options.setInteger(AVOptions.KEY_LIVE_STREAMING, 1); 63 | options.setInteger(AVOptions.KEY_DELAY_OPTIMIZATION, 1); 64 | options.setInteger(AVOptions.KEY_MEDIACODEC, 0); 65 | options.setInteger(AVOptions.KEY_START_ON_PREPARED, 0); 66 | videoTextureView.setAVOptions(options); 67 | videoTextureView.setDisplayAspectRatio(PLVideoView.ASPECT_RATIO_PAVED_PARENT); 68 | videoTextureView.setKeepScreenOn(true); 69 | videoTextureView.setOnCompletionListener(mOnCompletionListener); 70 | videoTextureView.setOnErrorListener(mOnErrorListener); 71 | videoTextureView.setVideoPath(livePath); 72 | videoTextureView.start(); 73 | } 74 | } 75 | 76 | private PLMediaPlayer.OnErrorListener mOnErrorListener = new PLMediaPlayer.OnErrorListener() { 77 | @Override 78 | public boolean onError(PLMediaPlayer mp, int errorCode) { 79 | switch (errorCode) { 80 | case PLMediaPlayer.ERROR_CODE_INVALID_URI: 81 | showToastTips("Invalid URL !"); 82 | break; 83 | case PLMediaPlayer.ERROR_CODE_404_NOT_FOUND: 84 | showToastTips("404 resource not found !"); 85 | break; 86 | case PLMediaPlayer.ERROR_CODE_CONNECTION_REFUSED: 87 | showToastTips("Connection refused !"); 88 | break; 89 | case PLMediaPlayer.ERROR_CODE_CONNECTION_TIMEOUT: 90 | showToastTips("Connection timeout !"); 91 | break; 92 | case PLMediaPlayer.ERROR_CODE_EMPTY_PLAYLIST: 93 | showToastTips("Empty playlist !"); 94 | break; 95 | case PLMediaPlayer.ERROR_CODE_STREAM_DISCONNECTED: 96 | showToastTips("Stream disconnected !"); 97 | break; 98 | case PLMediaPlayer.ERROR_CODE_IO_ERROR: 99 | showToastTips("Network IO Error !"); 100 | break; 101 | case PLMediaPlayer.ERROR_CODE_UNAUTHORIZED: 102 | showToastTips("Unauthorized Error !"); 103 | break; 104 | case PLMediaPlayer.ERROR_CODE_PREPARE_TIMEOUT: 105 | showToastTips("Prepare timeout !"); 106 | break; 107 | case PLMediaPlayer.ERROR_CODE_READ_FRAME_TIMEOUT: 108 | showToastTips("Read frame timeout !"); 109 | break; 110 | case PLMediaPlayer.MEDIA_ERROR_UNKNOWN: 111 | default: 112 | showToastTips("unknown error !"); 113 | break; 114 | } 115 | // getActivity().finish(); 116 | return true; 117 | } 118 | }; 119 | 120 | private PLMediaPlayer.OnCompletionListener mOnCompletionListener = new PLMediaPlayer.OnCompletionListener() { 121 | @Override 122 | public void onCompletion(PLMediaPlayer plMediaPlayer) { 123 | showToastTips("Play Completed !"); 124 | // getActivity().finish(); 125 | } 126 | }; 127 | 128 | private void showToastTips(final String tips) { 129 | getActivity().runOnUiThread(new Runnable() { 130 | @Override 131 | public void run() { 132 | Toast.makeText(getActivity(), tips, Toast.LENGTH_SHORT).show(); 133 | } 134 | }); 135 | } 136 | 137 | @Override 138 | public void onResume() { 139 | super.onResume(); 140 | videoTextureView.start(); 141 | } 142 | 143 | @Override 144 | public void onPause() { 145 | super.onPause(); 146 | videoTextureView.pause(); 147 | } 148 | 149 | @Override 150 | public void onDestroyView() { 151 | super.onDestroyView(); 152 | videoTextureView.stopPlayback(); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/record/LCLKRecordActivity.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.record; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.text.TextUtils; 8 | 9 | import com.avos.avoscloud.AVCallback; 10 | import com.avos.avoscloud.AVException; 11 | import com.avos.avoscloud.im.v2.AVIMClient; 12 | import com.avos.avoscloud.im.v2.AVIMConversation; 13 | import com.avos.avoscloud.im.v2.AVIMConversationQuery; 14 | import com.avos.avoscloud.im.v2.AVIMException; 15 | import com.avos.avoscloud.im.v2.callback.AVIMConversationCreatedCallback; 16 | import com.avos.avoscloud.im.v2.callback.AVIMConversationQueryCallback; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import cn.leancloud.leancloudlivekit.LCLiveKit; 22 | import cn.leancloud.leancloudlivekit.LCLiveKitProvider; 23 | import cn.leancloud.leancloudlivekit.R; 24 | import cn.leancloud.leancloudlivekit.im.LCLKIMFragment; 25 | import cn.leancloud.leancloudlivekit.utils.LCLKConstants; 26 | 27 | /** 28 | * Created by wli on 16/8/2. 29 | * 负责录制直播的页面 30 | * 只需要开发者传入 LCLKConstants.LIVE_ID 就可以 31 | */ 32 | public class LCLKRecordActivity extends AppCompatActivity { 33 | 34 | LCLKIMFragment lclkimFragment; 35 | LCLKRecordFragment lclkRecordFragment; 36 | 37 | @Override 38 | protected void onCreate(@Nullable Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.lclk_record_activity); 41 | lclkimFragment = (LCLKIMFragment) getSupportFragmentManager().findFragmentById(R.id.lclk_im_fragment); 42 | lclkRecordFragment = (LCLKRecordFragment) getSupportFragmentManager().findFragmentById(R.id.lclk_record_fragment); 43 | 44 | final String liveId = getIntent().getStringExtra(LCLKConstants.LIVE_ID); 45 | 46 | initConversation(liveId); 47 | initRecordFragment(liveId); 48 | } 49 | 50 | /** 51 | * 初始化录制直播的 Fragment 52 | * @param liveId 53 | */ 54 | private void initRecordFragment(String liveId) { 55 | LCLiveKitProvider provider = LCLiveKit.getInstance().getProfileProvider(); 56 | if (null != provider) { 57 | provider.fetchRecordStream(liveId, new AVCallback() { 58 | @Override 59 | protected void internalDone0(String s, AVException e) { 60 | if (null == e && !TextUtils.isEmpty(s)) { 61 | lclkRecordFragment.setStream(s); 62 | } 63 | } 64 | }); 65 | } 66 | } 67 | 68 | /** 69 | * 初始化实时通讯的 Fragment 70 | * @param liveId 71 | */ 72 | private void initConversation(final String liveId) { 73 | final AVIMClient client = LCLiveKit.getInstance().getClient(); 74 | AVIMConversationQuery conversationQuery = client.getQuery(); 75 | conversationQuery.whereEqualTo("name", liveId); 76 | conversationQuery.findInBackground(new AVIMConversationQueryCallback() { 77 | @Override 78 | public void done(List list, AVIMException e) { 79 | if (null != list && list.size() > 0) { 80 | lclkimFragment.setConversation(list.get(0)); 81 | } else { 82 | client.createConversation(new ArrayList(), liveId, null, false, true, new AVIMConversationCreatedCallback() { 83 | @Override 84 | public void done(AVIMConversation avimConversation, AVIMException e) { 85 | lclkimFragment.setConversation(avimConversation); 86 | } 87 | }); 88 | } 89 | } 90 | }); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/record/LCLKRecordFragment.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.record; 2 | 3 | import android.opengl.GLSurfaceView; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.text.TextUtils; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | 12 | import com.pili.pldroid.streaming.CameraStreamingManager; 13 | import com.pili.pldroid.streaming.CameraStreamingSetting; 14 | import com.pili.pldroid.streaming.StreamingProfile; 15 | import com.pili.pldroid.streaming.widget.AspectFrameLayout; 16 | 17 | import org.json.JSONException; 18 | import org.json.JSONObject; 19 | 20 | import cn.leancloud.leancloudlivekit.R; 21 | 22 | /** 23 | * Created by wli on 16/8/4. 24 | * 负责录制直播的 Fragment 25 | */ 26 | public class LCLKRecordFragment extends Fragment implements CameraStreamingManager.StreamingStateListener { 27 | 28 | private CameraStreamingManager mediaStreamingManager; 29 | private CameraStreamingSetting cameraStreamingSetting; 30 | private View mView; 31 | 32 | private String recordStream; 33 | 34 | @Override 35 | public void onResume() { 36 | super.onResume(); 37 | if (null != mediaStreamingManager) { 38 | mediaStreamingManager.resume(); 39 | } 40 | } 41 | 42 | @Override 43 | public void onPause() { 44 | super.onPause(); 45 | if (null != mediaStreamingManager) { 46 | mediaStreamingManager.pause(); 47 | } 48 | } 49 | 50 | @Override 51 | public void onDestroy() { 52 | super.onDestroy(); 53 | if (null != mediaStreamingManager) { 54 | mediaStreamingManager.destroy(); 55 | } 56 | } 57 | 58 | @Override 59 | public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { 60 | mView = inflater.inflate(R.layout.lclk_record_fragment, parent, false); 61 | return mView; 62 | } 63 | 64 | @Override 65 | public void onActivityCreated(Bundle savedInstanceState) { 66 | super.onActivityCreated(savedInstanceState); 67 | 68 | AspectFrameLayout aspectFrameLayout = (AspectFrameLayout) mView.findViewById(R.id.live_recore_aspect_layout); 69 | GLSurfaceView glSurfaceView = (GLSurfaceView) mView.findViewById(R.id.live_recore_camera_preview_view); 70 | aspectFrameLayout.setShowMode(AspectFrameLayout.SHOW_MODE.REAL); 71 | 72 | mediaStreamingManager = new CameraStreamingManager(getActivity(), aspectFrameLayout, glSurfaceView, 73 | CameraStreamingManager.EncodingType.SW_VIDEO_WITH_SW_AUDIO_CODEC); 74 | mediaStreamingManager.setStreamingStateListener(this); 75 | cameraStreamingSetting = getCameraSetting(); 76 | setStream(recordStream); 77 | } 78 | 79 | @Override 80 | public void onStateChanged(int state, Object info) { 81 | switch (state) { 82 | case CameraStreamingManager.STATE.PREPARING: 83 | break; 84 | case CameraStreamingManager.STATE.READY: 85 | new Thread(new Runnable() { 86 | @Override 87 | public void run() { 88 | if (mediaStreamingManager != null) { 89 | mediaStreamingManager.startStreaming(); 90 | } 91 | } 92 | }).start(); 93 | break; 94 | case CameraStreamingManager.STATE.CONNECTING: 95 | break; 96 | case CameraStreamingManager.STATE.STREAMING: 97 | // The av packet had been sent. 98 | break; 99 | case CameraStreamingManager.STATE.SHUTDOWN: 100 | // The streaming had been finished. 101 | break; 102 | case CameraStreamingManager.STATE.IOERROR: 103 | // Network connect error. 104 | break; 105 | case CameraStreamingManager.STATE.SENDING_BUFFER_EMPTY: 106 | break; 107 | case CameraStreamingManager.STATE.SENDING_BUFFER_FULL: 108 | break; 109 | case CameraStreamingManager.STATE.AUDIO_RECORDING_FAIL: 110 | // Failed to record audio. 111 | break; 112 | case CameraStreamingManager.STATE.OPEN_CAMERA_FAIL: 113 | // Failed to open camera. 114 | break; 115 | case CameraStreamingManager.STATE.DISCONNECTED: 116 | // The socket is broken while streaming 117 | break; 118 | case CameraStreamingManager.STATE.INVALID_STREAMING_URL: 119 | // invalid streaming url 120 | break; 121 | } 122 | } 123 | 124 | public void setStream(String recordStream) { 125 | this.recordStream = recordStream; 126 | /** 127 | * add by zzhong 128 | * 必须先调用MediaStreamingManager.prepare 函数,不然调用MediaStreamingManager.resume() 129 | * 函数会报错,所以无论recordStream值是什么都应该调用initStream,注意空字符串即可。如果recordStream 130 | * 不符合规范,会在onStateChanged发送INVALID_STREAMING_URL消息。 131 | */ 132 | //if (!TextUtils.isEmpty(recordStream) && null != mediaStreamingManager) { 133 | initStream(recordStream == null ? "" : recordStream); 134 | startRecord(); 135 | //} 136 | } 137 | 138 | private void initStream(String recordStream) { 139 | StreamingProfile streamingProfile = getSreamingProfile(recordStream); 140 | mediaStreamingManager.prepare(cameraStreamingSetting, null, null, streamingProfile); 141 | } 142 | 143 | private CameraStreamingSetting getCameraSetting() { 144 | CameraStreamingSetting cameraStreamingSetting = new CameraStreamingSetting(); 145 | cameraStreamingSetting.setContinuousFocusModeEnabled(true) 146 | .setCameraFacingId(CameraStreamingSetting.CAMERA_FACING_ID.CAMERA_FACING_BACK) 147 | .setBuiltInFaceBeautyEnabled(true) 148 | .setResetTouchFocusDelayInMs(3000) 149 | .setCameraPrvSizeLevel(CameraStreamingSetting.PREVIEW_SIZE_LEVEL.SMALL) 150 | .setCameraPrvSizeRatio(CameraStreamingSetting.PREVIEW_SIZE_RATIO.RATIO_16_9) 151 | .setFaceBeautySetting(new CameraStreamingSetting.FaceBeautySetting(1.0f, 1.0f, 0.8f)) 152 | .setVideoFilter(CameraStreamingSetting.VIDEO_FILTER_TYPE.VIDEO_FILTER_BEAUTY); 153 | return cameraStreamingSetting; 154 | } 155 | 156 | private StreamingProfile getSreamingProfile(String recordStream) { 157 | StreamingProfile streamingProfile = new StreamingProfile(); 158 | try { 159 | streamingProfile.setVideoQuality(StreamingProfile.VIDEO_QUALITY_HIGH3); 160 | streamingProfile.setAudioQuality(StreamingProfile.AUDIO_QUALITY_MEDIUM2); 161 | streamingProfile.setEncodingSizeLevel(StreamingProfile.VIDEO_ENCODING_HEIGHT_480); 162 | streamingProfile.setEncoderRCMode(StreamingProfile.EncoderRCModes.QUALITY_PRIORITY); 163 | streamingProfile.setStreamStatusConfig(new StreamingProfile.StreamStatusConfig(3)); 164 | streamingProfile.setSendingBufferProfile(new StreamingProfile.SendingBufferProfile(0.2f, 0.8f, 3.0f, 20 * 1000)); 165 | streamingProfile.setStream(new StreamingProfile.Stream(new JSONObject(recordStream))); 166 | return streamingProfile; 167 | } catch (JSONException e) { 168 | e.printStackTrace(); 169 | } 170 | return null; 171 | } 172 | 173 | public void startRecord() { 174 | mediaStreamingManager.startStreaming(); 175 | } 176 | 177 | public void stopRecord() { 178 | mediaStreamingManager.stopStreaming(); 179 | } 180 | 181 | @Override 182 | public boolean onStateHandled(int i, Object o) { 183 | return false; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/utils/LCLKConstants.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.utils; 2 | 3 | /** 4 | * Created by wli on 16/8/4. 5 | * LeanCloudLiveKit 常量类 6 | */ 7 | public class LCLKConstants { 8 | 9 | /** 10 | * LeanCloudLiveKit 常量前缀,避免与用户其他常量混淆 11 | */ 12 | private static final String LEANMESSAGE_CONSTANTS_PREFIX = "cn.leancloud.livekit."; 13 | 14 | private static String getPrefixConstant(String str) { 15 | return LEANMESSAGE_CONSTANTS_PREFIX + str; 16 | } 17 | 18 | public static final String LIVE_RECORD_STREAM_KEY = getPrefixConstant("live_record_stream_key"); 19 | 20 | 21 | public static final String LIVE_CONVERSATION_KEY = getPrefixConstant("live_conversation_key"); 22 | 23 | public static final String LIVE_ID = getPrefixConstant("live_id"); 24 | public static final String ANCHOR_ID = getPrefixConstant("anchor_id"); 25 | } 26 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/java/cn/leancloud/leancloudlivekit/utils/LCLKProfileCache.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.leancloudlivekit.utils; 2 | 3 | import com.avos.avoscloud.AVCallback; 4 | import com.avos.avoscloud.AVException; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import cn.leancloud.leancloudlivekit.LCLKUser; 13 | import cn.leancloud.leancloudlivekit.LCLiveKit; 14 | import cn.leancloud.leancloudlivekit.LCLiveKitProvider; 15 | 16 | /** 17 | * Created by wli on 16/8/10. 18 | * 用户信息的缓存,仅仅缓存与内存中 19 | */ 20 | public class LCLKProfileCache { 21 | 22 | /** 23 | * userMap 最大的数量,避免数据过多导致 oom 24 | */ 25 | private final int PROFILE_CACHE_MAX_SIZE = 1000; 26 | 27 | private Map userMap; 28 | 29 | private LCLKProfileCache() { 30 | userMap = new HashMap<>(); 31 | } 32 | 33 | private static LCLKProfileCache profileCache; 34 | 35 | public static synchronized LCLKProfileCache getInstance() { 36 | if (null == profileCache) { 37 | profileCache = new LCLKProfileCache(); 38 | } 39 | return profileCache; 40 | } 41 | 42 | /** 43 | * 根据 id 获取用户信息 44 | * 先从缓存中获取,若没有再调用用户回调获取 45 | * 46 | * @param id 47 | * @param callback 48 | */ 49 | public synchronized void getCachedUser(final String id, final AVCallback callback) { 50 | getCachedUsers(Arrays.asList(id), new AVCallback>() { 51 | @Override 52 | protected void internalDone0(List lclkUsers, AVException e) { 53 | LCLKUser lclkUser = (null != lclkUsers && !lclkUsers.isEmpty() ? lclkUsers.get(0) : null); 54 | callback.internalDone(lclkUser, e); 55 | } 56 | }); 57 | } 58 | 59 | /** 60 | * 获取多个用户的信息 61 | * 先从缓存中获取,若没有再调用用户回调获取 62 | * 63 | * @param idList 64 | * @param callback 65 | */ 66 | public synchronized void getCachedUsers(List idList, final AVCallback> callback) { 67 | if (null != callback) { 68 | if (null == idList || idList.isEmpty()) { 69 | callback.internalDone(null, new AVException(new Throwable("idList is empty!"))); 70 | } else { 71 | final List profileList = new ArrayList(); 72 | final List unCachedIdList = new ArrayList(); 73 | 74 | for (String id : idList) { 75 | if (userMap.containsKey(id)) { 76 | profileList.add(userMap.get(id)); 77 | } else { 78 | unCachedIdList.add(id); 79 | } 80 | } 81 | 82 | if (unCachedIdList.isEmpty()) { 83 | callback.internalDone(profileList, null); 84 | } else { 85 | getProfilesFromProvider(unCachedIdList, profileList, callback); 86 | } 87 | } 88 | } 89 | } 90 | 91 | /** 92 | * 根据 id 通过开发者设置的回调获取用户信息 93 | * 94 | * @param idList 95 | * @param callback 96 | */ 97 | private void getProfilesFromProvider(List idList, final List profileList, 98 | final AVCallback> callback) { 99 | LCLiveKitProvider profileProvider = LCLiveKit.getInstance().getProfileProvider(); 100 | if (null != profileProvider) { 101 | profileProvider.fetchProfiles(idList, new AVCallback>() { 102 | @Override 103 | protected void internalDone0(List lclkUsers, AVException e) { 104 | if (null != lclkUsers) { 105 | for (LCLKUser userProfile : lclkUsers) { 106 | cacheUser(userProfile); 107 | } 108 | } 109 | profileList.addAll(lclkUsers); 110 | callback.internalDone(profileList, null != e ? new AVException(e) : null); 111 | } 112 | }); 113 | } else { 114 | callback.internalDone(null, new AVException(new Throwable("please setProfileProvider first!"))); 115 | } 116 | } 117 | 118 | /** 119 | * 内存中是否包相关 LCLKUser 的信息 120 | * @param id 121 | * @return 122 | */ 123 | public synchronized boolean hasCachedUser(String id) { 124 | return userMap.containsKey(id); 125 | } 126 | 127 | public synchronized void cacheUser(LCLKUser userProfile) { 128 | if (userMap.size() > PROFILE_CACHE_MAX_SIZE) { 129 | userMap.clear(); 130 | } 131 | userMap.put(userProfile.getUserId(), userProfile); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroid_mmprocessing.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroid_mmprocessing.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroid_streaming_aac_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroid_streaming_aac_encoder.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroid_streaming_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroid_streaming_core.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroid_streaming_h264_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroid_streaming_h264_encoder.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroidplayer.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/arm64-v8a/libpldroidplayer.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroid_mmprocessing.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroid_mmprocessing.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_aac_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_aac_encoder.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_core.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_h264_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_h264_encoder.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroidplayer.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi-v7a/libpldroidplayer.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi/libpldroid_mmprocessing.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi/libpldroid_mmprocessing.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi/libpldroid_streaming_aac_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi/libpldroid_streaming_aac_encoder.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi/libpldroid_streaming_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi/libpldroid_streaming_core.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi/libpldroid_streaming_h264_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi/libpldroid_streaming_h264_encoder.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/jniLibs/armeabi/libpldroidplayer.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/jniLibs/armeabi/libpldroidplayer.so -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/drawable/lclk_barrage_item_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/drawable/lclk_im_gift_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/drawable/lclk_im_input_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/drawable/lclk_im_item_content_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/drawable/lclk_im_item_content_bg.9.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/drawable/lclk_praise_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_barrage_gift_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 18 | 19 | 26 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_barrage_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 17 | 18 | 27 | 28 | 38 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_gift_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_gift_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 21 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_im_barrage_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_im_chat_item_text_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 23 | 24 | 31 | 32 | 40 | 41 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_im_chat_status_item.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_im_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_im_gift_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_im_im_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 25 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_im_input_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 14 | 27 | 28 | 36 | 37 | 38 | 48 | 49 | 59 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_im_personal_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 24 | 25 | 33 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_play_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 20 | 25 | 26 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_play_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 20 | 21 | 25 | 26 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_record_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 17 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/layout/lclk_record_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_camera_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_camera_switch.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_default_avatar_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_default_avatar_icon.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_close.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_1.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_2.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_3.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_4.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_5.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_6.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_7.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_im_gift_8.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/LeanCloudLiveKit-Android/87f221305384f4e895f22949186b245532ee76b1/leancloudlivekit/src/main/res/mipmap-xxhdpi/lclk_star.png -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/values/lclk_colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #121E34 5 | #121E34 6 | @color/lclk_common_blue 7 | 8 | #2c97e8 9 | #898989 10 | #ff0000 11 | 12 | #664AE2 13 | 14 | #D1D1D1 15 | @android:color/white 16 | #a8FFFFFF 17 | @color/lclk_color_primary 18 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/values/lclk_dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10dp 4 | 175dp 5 | 24dp 6 | 45dp 7 | 40dp 8 | 40dp 9 | 10 | 11 | 12 | 14sp 13 | 16sp 14 | 18sp 15 | 20sp 16 | 22sp 17 | 24sp 18 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/values/lclk_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 礼物 4 | 弹幕 5 | -------------------------------------------------------------------------------- /leancloudlivekit/src/main/res/values/lclk_styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 17 | 18 | 22 | 23 | 26 | 27 | 33 | 34 | 35 | 36 | 41 | 42 | 45 | 46 | 49 | 50 | 53 | 54 | 57 | 58 | 59 | 60 | 66 | 67 | 71 | 72 | 76 | -------------------------------------------------------------------------------- /livekitapplication/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /livekitapplication/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "26.0.2" 6 | 7 | defaultConfig { 8 | applicationId "cn.leancloud.livekitapplication" 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | multiDexEnabled true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | lintOptions { 23 | abortOnError false 24 | } 25 | } 26 | 27 | dependencies { 28 | compile project(':leancloudlivekit') 29 | implementation 'com.jakewharton:butterknife:8.8.1' 30 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 31 | } 32 | -------------------------------------------------------------------------------- /livekitapplication/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/wli/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /livekitapplication/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /livekitapplication/src/main/java/cn/leancloud/livekitapplication/LCLKAppProvider.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.livekitapplication; 2 | 3 | import com.avos.avoscloud.AVCallback; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import cn.leancloud.leancloudlivekit.LCLiveKitProvider; 9 | import cn.leancloud.leancloudlivekit.LCLKUser; 10 | 11 | /** 12 | * Created by wli on 16/8/5. 13 | */ 14 | public class LCLKAppProvider implements LCLiveKitProvider { 15 | 16 | private static LCLKAppProvider customUserProvider; 17 | 18 | public synchronized static LCLKAppProvider getInstance() { 19 | if (null == customUserProvider) { 20 | customUserProvider = new LCLKAppProvider(); 21 | } 22 | return customUserProvider; 23 | } 24 | 25 | private LCLKAppProvider() { 26 | } 27 | 28 | private static List partUsers = new ArrayList(); 29 | 30 | // 此数据均为 fake,仅供参考 31 | static { 32 | partUsers.add(new LCLKUser("Tom", "Tom", "http://www.avatarsdb.com/avatars/tom_and_jerry2.jpg")); 33 | partUsers.add(new LCLKUser("Jerry", "Jerry", "http://www.avatarsdb.com/avatars/jerry.jpg")); 34 | partUsers.add(new LCLKUser("Harry", "Harry", "http://www.avatarsdb.com/avatars/young_harry.jpg")); 35 | partUsers.add(new LCLKUser("William", "William", "http://www.avatarsdb.com/avatars/william_shakespeare.jpg")); 36 | partUsers.add(new LCLKUser("Bob", "Bob", "http://www.avatarsdb.com/avatars/bath_bob.jpg")); 37 | } 38 | 39 | @Override 40 | public void fetchProfiles(List userIdList, AVCallback> profilesCallBack) { 41 | List userList = new ArrayList(); 42 | for (String userId : userIdList) { 43 | for (LCLKUser user : partUsers) { 44 | if (user.getUserId().equals(userId)) { 45 | userList.add(user); 46 | break; 47 | } 48 | } 49 | } 50 | profilesCallBack.internalDone(userList, null); 51 | } 52 | 53 | @Override 54 | public void fetchRecordStream(String liveId, AVCallback streamCallback) { 55 | //TODO:根据liveId获取对应的推流地址 56 | streamCallback.internalDone(null, null); 57 | } 58 | 59 | @Override 60 | public void fetchPlayStream(String liveId, AVCallback streamCallback) { 61 | //TODO:根据liveId获取对应的直播地址 62 | streamCallback.internalDone("rtmp://live.hkstv.hk.lxdns.com/live/hks" , null); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /livekitapplication/src/main/java/cn/leancloud/livekitapplication/LCLKApplication.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.livekitapplication; 2 | 3 | import android.app.Application; 4 | 5 | import com.avos.avoscloud.AVOSCloud; 6 | import com.avos.avoscloud.AVObject; 7 | import com.pili.pldroid.streaming.StreamingEnv; 8 | 9 | import cn.leancloud.leancloudlivekit.LCLiveKit; 10 | 11 | /** 12 | * Created by wli on 16/8/5. 13 | */ 14 | public class LCLKApplication extends Application { 15 | 16 | // 此 id 与 key 仅供测试使用 17 | private final String APP_ID = "683jigxkqb10jrirelvd9vcn9ywbq2o436lfz1kngsvigm27"; 18 | private final String APP_KEY = "ualzl8f8pxmryous77m3gf2z0dyhrhk6xdb7zkiu6flc0jxy"; 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | LCLiveKit.getInstance().setProfileProvider(LCLKAppProvider.getInstance()); 24 | LCLiveKit.getInstance().init(getApplicationContext(), APP_ID, APP_KEY); 25 | AVOSCloud.setDebugLogEnabled(true); 26 | AVObject.registerSubclass(LCLiveRoom.class); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /livekitapplication/src/main/java/cn/leancloud/livekitapplication/LCLKLiveRoomCreateActivity.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.livekitapplication; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.database.Cursor; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.os.Environment; 10 | import android.provider.MediaStore; 11 | import android.support.annotation.Nullable; 12 | import android.support.v7.app.AlertDialog; 13 | import android.support.v7.app.AppCompatActivity; 14 | import android.text.TextUtils; 15 | import android.view.View; 16 | import android.widget.EditText; 17 | import android.widget.ImageView; 18 | import android.widget.TextView; 19 | import android.widget.Toast; 20 | 21 | import com.avos.avoscloud.AVException; 22 | import com.avos.avoscloud.AVFile; 23 | import com.avos.avoscloud.SaveCallback; 24 | import com.pili.pldroid.streaming.widget.AspectFrameLayout; 25 | import com.squareup.picasso.Picasso; 26 | 27 | import java.io.File; 28 | import java.io.FileNotFoundException; 29 | import java.util.UUID; 30 | 31 | import butterknife.BindView; 32 | import butterknife.ButterKnife; 33 | import butterknife.OnClick; 34 | import cn.leancloud.leancloudlivekit.LCLiveKit; 35 | import cn.leancloud.leancloudlivekit.record.LCLKRecordActivity; 36 | import cn.leancloud.leancloudlivekit.utils.LCLKConstants; 37 | 38 | /** 39 | * Created by wli on 16/8/8. 40 | * 创建直播间页面 41 | */ 42 | public class LCLKLiveRoomCreateActivity extends AppCompatActivity { 43 | 44 | @BindView(R.id.live_channle_start_et_title) 45 | EditText titleView; 46 | 47 | @BindView(R.id.live_channle_start_et_topic) 48 | EditText topicView; 49 | 50 | @BindView(R.id.live_channle_start_iv_conver) 51 | ImageView converView; 52 | 53 | @BindView(R.id.live_channle_start_tv_start) 54 | TextView startView; 55 | 56 | private String localCameraPath; 57 | 58 | @Override 59 | protected void onCreate(@Nullable Bundle savedInstanceState) { 60 | super.onCreate(savedInstanceState); 61 | setContentView(R.layout.lclk_live_room_create_activity); 62 | ButterKnife.bind(this); 63 | if (TextUtils.isEmpty(LCLiveKit.getInstance().getCurrentUserId())) { 64 | Toast.makeText(this, "请先执行登陆操作!", Toast.LENGTH_SHORT).show(); 65 | finish(); 66 | } 67 | } 68 | 69 | @OnClick(R.id.live_channle_start_iv_conver) 70 | public void onConverClick(View view) { 71 | final String[] Items = {"手机相册", "拍照"}; 72 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 73 | builder.setItems(Items, new DialogInterface.OnClickListener() { 74 | @Override 75 | public void onClick(DialogInterface dialogInterface, int i) { 76 | switch (i) { 77 | case 0: 78 | dispatchPickPictureIntent(); 79 | break; 80 | case 1: 81 | dispatchTakePictureIntent(); 82 | break; 83 | } 84 | } 85 | }); 86 | builder.setCancelable(true); 87 | AlertDialog dialog = builder.create(); 88 | dialog.show(); 89 | } 90 | 91 | @OnClick(R.id.live_channle_start_tv_start) 92 | public void onStartClick(View view) { 93 | final String title = titleView.getText().toString(); 94 | final String topic = topicView.getText().toString(); 95 | final String clientId = LCLiveKit.getInstance().getCurrentUserId(); 96 | //TODO:自己定义直播间ID,保证每个直播间ID唯一即可 97 | final String liveId = UUID.randomUUID().toString(); 98 | final String anchorId = clientId; 99 | 100 | if (TextUtils.isEmpty(title.trim())) { 101 | Toast.makeText(this, "标题不能为空", Toast.LENGTH_SHORT).show(); 102 | return; 103 | } 104 | 105 | if (TextUtils.isEmpty(topic.trim())) { 106 | Toast.makeText(this, "话题不能为空", Toast.LENGTH_SHORT).show(); 107 | return; 108 | } 109 | 110 | if (TextUtils.isEmpty(localCameraPath)) { 111 | Toast.makeText(this, "封面不能为空", Toast.LENGTH_SHORT).show(); 112 | return; 113 | } 114 | 115 | if (TextUtils.isEmpty(clientId.trim())) { 116 | Toast.makeText(this, "请重新登陆!", Toast.LENGTH_SHORT).show(); 117 | } 118 | 119 | LCLiveRoom liveRoom = new LCLiveRoom(); 120 | 121 | try { 122 | liveRoom.setConver(AVFile.withAbsoluteLocalPath("cover", localCameraPath)); 123 | } catch (FileNotFoundException e) { 124 | e.printStackTrace(); 125 | } 126 | liveRoom.setTopic(topic); 127 | liveRoom.setTitle(title); 128 | liveRoom.setLiveId(liveId); 129 | liveRoom.setAnchorId(anchorId); 130 | liveRoom.saveInBackground(new SaveCallback() { 131 | @Override 132 | public void done(AVException e) { 133 | finish(); 134 | Intent intent = new Intent(LCLKLiveRoomCreateActivity.this, LCLKRecordActivity.class); 135 | intent.putExtra(LCLKConstants.LIVE_ID, liveId); 136 | intent.putExtra(LCLKConstants.ANCHOR_ID, anchorId); 137 | startActivity(intent); 138 | } 139 | }); 140 | } 141 | 142 | private void dispatchTakePictureIntent() { 143 | localCameraPath = getPicturePathByCurrentTime(this); 144 | Intent takePictureIntent = new Intent("android.media.action.IMAGE_CAPTURE"); 145 | Uri imageUri = Uri.fromFile(new File(localCameraPath)); 146 | takePictureIntent.putExtra("return-data", false); 147 | takePictureIntent.putExtra("output", imageUri); 148 | if (takePictureIntent.resolveActivity(getPackageManager()) != null) { 149 | this.startActivityForResult(takePictureIntent, 1); 150 | } 151 | } 152 | 153 | private void dispatchPickPictureIntent() { 154 | Intent photoPickerIntent = new Intent("android.intent.action.PICK", (Uri) null); 155 | photoPickerIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); 156 | this.startActivityForResult(photoPickerIntent, 2); 157 | } 158 | 159 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 160 | super.onActivityResult(requestCode, resultCode, data); 161 | if (-1 == resultCode) { 162 | switch (requestCode) { 163 | case 1: 164 | Picasso.with(this).load(new File(localCameraPath)).into(converView); 165 | break; 166 | case 2: 167 | localCameraPath = getRealPathFromURI(this, data.getData()); 168 | Picasso.with(this).load(new File(localCameraPath)).into(converView); 169 | } 170 | } 171 | } 172 | 173 | private String getRealPathFromURI(Context context, Uri contentUri) { 174 | Cursor cursor = null; 175 | String var6; 176 | try { 177 | String[] proj = new String[]{"_data"}; 178 | cursor = context.getContentResolver().query(contentUri, proj, (String) null, (String[]) null, (String) null); 179 | int column_index = cursor.getColumnIndexOrThrow("_data"); 180 | cursor.moveToFirst(); 181 | var6 = cursor.getString(column_index); 182 | } finally { 183 | if (cursor != null) { 184 | cursor.close(); 185 | } 186 | } 187 | return var6; 188 | } 189 | 190 | private static String getPicturePathByCurrentTime(Context context) { 191 | String state = Environment.getExternalStorageState(); 192 | boolean isExternalStorageWritable = "mounted".equals(state); 193 | File fille = isExternalStorageWritable ? context.getExternalCacheDir() : context.getCacheDir(); 194 | return (new File(fille, "picture_" + System.currentTimeMillis())).getAbsolutePath(); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /livekitapplication/src/main/java/cn/leancloud/livekitapplication/LCLKLiveRoomItemClickEvent.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.livekitapplication; 2 | 3 | /** 4 | * Created by wli on 16/8/10. 5 | * 直播间 item 的点击事件 6 | */ 7 | public class LCLKLiveRoomItemClickEvent { 8 | public LCLiveRoom liveRoom; 9 | 10 | LCLKLiveRoomItemClickEvent(LCLiveRoom liveRoom) { 11 | this.liveRoom = liveRoom; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /livekitapplication/src/main/java/cn/leancloud/livekitapplication/LCLKLiveRoomItemHolder.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.livekitapplication; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | 9 | import com.avos.avoscloud.AVCallback; 10 | import com.avos.avoscloud.AVException; 11 | import com.squareup.picasso.Picasso; 12 | 13 | import cn.leancloud.leancloudlivekit.LCLKUser; 14 | import cn.leancloud.leancloudlivekit.im.LCLKCommonViewHolder; 15 | import cn.leancloud.leancloudlivekit.im.LCLKRoundCornerImageView; 16 | import cn.leancloud.leancloudlivekit.utils.LCLKProfileCache; 17 | import de.greenrobot.event.EventBus; 18 | 19 | /** 20 | * Created by wli on 16/8/5. 21 | */ 22 | public class LCLKLiveRoomItemHolder extends LCLKCommonViewHolder { 23 | 24 | private ImageView backgroudView; 25 | private TextView nameView; 26 | private LCLKRoundCornerImageView avatarView; 27 | private TextView statusView; 28 | private TextView descriptionView; 29 | 30 | private LCLiveRoom liveRoom; 31 | 32 | public LCLKLiveRoomItemHolder(Context context, ViewGroup root) { 33 | super(context, root, R.layout.lclk_live_room_item); 34 | backgroudView = (ImageView) itemView.findViewById(R.id.live_room_item_bg_view); 35 | nameView = (TextView) itemView.findViewById(R.id.live_room_item_name_view); 36 | avatarView = (LCLKRoundCornerImageView) itemView.findViewById(R.id.live_room_item_avatar_view); 37 | statusView = (TextView) itemView.findViewById(R.id.live_room_item_status_view); 38 | descriptionView = (TextView) itemView.findViewById(R.id.live_room_item_description_view); 39 | 40 | itemView.setOnClickListener(new View.OnClickListener() { 41 | @Override 42 | public void onClick(View v) { 43 | EventBus.getDefault().post(new LCLKLiveRoomItemClickEvent(liveRoom)); 44 | } 45 | }); 46 | } 47 | 48 | @Override 49 | public void bindData(LCLiveRoom liveRoom) { 50 | this.liveRoom = liveRoom; 51 | if(liveRoom.getConver() != null) { 52 | Picasso.with(getContext()).load(liveRoom.getConver().getUrl()).into(backgroudView); 53 | }else { 54 | Picasso.with(getContext()).load(R.mipmap.lclk_live_add_image).into(backgroudView); 55 | } 56 | LCLKProfileCache.getInstance().getCachedUser(liveRoom.getAnchorId(), new AVCallback() { 57 | @Override 58 | protected void internalDone0(LCLKUser lclkUser, AVException e) { 59 | if(lclkUser != null) { 60 | nameView.setText(lclkUser.getUserName()); 61 | Picasso.with(getContext()).load(lclkUser.getAvatarUrl()).into(avatarView); 62 | }else { 63 | nameView.setText(R.string.lclk_live_room_unknow_name); 64 | Picasso.with(getContext()).load(R.mipmap.ic_launcher).into(avatarView); 65 | } 66 | } 67 | }); 68 | descriptionView.setText(liveRoom.getTitle() + " #" + liveRoom.getTopic()); 69 | } 70 | 71 | public static ViewHolderCreator HOLDER_CREATOR = new ViewHolderCreator() { 72 | @Override 73 | public LCLKLiveRoomItemHolder createByViewGroupAndType(ViewGroup parent, int viewType) { 74 | return new LCLKLiveRoomItemHolder(parent.getContext(), parent); 75 | } 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /livekitapplication/src/main/java/cn/leancloud/livekitapplication/LCLKLiveRoomListActivity.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.livekitapplication; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.widget.SwipeRefreshLayout; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.GridLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | 13 | import com.avos.avoscloud.AVException; 14 | import com.avos.avoscloud.AVQuery; 15 | import com.avos.avoscloud.FindCallback; 16 | 17 | import java.util.List; 18 | 19 | import butterknife.BindView; 20 | import butterknife.ButterKnife; 21 | import cn.leancloud.leancloudlivekit.im.LCLKCommonListAdapter; 22 | import cn.leancloud.leancloudlivekit.play.LCLKPlayActivity; 23 | import cn.leancloud.leancloudlivekit.utils.LCLKConstants; 24 | import de.greenrobot.event.EventBus; 25 | 26 | /** 27 | * Created by wli on 16/8/5. 28 | * 直播间页面 29 | */ 30 | public class LCLKLiveRoomListActivity extends AppCompatActivity { 31 | 32 | @BindView(R.id.refreshable_list_srl) 33 | SwipeRefreshLayout refreshLayout; 34 | 35 | @BindView(R.id.refreshable_list_recycler) 36 | RecyclerView recyclerView; 37 | 38 | GridLayoutManager layoutManager; 39 | LCLKCommonListAdapter itemAdapter; 40 | 41 | @Override 42 | protected void onCreate(@Nullable Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.lclk_live_room_list_activity); 45 | EventBus.getDefault().register(this); 46 | ButterKnife.bind(this); 47 | 48 | layoutManager = new GridLayoutManager(this, 2); 49 | recyclerView.setLayoutManager(layoutManager); 50 | 51 | refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 52 | @Override 53 | public void onRefresh() { 54 | fetchChannels(); 55 | } 56 | }); 57 | 58 | itemAdapter = new LCLKCommonListAdapter<>(LCLKLiveRoomItemHolder.class); 59 | recyclerView.setAdapter(itemAdapter); 60 | 61 | fetchChannels(); 62 | } 63 | 64 | 65 | @Override 66 | public boolean onCreateOptionsMenu(Menu menu) { 67 | getMenuInflater().inflate(R.menu.lclk_menu_live_create, menu); 68 | return true; 69 | } 70 | 71 | @Override 72 | public boolean onOptionsItemSelected(MenuItem item) { 73 | int menuId = item.getItemId(); 74 | if (menuId == R.id.menu_channel_start) { 75 | gotoStartChannelAcitivty(); 76 | } 77 | return super.onOptionsItemSelected(item); 78 | } 79 | 80 | /** 81 | * 跳转到创建直播间的页面 82 | */ 83 | private void gotoStartChannelAcitivty() { 84 | Intent intent = new Intent(this, LCLKLiveRoomCreateActivity.class); 85 | startActivity(intent); 86 | } 87 | 88 | /** 89 | * 拉取直播间信息 90 | */ 91 | private void fetchChannels() { 92 | AVQuery avQuery = AVQuery.getQuery(LCLiveRoom.class); 93 | avQuery.findInBackground(new FindCallback() { 94 | @Override 95 | public void done(List list, AVException e) { 96 | refreshLayout.setRefreshing(false); 97 | itemAdapter.setDataList(list); 98 | itemAdapter.notifyDataSetChanged(); 99 | } 100 | }); 101 | } 102 | 103 | public void onEvent(LCLKLiveRoomItemClickEvent event) { 104 | Intent intent = new Intent(LCLKLiveRoomListActivity.this, LCLKPlayActivity.class); 105 | intent.putExtra(LCLKConstants.LIVE_ID, event.liveRoom.getLiveId()); 106 | intent.putExtra(LCLKConstants.ANCHOR_ID, event.liveRoom.getAnchorId()); 107 | startActivity(intent); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /livekitapplication/src/main/java/cn/leancloud/livekitapplication/LCLKLoginActivity.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.livekitapplication; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.text.TextUtils; 7 | import android.view.View; 8 | import android.widget.EditText; 9 | import android.widget.Toast; 10 | 11 | import com.avos.avoscloud.im.v2.AVIMClient; 12 | import com.avos.avoscloud.im.v2.AVIMException; 13 | import com.avos.avoscloud.im.v2.callback.AVIMClientCallback; 14 | 15 | import butterknife.BindView; 16 | import butterknife.ButterKnife; 17 | import butterknife.OnClick; 18 | import cn.leancloud.leancloudlivekit.LCLiveKit; 19 | 20 | /** 21 | * Created by wli on 16/8/5. 22 | * 登陆页面 23 | */ 24 | public class LCLKLoginActivity extends AppCompatActivity { 25 | 26 | @BindView(R.id.activity_login_et_username) 27 | EditText nameView; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.lclk_login_activity); 33 | ButterKnife.bind(this); 34 | } 35 | 36 | @OnClick(R.id.activity_login_btn_login) 37 | public void onLoginClick(View view) { 38 | String clientId = nameView.getText().toString(); 39 | if (TextUtils.isEmpty(clientId.trim())) { 40 | Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show(); 41 | return; 42 | } 43 | 44 | // 打开 LeanCloud 实时通讯 45 | LCLiveKit.getInstance().open(clientId, new AVIMClientCallback() { 46 | @Override 47 | public void done(AVIMClient avimClient, AVIMException e) { 48 | if (null == e) { 49 | finish(); 50 | Intent intent = new Intent(LCLKLoginActivity.this, LCLKLiveRoomListActivity.class); 51 | startActivity(intent); 52 | } else { 53 | Toast.makeText(LCLKLoginActivity.this, e.toString(), Toast.LENGTH_SHORT).show(); 54 | } 55 | } 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /livekitapplication/src/main/java/cn/leancloud/livekitapplication/LCLiveRoom.java: -------------------------------------------------------------------------------- 1 | package cn.leancloud.livekitapplication; 2 | 3 | import android.os.Parcel; 4 | 5 | import com.avos.avoscloud.AVClassName; 6 | import com.avos.avoscloud.AVFile; 7 | import com.avos.avoscloud.AVObject; 8 | 9 | /** 10 | * Created by wli on 16/8/10. 11 | * 直播间 12 | */ 13 | @AVClassName("live") 14 | public class LCLiveRoom extends AVObject { 15 | 16 | private final String LC_LIVEROOM_TOPIC = "topic"; 17 | private final String LC_LIVEROOM_TITLE = "title"; 18 | private final String LC_LIVEROOM_LIVEID = "liveId"; 19 | private final String LC_LIVEROOM_ANCHORID = "AnchorId"; 20 | private final String LC_LIVEROOM_COVER = "conver"; 21 | 22 | public LCLiveRoom(){ 23 | super(); 24 | } 25 | 26 | public LCLiveRoom(Parcel in){ 27 | super(in); 28 | } 29 | 30 | public void setTopic(String topic) { 31 | this.put(LC_LIVEROOM_TOPIC, topic); 32 | } 33 | 34 | public String getTopic() { 35 | return this.getString(LC_LIVEROOM_TOPIC); 36 | } 37 | 38 | public void setTitle(String title) { 39 | this.put(LC_LIVEROOM_TITLE, title); 40 | } 41 | 42 | public String getTitle() { 43 | return this.getString(LC_LIVEROOM_TITLE); 44 | } 45 | 46 | public void setLiveId(String liveId) { 47 | this.put(LC_LIVEROOM_LIVEID, liveId); 48 | } 49 | 50 | public String getLiveId() { 51 | return this.getString(LC_LIVEROOM_LIVEID); 52 | } 53 | 54 | public void setAnchorId(String anchorId) { 55 | this.put(LC_LIVEROOM_ANCHORID, anchorId); 56 | } 57 | 58 | public String getAnchorId() { 59 | return this.getString(LC_LIVEROOM_ANCHORID); 60 | } 61 | 62 | public void setConver(AVFile file) { 63 | this.put(LC_LIVEROOM_COVER, file); 64 | } 65 | 66 | public AVFile getConver() { 67 | return this.getAVFile(LC_LIVEROOM_COVER); 68 | } 69 | 70 | public static final Creator CREATOR = AVObjectCreator.instance; 71 | } 72 | -------------------------------------------------------------------------------- /livekitapplication/src/main/res/drawable/edittext_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /livekitapplication/src/main/res/drawable/start_channel_btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /livekitapplication/src/main/res/drawable/start_channel_image_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /livekitapplication/src/main/res/layout/lclk_live_room_create_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 27 | 28 | 39 | 40 | 51 | 52 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /livekitapplication/src/main/res/layout/lclk_live_room_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | 22 | 23 | 30 | 31 | 39 | 40 | 49 | 50 | 51 | 59 | -------------------------------------------------------------------------------- /livekitapplication/src/main/res/layout/lclk_live_room_list_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /livekitapplication/src/main/res/layout/lclk_login_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 25 | 26 |