Commit eac85ca3943385208f3b0ae72ae21e02179dbecd

Authored by 朱显松
1 parent 96ab3599

推流功能页面代码迁移

Showing 72 changed files with 1734 additions and 1 deletions
app/build.gradle
... ... @@ -25,5 +25,8 @@ android {
25 25 dependencies {
26 26 compile fileTree(dir: 'libs', include: ['*.jar'])
27 27 compile 'com.android.support:appcompat-v7:26.+'
  28 + compile 'com.android.support:recyclerview-v7:26.+'
  29 +
  30 + compile 'com.google.code.gson:gson:2.7'
28 31 compile 'com.android.support.constraint:constraint-layout:1.0.2'
29 32 }
... ...
app/libs/libcnlivelive.jar 0 → 100644
No preview for this file type
app/libs/libcnliveutil.jar 0 → 100644
No preview for this file type
app/src/main/AndroidManifest.xml
... ... @@ -2,6 +2,22 @@
2 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 3 package="com.cnlive.gundam">
4 4  
  5 + <uses-permission android:name="android.permission.GET_TASKS" />
  6 + <uses-permission android:name="android.permission.VIBRATE" />
  7 + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  8 + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  9 + <uses-permission android:name="android.permission.INTERNET" />
  10 + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  11 + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  12 + <uses-permission android:name="android.permission.RECORD_AUDIO" />
  13 + <uses-permission android:name="android.permission.CAMERA" />
  14 + <uses-permission android:name="android.permission.WAKE_LOCK" />
  15 + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  16 + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
  17 + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  18 + <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  19 + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  20 +
5 21 <application
6 22 android:allowBackup="true"
7 23 android:icon="@mipmap/ic_launcher"
... ... @@ -16,6 +32,14 @@
16 32 <category android:name="android.intent.category.LAUNCHER" />
17 33 </intent-filter>
18 34 </activity>
  35 +
  36 +
  37 + <activity
  38 + android:name=".stream.ui.activity.StreamActivity"
  39 + android:configChanges="orientation|keyboardHidden|screenSize"
  40 + android:launchMode="singleTop"
  41 + android:screenOrientation="portrait"
  42 + android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
19 43 </application>
20 44  
21 45 </manifest>
22 46 \ No newline at end of file
... ...
app/src/main/java/com/cnlive/gundam/main/ui/activity/MainActivity.java
1 1 package com.cnlive.gundam.main.ui.activity;
2 2  
  3 +import android.content.Intent;
3 4 import android.databinding.DataBindingUtil;
4 5 import android.os.Bundle;
5 6 import android.support.v7.app.AppCompatActivity;
... ... @@ -11,6 +12,7 @@ import com.cnlive.gundam.main.ui.fragment.HomeFragment;
11 12 import com.cnlive.gundam.main.ui.fragment.LiveFragment;
12 13 import com.cnlive.gundam.main.ui.fragment.UGCListFragment;
13 14 import com.cnlive.gundam.main.ui.widget.FragmentTabHost;
  15 +import com.cnlive.gundam.stream.ui.activity.StreamActivity;
14 16  
15 17 import java.util.Arrays;
16 18  
... ... @@ -39,9 +41,16 @@ public class MainActivity extends AppCompatActivity implements FragmentTabHost.O
39 41 public boolean onSetCurrentTab(String tag) {
40 42 switch (tag) {
41 43 case "stream":
42   - //TODO 启动直播推流页面0
  44 + Intent intent = new Intent(this, StreamActivity.class);
  45 + intent.putExtra("activityId", ""+System.currentTimeMillis());
  46 + intent.putExtra("channelId", "androidtest");
  47 + intent.putExtra("channelName", "androidTest");
  48 + intent.putExtra("coverImgUrl", "");
  49 + intent.putExtra("landscape", false);
  50 + startActivity(intent);
43 51 return true;
44 52 }
45 53 return false;
46 54 }
  55 +
47 56 }
48 57 \ No newline at end of file
... ...
app/src/main/java/com/cnlive/gundam/stream/model/ChatItem.java 0 → 100644
  1 +package com.cnlive.gundam.stream.model;
  2 +
  3 +/**
  4 + * Created by Lynn on 5/24/2017.
  5 + */
  6 +
  7 +public class ChatItem {
  8 + private String headUrl;
  9 + private String userName;
  10 + private String content;
  11 +
  12 + public ChatItem() {
  13 +
  14 + }
  15 +
  16 + public ChatItem(String headUrl, String userName, String content) {
  17 + this.headUrl = headUrl;
  18 + this.userName = userName;
  19 + this.content = content;
  20 + }
  21 +
  22 + public String getHeadUrl() {
  23 + return headUrl == null ? " " : headUrl;
  24 + }
  25 +
  26 + public void setHeadUrl(String headUrl) {
  27 + this.headUrl = headUrl;
  28 + }
  29 +
  30 + public String getUserName() {
  31 + return userName;
  32 + }
  33 +
  34 + public void setUserName(String userName) {
  35 + this.userName = userName;
  36 + }
  37 +
  38 + public String getContent() {
  39 + return content;
  40 + }
  41 +
  42 + public void setContent(String content) {
  43 + this.content = content;
  44 + }
  45 +
  46 + @Override
  47 + public String toString() {
  48 + return "ChatItem{" +
  49 + "headUrl='" + headUrl + '\'' +
  50 + ", userName='" + userName + '\'' +
  51 + ", content='" + content + '\'' +
  52 + '}';
  53 + }
  54 +}
... ...
app/src/main/java/com/cnlive/gundam/stream/model/ExtensionModel.java 0 → 100644
  1 +package com.cnlive.gundam.stream.model;
  2 +
  3 +/**
  4 + * Created by Lynn on 5/26/2017.
  5 + */
  6 +
  7 +public class ExtensionModel {
  8 + private String name;
  9 + private String iconUrl;
  10 +
  11 + public ExtensionModel() {
  12 + }
  13 +
  14 + public ExtensionModel(String name, String iconUrl) {
  15 + this.name = name;
  16 + this.iconUrl = iconUrl;
  17 + }
  18 +
  19 + public String getName() {
  20 + return name;
  21 + }
  22 +
  23 + public void setName(String name) {
  24 + this.name = name;
  25 + }
  26 +
  27 + public String getIconUrl() {
  28 + return iconUrl;
  29 + }
  30 +
  31 + public void setIconUrl(String iconUrl) {
  32 + this.iconUrl = iconUrl;
  33 + }
  34 +}
... ...
app/src/main/java/com/cnlive/gundam/stream/model/ShowView.java 0 → 100644
  1 +package com.cnlive.gundam.stream.model;
  2 +
  3 +/**
  4 + * Created by Lynn on 5/23/2017.
  5 + */
  6 +
  7 +public class ShowView {
  8 + //开始直播按钮
  9 + private boolean startBtn = true;
  10 + //结束页面
  11 + private boolean endLayout = false;
  12 + //重试页面
  13 + private boolean retryLayout = false;
  14 + //结束页面的提示文字(红色文字)
  15 + private boolean msgHint = false;
  16 + //自动重连提示文字(黄色文字)
  17 + private boolean autoRecMsg = false;
  18 + //推流美颜等按钮的显示
  19 + private boolean recordBtns = true;
  20 + //美颜按钮
  21 + private boolean beautifulBtn = false;
  22 + //闪光灯
  23 + private boolean flashBtn = false;
  24 + //聊天recyclerview
  25 + private boolean chatView = false;
  26 + //聊天室人数
  27 + private boolean viewers = false;
  28 +
  29 + //自动重连的提示文字
  30 + private boolean autoRectTxt;
  31 +
  32 + public ShowView() {
  33 + }
  34 +
  35 + public boolean isStartBtn() {
  36 + return startBtn;
  37 + }
  38 +
  39 + public void setStartBtn(boolean startBtn) {
  40 + this.startBtn = startBtn;
  41 + }
  42 +
  43 + public boolean isEndLayout() {
  44 + return endLayout;
  45 + }
  46 +
  47 + public void setEndLayout(boolean endLayout) {
  48 + this.endLayout = endLayout;
  49 + }
  50 +
  51 + public boolean isRetryLayout() {
  52 + return retryLayout;
  53 + }
  54 +
  55 + public void setRetryLayout(boolean retryLayout) {
  56 + this.retryLayout = retryLayout;
  57 + }
  58 +
  59 + public boolean isMsgHint() {
  60 + return msgHint;
  61 + }
  62 +
  63 + public void setMsgHint(boolean msgHint) {
  64 + this.msgHint = msgHint;
  65 + }
  66 +
  67 + public boolean isAutoRecMsg() {
  68 + return autoRecMsg;
  69 + }
  70 +
  71 + public void setAutoRecMsg(boolean autoRecMsg) {
  72 + this.autoRecMsg = autoRecMsg;
  73 + }
  74 +
  75 + public boolean isAutoRectTxt() {
  76 + return autoRectTxt;
  77 + }
  78 +
  79 + public boolean isRecordBtns() {
  80 + return recordBtns;
  81 + }
  82 +
  83 + public void setRecordBtns(boolean recordBtns) {
  84 + this.recordBtns = recordBtns;
  85 + }
  86 +
  87 + public boolean isBeautifulBtn() {
  88 + return beautifulBtn;
  89 + }
  90 +
  91 + public void setBeautifulBtn(boolean beautifulBtn) {
  92 + this.beautifulBtn = beautifulBtn;
  93 + }
  94 +
  95 + public boolean isFlashBtn() {
  96 + return flashBtn;
  97 + }
  98 +
  99 + public void setFlashBtn(boolean flashBtn) {
  100 + this.flashBtn = flashBtn;
  101 + }
  102 +
  103 + public void setAutoRectTxt(boolean autoRectTxt) {
  104 + this.autoRectTxt = autoRectTxt;
  105 + }
  106 +
  107 + public boolean isChatView() {
  108 + return chatView;
  109 + }
  110 +
  111 + public void setChatView(boolean chatView) {
  112 + this.chatView = chatView;
  113 + }
  114 +
  115 + public boolean isViewers() {
  116 + return viewers;
  117 + }
  118 +
  119 + public void setViewers(boolean viewers) {
  120 + this.viewers = viewers;
  121 + }
  122 +}
... ...
app/src/main/java/com/cnlive/gundam/stream/ui/activity/BaseStreamActivity.java 0 → 100644
  1 +package com.cnlive.gundam.stream.ui.activity;
  2 +
  3 +import android.Manifest;
  4 +import android.app.AlertDialog;
  5 +import android.app.Dialog;
  6 +import android.content.DialogInterface;
  7 +import android.content.Intent;
  8 +import android.content.pm.ActivityInfo;
  9 +import android.content.pm.PackageManager;
  10 +import android.opengl.GLSurfaceView;
  11 +import android.os.Build;
  12 +import android.os.Bundle;
  13 +import android.os.Environment;
  14 +import android.os.Handler;
  15 +import android.os.Message;
  16 +import android.provider.Settings;
  17 +import android.support.annotation.Nullable;
  18 +import android.support.v4.app.ActivityCompat;
  19 +import android.support.v7.app.AppCompatActivity;
  20 +import android.text.TextUtils;
  21 +import android.util.Log;
  22 +
  23 +import com.cnlive.libs.stream.Streamer;
  24 +import com.cnlive.libs.stream.base.IStreamer;
  25 +import com.cnlive.libs.stream.model.ActivityConfig;
  26 +import com.cnlive.libs.stream.model.Filters;
  27 +
  28 +import java.io.File;
  29 +
  30 +/**
  31 + * Created by Lynn on 5/16/2017.
  32 + */
  33 +
  34 +public class BaseStreamActivity extends AppCompatActivity implements Handler.Callback {
  35 +
  36 + private static final String TAG = "BaseStreamActivity";
  37 +
  38 + private Streamer mStreamer;
  39 +
  40 + protected String reconnect_msg;
  41 + protected String finish_msg;
  42 + protected String stop_msg;
  43 + protected int error_code;
  44 +
  45 + protected Handler mHandler;
  46 +
  47 + protected boolean isAutoStart = false;
  48 +
  49 + private final static int PERMISSION_REQUEST_CAMERA_AUDIOREC = 1;
  50 +
  51 + @Override
  52 + protected void onCreate(@Nullable Bundle savedInstanceState) {
  53 + super.onCreate(savedInstanceState);
  54 +
  55 + mHandler = new Handler(this);
  56 + }
  57 +
  58 + protected void initStreamer(boolean landScape, GLSurfaceView glSurfaceView) {
  59 + setRequestedOrientation(landScape ?
  60 + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE :
  61 + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  62 + mStreamer = new Streamer(this);
  63 + //设置预览编码帧率
  64 + mStreamer.setPreviewFps(15);
  65 + //设置推流编码帧率
  66 + mStreamer.setTargetFps(15);
  67 + //设置最初、最大、最小视频码率
  68 + mStreamer.setVideoKBitrate(800 * 3 / 4, 800, 800 / 4);
  69 + //设置音频编码码率
  70 + mStreamer.setAudioKBitrate(48);
  71 + //设置预览分辨率
  72 + mStreamer.setPreviewResolution(IStreamer.VIDEO_RESOLUTION_540P);
  73 + //设置推流分辨率
  74 + mStreamer.setTargetResolution(IStreamer.VIDEO_RESOLUTION_540P);
  75 + //设置编码模式
  76 + mStreamer.setEncodeMethod(IStreamer.ENCODE_METHOD_SOFTWARE_COMPAT);
  77 + //设置横竖屏方向
  78 + mStreamer.setRotateDegrees(landScape ? 90 : 0);
  79 + if (landScape) {
  80 + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//横屏
  81 + } else {
  82 + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏
  83 + }
  84 + //设置预览view
  85 + mStreamer.setDisplayPreview(glSurfaceView);
  86 + //设置前置摄像头镜像
  87 + mStreamer.setFrontCameraMirror(true);
  88 + //设置水印
  89 + String path = Environment.getExternalStorageDirectory() + File.separator + "cnlive_logo.png";
  90 + mStreamer.showWaterMarkLogo(path, 0.03f, 0.02f, landScape ? 0.0911f : 0.15f, landScape ? 0.0545f : 0.033f, 1f);
  91 + //设置视频编码是的帧间隔,默认3秒
  92 + mStreamer.setIFrameInterval(2);
  93 + //设置音频通道
  94 + mStreamer.setEnableStreamStatModule(true);
  95 + //设置自动重连
  96 + mStreamer.setEnableAutoRestart(true, 3, 10000);
  97 + //设置Info回调
  98 + mStreamer.setOnInfoListener(mInfoListener);
  99 + //设置error回调
  100 + mStreamer.setOnErrorListener(mErrorListener);
  101 + }
  102 +
  103 + protected void switchCamera() {
  104 + mStreamer.switchCamera();
  105 + }
  106 +
  107 + protected boolean isFrontCamera() {
  108 + return mStreamer.isFrontCamera();
  109 + }
  110 +
  111 + protected void setFilter(Filters filter) {
  112 + mStreamer.setFilter(filter.getFilter());
  113 + }
  114 +
  115 + protected void toggleTorch(boolean b) {
  116 + mStreamer.toggleTorch(b);
  117 + }
  118 +
  119 + protected boolean isRecording() {
  120 + return mStreamer.isRecording();
  121 + }
  122 +
  123 + protected boolean autoRestart() {
  124 + return mStreamer.getEnableAutoRestart();
  125 + }
  126 +
  127 + protected int getAutoRestartCount() {
  128 + return mStreamer.getAutoRestartCount();
  129 + }
  130 +
  131 + protected boolean startStream(ActivityConfig activityConfig) {
  132 + // TODO: 5/22/2017 统计
  133 + return mStreamer.startStream(activityConfig);
  134 + }
  135 +
  136 + protected boolean stopStream() {
  137 + // TODO: 5/22/2017 统计
  138 + return mStreamer.stopStream();
  139 + }
  140 +
  141 + @Override
  142 + protected void onPause() {
  143 + super.onPause();
  144 + if (dialog != null) dialog.dismiss();
  145 + mStreamer.setAudioOnly(true);
  146 + mStreamer.setMuteAudio(true);
  147 + mStreamer.stopCameraPreview();
  148 + mStreamer.pause();
  149 + }
  150 +
  151 + @Override
  152 + protected void onResume() {
  153 + super.onResume();
  154 + mStreamer.resume();
  155 + startCameraPreviewWithPermCheck();
  156 + mStreamer.setAudioOnly(false);
  157 + mStreamer.setMuteAudio(false);
  158 + }
  159 +
  160 + @Override
  161 + protected void onDestroy() {
  162 + super.onDestroy();
  163 + mStreamer.release();
  164 + mHandler.removeCallbacksAndMessages(null);
  165 + }
  166 +
  167 + @Override
  168 + public boolean handleMessage(Message message) {
  169 + reconnect_msg = null;
  170 + finish_msg = null;
  171 + stop_msg = null;
  172 + error_code = message.what;
  173 + switch (message.what) {
  174 + case IStreamer.cnstream_camera_init_done:
  175 + if (mStreamer.isRecording())
  176 + if (isAutoStart && !mStreamer.startStream())
  177 + stop_msg = "未能开启直播请重试.";
  178 + break;
  179 + case IStreamer.cnstream_stop_stream_done:
  180 + Log.i(TAG, "停止直播活动成功");
  181 + break;
  182 + case IStreamer.cnstream_open_stream_succ:
  183 + Log.i(TAG, "推流成功");
  184 + break;
  185 + case IStreamer.cnstream_frame_send_slow:
  186 + Log.i(TAG, "网络状态不佳,数据发送可能有延迟 发送当前帧的时长");
  187 + break;
  188 + case IStreamer.cnstream_est_bw_raise:
  189 + Log.i(TAG, "BW raise to" + message.arg1 / 1000 + "kbps");
  190 + break;
  191 + case IStreamer.cnstream_est_bw_drop:
  192 + Log.i(TAG, "BW drop to" + message.arg1 / 1000 + "kbps");
  193 + break;
  194 + case IStreamer.cnstream_error_start_stream_server:
  195 + Log.e(TAG, "开始直播是请求服务器失败");
  196 + reconnect_msg = "开始直播是请求服务器失败";
  197 + break;
  198 + case IStreamer.cnstream_error_start_stream_network:
  199 + Log.e(TAG, "开始直播时网络连接失败");
  200 + reconnect_msg = "开始直播时网络连接失败";
  201 + break;
  202 + case IStreamer.cnstream_error_start_stream_other:
  203 + Log.e(TAG, "开始直播活动失败");
  204 + reconnect_msg = "开始直播活动失败";
  205 + break;
  206 + case IStreamer.cnstream_error_create_stream_server:
  207 + Log.e(TAG, "创建直播活动请求服务器失败");
  208 + finish_msg = "创建直播活动请求服务器失败";
  209 + break;
  210 + case IStreamer.cnstream_error_create_stream_network:
  211 + Log.e(TAG, "创建直播活动是网络连接失败");
  212 + finish_msg = "创建直播活动是网络连接失败";
  213 + break;
  214 + case IStreamer.cnstream_error_create_stream_other:
  215 + Log.e(TAG, "创建直播活动时失败");
  216 + finish_msg = "创建直播活动时失败";
  217 + break;
  218 + case IStreamer.cnstream_record_error:
  219 + Log.e(TAG, "直播录制失败");
  220 + finish_msg = "直播录制失败";
  221 + break;
  222 + case IStreamer.cnstream_error_stop_stream:
  223 + Log.e(TAG, "停止直播活动失败");
  224 + break;
  225 + case IStreamer.cnstream_socket_disconnect:
  226 + Log.e(TAG, "推流socket异常断开");
  227 + reconnect_msg = "推流socket异常断开";
  228 + break;
  229 + case IStreamer.cnstream_error_dns_parse_failed:
  230 + Log.e(TAG, "url域名解析错误");
  231 + reconnect_msg = "url域名解析错误";
  232 + break;
  233 + case IStreamer.cnstream_error_connect_failed:
  234 + Log.e(TAG, "网络连接失败");
  235 + reconnect_msg = "网络连接失败";
  236 + break;
  237 + case IStreamer.cnstream_error_connect_breaked:
  238 + Log.e(TAG, "网络连接断开");
  239 + reconnect_msg = "网络连接断开";
  240 + break;
  241 + case IStreamer.cnstream_error_publish_failed:
  242 + Log.e(TAG, "跟RTMP服务器完成握手后,向{streamname}推流失败)");
  243 + reconnect_msg = "跟RTMP服务器完成握手后,向{streamname}推流失败)";
  244 + break;
  245 + case IStreamer.cnstream_error_av_async:
  246 + Log.e(TAG, "音视频采集pts差值超过5s");
  247 + reconnect_msg = "音视频采集pts差值超过5s";
  248 + break;
  249 + case IStreamer.cnstream_video_encoder_error_unsupported:
  250 + Log.e(TAG, "编码器初始化失败");
  251 + reconnect_msg = "编码器初始化失败";
  252 + break;
  253 + case IStreamer.cnstream_video_encoder_error_unknown:
  254 + Log.e(TAG, "视频编码失败");
  255 + reconnect_msg = "视频编码失败";
  256 + break;
  257 + case IStreamer.cnstream_audio_encoder_error_unsupported:
  258 + Log.e(TAG, "音频初始化失败");
  259 + reconnect_msg = "音频初始化失败";
  260 + break;
  261 + case IStreamer.cnstream_audio_encoder_error_unknown:
  262 + Log.e(TAG, "录音开始失败");
  263 + reconnect_msg = "录音开始失败";
  264 + break;
  265 + case IStreamer.cnstream_audio_recorder_error_unknown:
  266 + finish_msg = "录音开启未知错误";
  267 + Log.d(TAG, "录音开启未知错误");
  268 + break;
  269 + case IStreamer.cnstream_camera_error_unknown:
  270 + Log.d(TAG, "摄像头未知错误");
  271 +// finish_msg = "摄像头未知错误";
  272 + break;
  273 + case IStreamer.cnstream_camera_error_start_failed:
  274 + finish_msg = "打开摄像头失败";
  275 + Log.d(TAG, "打开摄像头失败");
  276 + break;
  277 + case IStreamer.cnstream_camera_error_server_died:
  278 +// finish_msg = "系统camera服务进程退出";
  279 + Log.d(TAG, "系统camera服务进程退出");
  280 + break;
  281 + case IStreamer.cnstream_error_already_over:
  282 + stop_msg = "直播已结束";
  283 + Log.d(TAG, "直播已结束");
  284 + break;
  285 + case IStreamer.cnstream_auto_restart_failed:
  286 + reconnect_msg = "自动重连失败";
  287 + Log.d(TAG, "自动重连失败");
  288 + break;
  289 + }
  290 + if (!TextUtils.isEmpty(reconnect_msg)) {
  291 + Log.e(TAG, " " + reconnect_msg);
  292 + }
  293 + if (!TextUtils.isEmpty(stop_msg)) {
  294 + Log.e(TAG, " " + stop_msg);
  295 + }
  296 + if (!TextUtils.isEmpty(finish_msg)) {
  297 + Log.e(TAG, " " + finish_msg);
  298 + }
  299 + return true;
  300 + }
  301 +
  302 + private IStreamer.OnInfoListener mInfoListener = new IStreamer.OnInfoListener() {
  303 + @Override
  304 + public void onInfo(int i, int i1, int i2) {
  305 + Log.e(TAG, " info " + i);
  306 + if (mHandler != null) {
  307 + Message message = new Message();
  308 + message.what = i;
  309 + message.arg1 = i1;
  310 + message.arg2 = i2;
  311 + mHandler.sendMessage(message);
  312 + }
  313 + }
  314 + };
  315 +
  316 + private IStreamer.OnErrorListener mErrorListener = new IStreamer.OnErrorListener() {
  317 + @Override
  318 + public void onError(int i, int i1, int i2) {
  319 + Log.e(TAG, " error " + i);
  320 + if (mHandler != null) {
  321 + Message message = new Message();
  322 + message.what = i;
  323 + message.arg1 = i1;
  324 + message.arg2 = i2;
  325 + mHandler.sendMessage(message);
  326 + }
  327 + }
  328 + };
  329 +
  330 + private Dialog dialog;
  331 +
  332 + private void startCameraPreviewWithPermCheck() {
  333 + int cameraPerm = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
  334 + int audioPerm = ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);
  335 + if (cameraPerm != PackageManager.PERMISSION_GRANTED || audioPerm != PackageManager.PERMISSION_GRANTED) {
  336 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
  337 + if (dialog != null) dialog.dismiss();
  338 + dialog = new AlertDialog.Builder(this)
  339 + .setMessage("")
  340 + .setPositiveButton("去设置", new DialogInterface.OnClickListener() {
  341 + @Override
  342 + public void onClick(DialogInterface dialogInterface, int i) {
  343 + Intent intent = new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
  344 + startActivity(intent);
  345 + }
  346 + })
  347 + .setNegativeButton("取消", new DialogInterface.OnClickListener() {
  348 + @Override
  349 + public void onClick(DialogInterface dialogInterface, int i) {
  350 + dialog.dismiss();
  351 + }
  352 + }).show();
  353 + } else {
  354 + String[] permissions = {Manifest.permission.CAMERA,
  355 + Manifest.permission.RECORD_AUDIO,
  356 + Manifest.permission.READ_EXTERNAL_STORAGE};
  357 + ActivityCompat.requestPermissions(this, permissions,
  358 + PERMISSION_REQUEST_CAMERA_AUDIOREC);
  359 + }
  360 + } else {
  361 + mStreamer.startCameraPreview();
  362 + }
  363 + }
  364 +}
... ...
app/src/main/java/com/cnlive/gundam/stream/ui/activity/StreamActivity.java 0 → 100644
  1 +package com.cnlive.gundam.stream.ui.activity;
  2 +
  3 +import android.app.AlertDialog;
  4 +import android.app.Dialog;
  5 +import android.content.DialogInterface;
  6 +import android.databinding.DataBindingUtil;
  7 +import android.os.Bundle;
  8 +import android.os.Handler;
  9 +import android.os.Message;
  10 +import android.support.v7.widget.LinearLayoutManager;
  11 +import android.text.Spannable;
  12 +import android.text.SpannableStringBuilder;
  13 +import android.text.TextUtils;
  14 +import android.text.style.ForegroundColorSpan;
  15 +import android.view.View;
  16 +import android.view.Window;
  17 +import android.view.WindowManager;
  18 +import android.widget.TextView;
  19 +
  20 +import com.cnlive.gundam.R;
  21 +import com.cnlive.gundam.databinding.ActivityStreamPortraitBinding;
  22 +import com.cnlive.gundam.stream.model.ChatItem;
  23 +import com.cnlive.gundam.stream.model.ExtensionModel;
  24 +import com.cnlive.gundam.stream.model.ShowView;
  25 +import com.cnlive.gundam.stream.ui.adapter.SimpleAdapter;
  26 +import com.cnlive.gundam.stream.util.ChatConnect;
  27 +import com.cnlive.gundam.stream.util.TimerUtil;
  28 +import com.cnlive.libs.stream.base.IStreamer;
  29 +import com.cnlive.libs.stream.model.ActivityConfig;
  30 +import com.cnlive.libs.stream.model.Filters;
  31 +import com.cnlive.libs.util.chat.ChatUtil;
  32 +import com.cnlive.libs.util.chat.base.IChat;
  33 +import com.cnlive.libs.util.chat.model.CNChatRoomInfo;
  34 +import com.cnlive.libs.util.chat.model.CNMessage;
  35 +import com.cnlive.libs.util.chat.model.CNUserInfo;
  36 +import com.google.gson.Gson;
  37 +
  38 +import org.json.JSONException;
  39 +import org.json.JSONObject;
  40 +
  41 +import java.util.ArrayList;
  42 +import java.util.List;
  43 +
  44 +public class StreamActivity extends BaseStreamActivity implements ChatConnect.ChatCallback, TimerUtil.TimerListener {
  45 +
  46 + private boolean isFace = false;
  47 + private boolean isFlash = false;
  48 +
  49 + private boolean inLive = false;
  50 +
  51 + private boolean hasError = false;
  52 +
  53 + private String activityId;
  54 + private String channelId;
  55 + private String channelName;
  56 + private String coverImgUrl;
  57 + private boolean isLandscape = false;
  58 +
  59 + private Dialog endDialog;
  60 + private ActivityStreamPortraitBinding binding;
  61 +
  62 + private String roomId;
  63 +
  64 + private ShowView showView = new ShowView();
  65 +
  66 + private List<ChatItem> chatItemList;
  67 + private SimpleAdapter chatAdapter;
  68 + private ChatConnect chatConnect;
  69 +
  70 + private TimerUtil timerUtil;
  71 + private String extensions;
  72 +
  73 + @Override
  74 + protected void onCreate(Bundle savedInstanceState) {
  75 + super.onCreate(savedInstanceState);
  76 + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
  77 + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  78 + requestWindowFeature(Window.FEATURE_NO_TITLE);
  79 +
  80 + //// TODO: 5/22/2017 统计
  81 + initData();
  82 +
  83 + binding = DataBindingUtil.setContentView(this, R.layout.activity_stream_portrait);
  84 + initStreamer(isLandscape, binding.cameraPreview);
  85 + showView.setStartBtn(true);
  86 + showView.setRetryLayout(false);
  87 + showView.setEndLayout(false);
  88 + showView.setMsgHint(false);
  89 + showView.setAutoRecMsg(false);
  90 + showView.setChatView(false);
  91 + showView.setViewers(false);
  92 + binding.setIsShow(showView);
  93 + binding.chatRecyclerView.setLayoutManager(new LinearLayoutManager(this));
  94 + chatItemList = new ArrayList<>();
  95 +
  96 + timerUtil = new TimerUtil(this);
  97 + }
  98 +
  99 + private void initData() {
  100 + activityId = getIntent().getStringExtra("activityId");
  101 + channelId = getIntent().getStringExtra("channelId");
  102 + channelName = getIntent().getStringExtra("channelName");
  103 + coverImgUrl = getIntent().getStringExtra("coverImgUrl");
  104 + isLandscape = getIntent().getBooleanExtra("landscape", false);
  105 +
  106 + ExtensionModel extensionModel = new ExtensionModel(channelName, coverImgUrl);
  107 + extensions = new Gson().toJson(extensionModel);
  108 +
  109 + roomId = activityId;
  110 + }
  111 +
  112 + public void onClick(View view) {
  113 + if (view.getId() == R.id.btn_beautiful) {
  114 + isFace = !isFace;
  115 + setFilter(isFace ? Filters.DENOISE : Filters.BEAUTY_DISABLE);
  116 + toggleBeautifulBtn(isFace);
  117 + } else if (view.getId() == R.id.btn_flip) {
  118 + toggleFlashBtn(isFrontCamera());
  119 + toggleTorch(isFlash = false);
  120 + switchCamera();
  121 + } else if (view.getId() == R.id.btn_flash) {
  122 + toggleTorch(isFlash = !isFlash);
  123 + } else if (view.getId() == R.id.btn_close) {
  124 + onBackPressed();
  125 + } else if (view.getId() == R.id.startStream) {
  126 + startStream(activityConfig);
  127 + showView.setStartBtn(false);
  128 + binding.setIsShow(showView);
  129 + } else if (view.getId() == R.id.btn_end_close) {
  130 + finish();
  131 + } else if (view.getId() == R.id.button_retry) {
  132 + startStream(activityConfig);
  133 + showRetryLayout(false);
  134 + }
  135 + }
  136 +
  137 + @Override
  138 + public boolean handleMessage(Message message) {
  139 + super.handleMessage(message);
  140 +
  141 + switch (message.what) {
  142 + case IStreamer.cnstream_camera_init_done:
  143 + isFace = true;
  144 + setFilter(Filters.DENOISE);
  145 + toggleBeautifulBtn(true);
  146 + break;
  147 + case IStreamer.cnstream_open_stream_succ:
  148 + inLive = true;
  149 + if (showView.isRetryLayout())
  150 + showRetryLayout(false);
  151 + //// TODO: 5/23/2017 连接聊天室
  152 + new Handler().post(new Runnable() {
  153 + @Override
  154 + public void run() {
  155 + if (!hasError) {
  156 + chatConnect = new ChatConnect(StreamActivity.this, StreamActivity.this);
  157 + chatConnect.connect(roomId, new CNUserInfo(channelId, channelName, coverImgUrl));
  158 + showView.setViewers(true);
  159 + }
  160 + chatViewShow(true);
  161 + }
  162 + });
  163 +
  164 + break;
  165 + }
  166 +
  167 + if (!TextUtils.isEmpty(reconnect_msg)) {
  168 + hasError = true;
  169 + showRetryView();
  170 + }
  171 + if (!TextUtils.isEmpty(finish_msg)) {
  172 + if (isRecording()) {
  173 + stopRecord(finish_msg);
  174 + } else {
  175 + finish();
  176 + }
  177 + }
  178 + if (!TextUtils.isEmpty(stop_msg)) {
  179 + stopRecord(stop_msg);
  180 + showMsgHint(true);
  181 + }
  182 + return true;
  183 + }
  184 +
  185 + private void stopRecord(String msg) {
  186 + stopStream();
  187 + showEndView(msg);
  188 + }
  189 +
  190 + //重试页面
  191 + private void showRetryView() {
  192 + if (showView.isChatView()) {
  193 + chatViewShow(false);
  194 + }
  195 + if (autoRestart()) {
  196 + showAutoRecMsg(true);
  197 + toggleAutoRecMsg(reconnect_msg);
  198 + } else {
  199 + showAutoRecMsg(false);
  200 + }
  201 + showRetryLayout(true);
  202 + }
  203 +
  204 + //结束页面
  205 + private void showEndView(String msg) {
  206 + if (showView.isRetryLayout()) {
  207 + showRetryLayout(false);
  208 + }
  209 + showView.setViewers(false);
  210 + if (msg.equals("直播已结束")) {
  211 + showMsgHint(true);
  212 + } else {
  213 + showMsgHint(false);
  214 + }
  215 + inLive = false;
  216 + showView.setRecordBtns(false);
  217 +
  218 + getOnlinePerson();
  219 +
  220 + binding.setIsShow(showView);
  221 + chatViewShow(false);
  222 + showEndLayout(true);
  223 + }
  224 +
  225 + private void showSpannableView(TextView view, String content, int start, int end) {
  226 + SpannableStringBuilder mSpannableStringBuilder = new SpannableStringBuilder(content);
  227 + mSpannableStringBuilder.setSpan
  228 + (new ForegroundColorSpan(0xFFfbdb44), start, end, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
  229 + view.setText(mSpannableStringBuilder);
  230 + }
  231 +
  232 + private void showDialog() {
  233 + endDialog = new AlertDialog.Builder(this).setMessage("确认结束此直播吗?")
  234 + .setPositiveButton("确认", new DialogInterface.OnClickListener() {
  235 + @Override
  236 + public void onClick(DialogInterface dialogInterface, int i) {
  237 + endDialog.dismiss();
  238 + stopRecord("");
  239 + }
  240 + })
  241 + .setNegativeButton("取消", new DialogInterface.OnClickListener() {
  242 + @Override
  243 + public void onClick(DialogInterface dialogInterface, int i) {
  244 + endDialog.dismiss();
  245 + }
  246 + }).show();
  247 + }
  248 +
  249 + private ActivityConfig activityConfig = new ActivityConfig() {
  250 + @Override
  251 + public String getActivityID() {
  252 + return activityId;
  253 + }
  254 +
  255 + @Override
  256 + public String getActivityName() {
  257 + return null;
  258 + }
  259 +
  260 + @Override
  261 + public int getActivityStatus() {
  262 + return 0;
  263 + }
  264 +
  265 + @Override
  266 + public int getIsHorizontalScreen() {
  267 + return isLandscape ? 1 : 0;
  268 + }
  269 +
  270 + @Override
  271 + public String getChannelID() {
  272 + return channelId;
  273 + }
  274 +
  275 + @Override
  276 + public String getCoverImgUrl() {
  277 + return coverImgUrl;
  278 + }
  279 +
  280 + @Override
  281 + public String getActivityCategory() {
  282 + return null;
  283 + }
  284 +
  285 + @Override
  286 + public String getExtensions() {
  287 + return extensions;
  288 + }
  289 +
  290 + @Override
  291 + public String getUuid() {
  292 + return null;
  293 + }
  294 +
  295 + @Override
  296 + public String getCallbackAcitvityStatushttpPostUrl() {
  297 + return null;
  298 + }
  299 + };
  300 +
  301 + @Override
  302 + protected void onPause() {
  303 + super.onPause();
  304 + if (endDialog != null) endDialog.dismiss();
  305 + if (timerUtil != null) timerUtil.cancelTimer();
  306 + }
  307 +
  308 + @Override
  309 + protected void onResume() {
  310 + super.onResume();
  311 + if (timerUtil != null) timerUtil.startTimer(0, TimerUtil.UPDATE_ONLINE_PERSON);
  312 + }
  313 +
  314 + @Override
  315 + protected void onDestroy() {
  316 + super.onDestroy();
  317 + if (chatConnect != null) chatConnect.disconnect();
  318 + }
  319 +
  320 + @Override
  321 + public void onBackPressed() {
  322 + if (!inLive) {
  323 + super.onBackPressed();
  324 + } else {
  325 + showDialog();
  326 + }
  327 + }
  328 +
  329 + private void showRetryLayout(boolean show) {
  330 + showView.setRetryLayout(show);
  331 + binding.setIsShow(showView);
  332 + }
  333 +
  334 + private void showEndLayout(boolean show) {
  335 + showView.setEndLayout(show);
  336 + binding.setIsShow(showView);
  337 + }
  338 +
  339 + private void showMsgHint(boolean show) {
  340 + showView.setMsgHint(show);
  341 + binding.setIsShow(showView);
  342 + }
  343 +
  344 + private void showAutoRecMsg(boolean show) {
  345 + showView.setAutoRecMsg(show);
  346 + binding.setIsShow(showView);
  347 + }
  348 +
  349 + private void toggleAutoRecMsg(String msg) {
  350 + if (msg.equals("自动重连失败")) {
  351 + showView.setAutoRectTxt(false);
  352 + } else {
  353 + if (getAutoRestartCount() > 0) {
  354 + showView.setAutoRectTxt(true);
  355 + } else {
  356 + showAutoRecMsg(false);
  357 + }
  358 + }
  359 + binding.setIsShow(showView);
  360 + }
  361 +
  362 + private void toggleBeautifulBtn(boolean beauty) {
  363 + showView.setBeautifulBtn(beauty);
  364 + binding.setIsShow(showView);
  365 + }
  366 +
  367 + private void toggleFlashBtn(boolean isFlash) {
  368 + showView.setFlashBtn(isFlash);
  369 + binding.setIsShow(showView);
  370 + }
  371 +
  372 + private void chatViewShow(boolean show) {
  373 + showView.setChatView(show);
  374 + binding.setIsShow(showView);
  375 + }
  376 +
  377 + //聊天室
  378 + @Override
  379 + public void onChatState(int what) {
  380 +
  381 + }
  382 +
  383 + @Override
  384 + public void onChatMessage(CNMessage cnMessage) {
  385 + //// TODO: 5/23/2017 Recyclerview添加数据
  386 + String headUrl = cnMessage.getUserInfo().getHeadUrl() == null ? "" : cnMessage.getUserInfo().getHeadUrl();
  387 + String userName = cnMessage.getUserInfo().getUserName() == null ? "" : cnMessage.getUserInfo().getUserName();
  388 + String content = null;
  389 + try {
  390 + content = cnMessage.getContent() == null ? "" : (String) new JSONObject(cnMessage.getContent()).get("message");
  391 + } catch (JSONException e) {
  392 + e.printStackTrace();
  393 + }
  394 + ChatItem chatItem = new ChatItem(headUrl, userName, content);
  395 + chatItemList.add(chatItem);
  396 + chatAdapter = new SimpleAdapter(this, chatItemList, R.layout.item_chat, com.cnlive.gundam.BR.chat);
  397 + binding.setChatAdapter(chatAdapter);
  398 +
  399 + }
  400 +
  401 + private void getOnlinePerson() {
  402 + ChatUtil.getChatRoomInfo(roomId, 0, CNChatRoomInfo.ChatRoomMemberOrder.RC_CHAT_ROOM_MEMBER_ASC, new IChat.CNResultCallback<CNChatRoomInfo>() {
  403 + @Override
  404 + public void onSuccess(CNChatRoomInfo cnChatRoomInfo) {
  405 + binding.viewers.setText(cnChatRoomInfo.getTotalMemberCount() + "人");
  406 + }
  407 +
  408 + @Override
  409 + public void onError(int i, String s) {
  410 + }
  411 + });
  412 + }
  413 +
  414 + @Override
  415 + public void onTimer() {
  416 + getOnlinePerson();
  417 + }
  418 +}
  419 +
... ...
app/src/main/java/com/cnlive/gundam/stream/ui/adapter/ChatAdapter.java 0 → 100644
  1 +package com.cnlive.gundam.stream.ui.adapter;
  2 +
  3 +
  4 +import android.content.Context;
  5 +import android.databinding.DataBindingUtil;
  6 +import android.databinding.ViewDataBinding;
  7 +import android.view.LayoutInflater;
  8 +import android.view.View;
  9 +import android.view.ViewGroup;
  10 +import android.widget.BaseAdapter;
  11 +
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * Created by Lynn on 5/24/2017.
  16 + */
  17 +
  18 +public class ChatAdapter<T> extends BaseAdapter {
  19 + private Context mContext;
  20 + private List<T> mList;
  21 + private int layoutId;
  22 + private int variableId;
  23 +
  24 + public ChatAdapter(Context mContext, List<T> list, int layoutId, int variableId) {
  25 + this.mContext = mContext;
  26 + this.mList = list;
  27 + this.layoutId = layoutId;
  28 + this.variableId = variableId;
  29 + }
  30 +
  31 + @Override
  32 + public int getCount() {
  33 + return mList.size();
  34 + }
  35 +
  36 + @Override
  37 + public Object getItem(int i) {
  38 + return mList.get(i);
  39 + }
  40 +
  41 + @Override
  42 + public long getItemId(int i) {
  43 + return i;
  44 + }
  45 +
  46 + @Override
  47 + public View getView(int i, View view, ViewGroup viewGroup) {
  48 + ViewDataBinding binding;
  49 + if (view == null) {
  50 + binding = DataBindingUtil.inflate(LayoutInflater.from(mContext), layoutId, viewGroup, false);
  51 + } else {
  52 + binding = DataBindingUtil.getBinding(view);
  53 + }
  54 + binding.setVariable(variableId, mList.get(i));
  55 + binding.executePendingBindings();
  56 + return binding.getRoot();
  57 + }
  58 +}
... ...
app/src/main/java/com/cnlive/gundam/stream/ui/adapter/SimpleAdapter.java 0 → 100644
  1 +package com.cnlive.gundam.stream.ui.adapter;
  2 +
  3 +import android.content.Context;
  4 +import android.databinding.DataBindingUtil;
  5 +import android.databinding.ViewDataBinding;
  6 +import android.support.v7.widget.RecyclerView;
  7 +import android.view.LayoutInflater;
  8 +import android.view.View;
  9 +import android.view.ViewGroup;
  10 +
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * Created by Lynn on 5/25/2017.
  15 + */
  16 +
  17 +public class SimpleAdapter<T> extends RecyclerView.Adapter<SimpleAdapter.ViewHolder> {
  18 + private Context mContext;
  19 + private List<T> activeInfoList;
  20 + private int layoutId;
  21 + private int variableId;
  22 +
  23 +
  24 + public SimpleAdapter(Context mContext, List<T> activeInfoList, int layoutId, int variableId) {
  25 + this.mContext = mContext;
  26 + this.activeInfoList = activeInfoList;
  27 + this.layoutId = layoutId;
  28 + this.variableId = variableId;
  29 + }
  30 +
  31 + @Override
  32 + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  33 + LayoutInflater inflater = LayoutInflater.from(parent.getContext());
  34 + ViewDataBinding binding = DataBindingUtil.inflate(inflater, layoutId, parent, false);
  35 + ViewHolder viewHolder = new ViewHolder(binding.getRoot());
  36 + viewHolder.setBinding(binding);
  37 + return viewHolder;
  38 + }
  39 +
  40 + @Override
  41 + public void onBindViewHolder(SimpleAdapter.ViewHolder holder, int position) {
  42 + holder.getBinding().setVariable(variableId, activeInfoList.get(position));
  43 + holder.getBinding().executePendingBindings();
  44 + }
  45 +
  46 +
  47 + @Override
  48 + public long getItemId(int i) {
  49 + return i;
  50 + }
  51 +
  52 + @Override
  53 + public int getItemCount() {
  54 + return activeInfoList == null ? 0 : activeInfoList.size();
  55 + }
  56 +
  57 + public class ViewHolder extends RecyclerView.ViewHolder {
  58 +
  59 + private ViewDataBinding binding;
  60 +
  61 + public ViewDataBinding getBinding() {
  62 + return binding;
  63 + }
  64 +
  65 + public void setBinding(ViewDataBinding binding) {
  66 + this.binding = binding;
  67 + }
  68 +
  69 + public ViewHolder(View itemView) {
  70 + super(itemView);
  71 + }
  72 + }
  73 +}
... ...
app/src/main/java/com/cnlive/gundam/stream/util/ChatConnect.java 0 → 100644
  1 +package com.cnlive.gundam.stream.util;
  2 +
  3 +import android.content.Context;
  4 +import android.util.Log;
  5 +
  6 +import com.cnlive.libs.util.chat.ChatUtil;
  7 +import com.cnlive.libs.util.chat.base.IChat;
  8 +import com.cnlive.libs.util.chat.model.CNChatRoomInfo;
  9 +import com.cnlive.libs.util.chat.model.CNMessage;
  10 +import com.cnlive.libs.util.chat.model.CNUserInfo;
  11 +
  12 +/**
  13 + * Created by Lynn on 5/23/2017.
  14 + */
  15 +
  16 +public class ChatConnect implements IChat.OnConnectStatusListener, IChat.OnReceiveMessageListener {
  17 + private static final String TAG = "ChatConnect";
  18 +
  19 + public static final int ERROR_CONNECT = 1001;
  20 + public static final int ERROR_CHAT = 1002;
  21 + public static final int ERROR_DISCONNECT = 1003;
  22 +
  23 + public static final int STATE_CONNECT = 2001;
  24 + public static final int STATE_JOIN = 2002;
  25 + public static final int STATE_DISCONNECT = 2003;
  26 + public static final int STATE_CONNECTING = 2004;
  27 +
  28 + private String userId;
  29 + private String userName;
  30 + private String headUrl;
  31 +
  32 + private String mRoomId;
  33 + private Context mContext;
  34 + private ChatCallback callback;
  35 + private CNChatRoomInfo mCnChatRoomInfo;
  36 +
  37 + public ChatConnect(Context mContext, ChatCallback callback) {
  38 + this.mContext = mContext;
  39 + this.callback = callback;
  40 + }
  41 +
  42 + public void connect(final String roomId, CNUserInfo cnUserInfo) {
  43 + userId = cnUserInfo.getUserId();
  44 + userName = cnUserInfo.getUserName();
  45 + headUrl = cnUserInfo.getHeadUrl();
  46 +
  47 + Log.e(TAG, userId + " : " + userName + " : " + headUrl);
  48 + this.mRoomId = roomId;
  49 +
  50 + ChatUtil.setOnReceiveMessageListener(this);
  51 +// ChatUtil.setConnectStatusListener(this);
  52 + ChatUtil.setOnConnectStatusListener(this);
  53 +
  54 + IChat.OnConnectListener onConnectListener = new IChat.OnConnectListener() {
  55 + @Override
  56 + public void onTokenIncorrect(String s) {
  57 + if (callback != null) callback.onChatState(ERROR_CONNECT);
  58 + }
  59 +
  60 + @Override
  61 + public void onSuccess(String s) {
  62 + if (callback != null) callback.onChatState(STATE_CONNECT);
  63 + if (userId == null || userId == "") ChatUtil.modifyUser(userName, "");
  64 + ChatUtil.joinChatRoom(mRoomId, 0, new IChat.OnOperationListener() {
  65 + @Override
  66 + public void onSuccess(int i, String s) {
  67 + if (callback != null) callback.onChatState(STATE_JOIN);
  68 + }
  69 +
  70 + @Override
  71 + public void onError(int i, String s) {
  72 + if (callback != null) callback.onChatState(ERROR_CHAT);
  73 + }
  74 + });
  75 + }
  76 +
  77 + @Override
  78 + public void onError(int i, String s) {
  79 + if (callback != null) callback.onChatState(ERROR_CONNECT);
  80 + }
  81 + };
  82 +
  83 + if (userId != null || userId != "") {
  84 + ChatUtil.connect(new CNUserInfo(String.valueOf(userId), userName, headUrl), onConnectListener);
  85 + } else {
  86 + ChatUtil.connect(onConnectListener);
  87 + }
  88 + }
  89 +
  90 + int viewCount = 0;
  91 +
  92 + public int getChatRoomInfo() {
  93 + ChatUtil.getChatRoomInfo(mRoomId, 500, CNChatRoomInfo.ChatRoomMemberOrder.RC_CHAT_ROOM_MEMBER_ASC, new IChat.CNResultCallback<CNChatRoomInfo>() {
  94 + @Override
  95 + public void onSuccess(CNChatRoomInfo cnChatRoomInfo) {
  96 + mCnChatRoomInfo = cnChatRoomInfo;
  97 + viewCount = cnChatRoomInfo.getTotalMemberCount();
  98 + }
  99 +
  100 + @Override
  101 + public void onError(int i, String s) {
  102 + }
  103 + });
  104 + return viewCount;
  105 + }
  106 +
  107 + public void disconnect() {
  108 + ChatUtil.quitChatRoom(mRoomId, new IChat.OnOperationListener() {
  109 + @Override
  110 + public void onSuccess(int i, String s) {
  111 + }
  112 +
  113 + @Override
  114 + public void onError(int i, String s) {
  115 + if (callback != null) callback.onChatState(ERROR_DISCONNECT);
  116 + }
  117 + });
  118 + ChatUtil.removeReceiveMessageListener(this);
  119 + ChatUtil.removeConnectStatusListener(this);
  120 + callback = null;
  121 + mContext = null;
  122 + }
  123 +
  124 + @Override
  125 + public void onConnectStatus(int i, String s) {
  126 + switch (i) {
  127 + case IChat.CONNECTED:
  128 + if (callback != null) callback.onChatState(STATE_CONNECT);
  129 + break;
  130 + case IChat.CONNECTING:
  131 + if (callback != null) callback.onChatState(STATE_CONNECTING);
  132 + break;
  133 + case IChat.DISCONNECTED:
  134 + case IChat.NETWORK_UNAVAILABLE:
  135 + if (callback != null) callback.onChatState(STATE_DISCONNECT);
  136 + break;
  137 + }
  138 +
  139 + }
  140 +
  141 + @Override
  142 + public void processMessage(CNMessage cnMessage) {
  143 +
  144 + if (mRoomId.equals(cnMessage.getTargetId())) {
  145 +// String from = cnMessage.getUserInfo() == null ? "" : cnMessage.getUserInfo().getUserName();
  146 +// String body = cnMessage.getContent();
  147 +// String userId = ChatUtil.getCurrentUserInfo().getUserId();
  148 + if (callback != null) callback.onChatMessage(cnMessage);
  149 + }
  150 +
  151 + }
  152 +
  153 + public void setCallback(ChatCallback callback) {
  154 + this.callback = callback;
  155 + }
  156 +
  157 + public interface ChatCallback {
  158 + void onChatState(int what);
  159 +
  160 + void onChatMessage(CNMessage cnMessage);
  161 + }
  162 +}
... ...
app/src/main/java/com/cnlive/gundam/stream/util/TimerUtil.java 0 → 100644
  1 +package com.cnlive.gundam.stream.util;
  2 +
  3 +import android.os.Handler;
  4 +import android.os.Message;
  5 +
  6 +/**
  7 + * Created by Lynn on 5/25/2017.
  8 + */
  9 +
  10 +public class TimerUtil {
  11 +
  12 + public static final int UPDATE_ONLINE_PERSON = 30000;
  13 +
  14 + private final int HANDLER_WHAT = 1;
  15 + private int delayTime;
  16 +
  17 + private TimerHandler mHandler = null;
  18 + private TimerListener l;
  19 +
  20 + public TimerUtil(TimerListener listener) {
  21 + if (mHandler == null)
  22 + mHandler = new TimerHandler();
  23 + this.l = listener;
  24 + }
  25 +
  26 + public void startTimer(int startTime, int delay) {
  27 + delayTime = delay;
  28 + if (mHandler != null) {
  29 + mHandler.removeMessages(HANDLER_WHAT);
  30 + } else {
  31 + mHandler = new TimerHandler();
  32 + }
  33 + mHandler.sendEmptyMessageDelayed(HANDLER_WHAT, startTime);
  34 + }
  35 +
  36 + public void cancelTimer() {
  37 + if (mHandler != null && mHandler.hasMessages(HANDLER_WHAT)) {
  38 + mHandler.removeMessages(HANDLER_WHAT);
  39 + mHandler = null;
  40 + }
  41 + }
  42 +
  43 + class TimerHandler extends Handler {
  44 + @Override
  45 + public void handleMessage(Message msg) {
  46 + super.handleMessage(msg);
  47 + if (msg.what == HANDLER_WHAT) {
  48 + startTimer(delayTime, delayTime);
  49 + if (l != null) l.onTimer();
  50 + }
  51 + }
  52 + }
  53 +
  54 + public interface TimerListener {
  55 + void onTimer();
  56 + }
  57 +
  58 +}
... ...
app/src/main/jniLibs/arm64-v8a/libRongIMLib.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/arm64-v8a/libksylive.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi-v7a/libRongIMLib.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi-v7a/libksylive.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi/libRongIMLib.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi/libksylive.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/x86/libRongIMLib.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/x86/libksylive.so 0 → 100644
No preview for this file type
app/src/main/res/drawable-hdpi/ic_bubble.png 0 → 100644

4.46 KB

app/src/main/res/drawable-hdpi/ic_bubble_sel.png 0 → 100644

4.12 KB

app/src/main/res/drawable-hdpi/ic_close.png 0 → 100644

4.45 KB

app/src/main/res/drawable-hdpi/ic_close_on.png 0 → 100644

4.12 KB

app/src/main/res/drawable-hdpi/ic_flash_on.png 0 → 100644

4.11 KB

app/src/main/res/drawable-hdpi/ic_flash_on_sel.png 0 → 100644

3.91 KB

app/src/main/res/drawable-hdpi/ic_switch_camera.png 0 → 100644

4.15 KB

app/src/main/res/drawable-hdpi/ic_switch_camera_sel.png 0 → 100644

4.05 KB

app/src/main/res/drawable-mdpi/ic_bubble.png 0 → 100644

2.49 KB

app/src/main/res/drawable-mdpi/ic_bubble_sel.png 0 → 100644

2.29 KB

app/src/main/res/drawable-mdpi/ic_close.png 0 → 100644

2.43 KB

app/src/main/res/drawable-mdpi/ic_close_on.png 0 → 100644

2.25 KB

app/src/main/res/drawable-mdpi/ic_flash_on.png 0 → 100644

2.28 KB

app/src/main/res/drawable-mdpi/ic_flash_on_sel.png 0 → 100644

2.21 KB

app/src/main/res/drawable-mdpi/ic_switch_camera.png 0 → 100644

2.36 KB

app/src/main/res/drawable-mdpi/ic_switch_camera_sel.png 0 → 100644

2.26 KB

app/src/main/res/drawable-xhdpi/ic_bubble.png 0 → 100644

5.84 KB

app/src/main/res/drawable-xhdpi/ic_bubble_sel.png 0 → 100644

5.52 KB

app/src/main/res/drawable-xhdpi/ic_close.png 0 → 100644

5.83 KB

app/src/main/res/drawable-xhdpi/ic_close_on.png 0 → 100644

5.44 KB

app/src/main/res/drawable-xhdpi/ic_flash_on.png 0 → 100644

5.4 KB

app/src/main/res/drawable-xhdpi/ic_flash_on_sel.png 0 → 100644

5.26 KB

app/src/main/res/drawable-xhdpi/ic_switch_camera.png 0 → 100644

5.35 KB

app/src/main/res/drawable-xhdpi/ic_switch_camera_sel.png 0 → 100644

5.36 KB

app/src/main/res/drawable-xxhdpi/ic_bubble.png 0 → 100644

10.5 KB

app/src/main/res/drawable-xxhdpi/ic_bubble_sel.png 0 → 100644

9.94 KB

app/src/main/res/drawable-xxhdpi/ic_close.png 0 → 100644

10.9 KB

app/src/main/res/drawable-xxhdpi/ic_close_on.png 0 → 100644

10.1 KB

app/src/main/res/drawable-xxhdpi/ic_flash_on.png 0 → 100644

9.61 KB

app/src/main/res/drawable-xxhdpi/ic_flash_on_sel.png 0 → 100644

9.46 KB

app/src/main/res/drawable-xxhdpi/ic_switch_camera.png 0 → 100644

9.68 KB

app/src/main/res/drawable-xxhdpi/ic_switch_camera_sel.png 0 → 100644

9.55 KB

app/src/main/res/drawable-xxhdpi/icon_record_meiyan_off.png 0 → 100644

4.48 KB

app/src/main/res/drawable-xxhdpi/icon_record_meiyan_on.png 0 → 100644

6.14 KB

app/src/main/res/drawable-xxxhdpi/ic_bubble.png 0 → 100644

14.3 KB

app/src/main/res/drawable-xxxhdpi/ic_bubble_sel.png 0 → 100644

13.8 KB

app/src/main/res/drawable-xxxhdpi/ic_close.png 0 → 100644

14.9 KB

app/src/main/res/drawable-xxxhdpi/ic_close_on.png 0 → 100644

14 KB

app/src/main/res/drawable-xxxhdpi/ic_flash_on.png 0 → 100644

13.2 KB

app/src/main/res/drawable-xxxhdpi/ic_flash_on_sel.png 0 → 100644

13.3 KB

app/src/main/res/drawable-xxxhdpi/ic_switch_camera.png 0 → 100644

13 KB

app/src/main/res/drawable-xxxhdpi/ic_switch_camera_sel.png 0 → 100644

13.4 KB

app/src/main/res/drawable/anchor_retry_nor.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<shape xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:shape="rectangle">
  4 +
  5 + <corners android:radius="10dp" />
  6 +
  7 + <stroke android:width="1dp" android:color="@android:color/white" android:dashWidth="1px" />
  8 +</shape>
0 9 \ No newline at end of file
... ...
app/src/main/res/drawable/btn_record_end.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<shape
  3 + xmlns:android="http://schemas.android.com/apk/res/android"
  4 + android:shape="rectangle"
  5 + android:useLevel="false">
  6 + <solid android:color="@android:color/transparent" />
  7 +
  8 + <stroke
  9 + android:width="3px"
  10 + android:color="#fbdb44" /> <!-- 定义描边的宽度和描边的颜色值 -->
  11 +
  12 + <size
  13 + android:height="40dp"
  14 + android:width="275dp"/>
  15 +
  16 + <corners
  17 + android:radius="10dp" />
  18 +</shape>
0 19 \ No newline at end of file
... ...
app/src/main/res/drawable/icon_record_close.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<selector xmlns:android="http://schemas.android.com/apk/res/android">
  3 +
  4 + <item android:drawable="@drawable/ic_close_on" android:state_pressed="true" />
  5 + <item android:drawable="@drawable/ic_close" android:state_pressed="false" />
  6 +</selector>
0 7 \ No newline at end of file
... ...
app/src/main/res/drawable/icon_record_flash.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<selector xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <item android:drawable="@drawable/ic_flash_on_sel" android:state_pressed="true" />
  4 + <item android:drawable="@drawable/ic_flash_on" android:state_pressed="false" />
  5 +</selector>
0 6 \ No newline at end of file
... ...
app/src/main/res/drawable/icon_record_flip.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<selector xmlns:android="http://schemas.android.com/apk/res/android">
  3 +
  4 + <item android:drawable="@drawable/ic_switch_camera_sel" android:state_pressed="true" />
  5 + <item android:drawable="@drawable/ic_switch_camera" android:state_pressed="false" />
  6 +</selector>
0 7 \ No newline at end of file
... ...
app/src/main/res/layout/activity_stream_portrait.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<layout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:app="http://schemas.android.com/apk/res-auto"
  4 + xmlns:tools="http://schemas.android.com/tools">
  5 +
  6 + <data>
  7 +
  8 + <variable
  9 + name="isShow"
  10 + type="com.cnlive.gundam.stream.model.ShowView" />
  11 +
  12 + <variable
  13 + name="chatAdapter"
  14 + type="com.cnlive.gundam.stream.ui.adapter.SimpleAdapter" />
  15 + </data>
  16 +
  17 + <RelativeLayout
  18 + android:layout_width="match_parent"
  19 + android:layout_height="match_parent"
  20 + tools:context="com.cnlive.granzon.streamer.StreamActivity">
  21 +
  22 + <android.opengl.GLSurfaceView
  23 + android:id="@+id/camera_preview"
  24 + android:layout_width="match_parent"
  25 + android:layout_height="match_parent"
  26 + android:layout_alignParentBottom="true"
  27 + android:layout_alignParentTop="true" />
  28 +
  29 + <TextView
  30 + android:id="@+id/viewers"
  31 + android:layout_width="wrap_content"
  32 + android:layout_height="wrap_content"
  33 + android:layout_marginLeft="10dp"
  34 + android:layout_marginTop="5dp"
  35 + android:text="0人"
  36 + android:textColor="@android:color/white"
  37 + android:textSize="12sp"
  38 + android:visibility="@{isShow.viewers ? 0 : 8}"
  39 + tools:text="62人" />
  40 +
  41 + <RelativeLayout
  42 + android:layout_width="300dp"
  43 + android:layout_height="200dp"
  44 + android:layout_alignParentBottom="true"
  45 + android:layout_alignParentLeft="true"
  46 + android:layout_alignParentStart="true">
  47 +
  48 + <android.support.v7.widget.RecyclerView
  49 + android:id="@+id/chat_recycler_view"
  50 + android:layout_width="match_parent"
  51 + android:layout_height="match_parent"
  52 + android:divider="@null"
  53 + android:scrollbars="none"
  54 + android:stackFromBottom="true"
  55 + android:transcriptMode="alwaysScroll"
  56 + android:visibility="@{isShow.chatView ? 0 : 8}"
  57 + app:adapter="@{chatAdapter}" />
  58 + </RelativeLayout>
  59 +
  60 + <RelativeLayout
  61 + android:id="@+id/end_layout"
  62 + android:layout_width="match_parent"
  63 + android:layout_height="match_parent"
  64 + android:background="#E6000000"
  65 + android:gravity="center_horizontal"
  66 + android:visibility="@{isShow.endLayout ? 0 : 8}">
  67 +
  68 + <RelativeLayout
  69 + android:id="@+id/main_layout"
  70 + android:layout_width="match_parent"
  71 + android:layout_height="wrap_content"
  72 + android:layout_centerHorizontal="true"
  73 + android:layout_centerVertical="true">
  74 +
  75 + <TextView
  76 + android:id="@+id/end_tag"
  77 + android:layout_width="wrap_content"
  78 + android:layout_height="wrap_content"
  79 + android:layout_centerHorizontal="true"
  80 + android:layout_marginTop="0dp"
  81 + android:text="直播已结束"
  82 + android:textColor="#fbdb44"
  83 + android:textSize="28sp" />
  84 +
  85 +
  86 + <TextView
  87 + android:id="@+id/end_view"
  88 + android:layout_width="wrap_content"
  89 + android:layout_height="wrap_content"
  90 + android:layout_below="@+id/end_tag"
  91 + android:layout_centerHorizontal="true"
  92 + android:layout_marginTop="40dp"
  93 + android:gravity="center"
  94 + android:text="感谢大家观看\n我们下次再见ლ(❛◡❛✿)ლ"
  95 + android:textColor="@android:color/white"
  96 + android:textSize="18sp" />
  97 +
  98 + <!--<TextView-->
  99 + <!--android:id="@+id/end_like"-->
  100 + <!--android:layout_width="wrap_content"-->
  101 + <!--android:layout_height="wrap_content"-->
  102 + <!--android:layout_below="@+id/end_view"-->
  103 + <!--android:layout_centerHorizontal="true"-->
  104 + <!--android:layout_marginTop="15dp"-->
  105 + <!--android:text="0人喜欢"-->
  106 + <!--android:textColor="@android:color/white"-->
  107 + <!--android:textSize="18sp" />-->
  108 +
  109 + <!--<TextView-->
  110 + <!--android:id="@+id/end_award"-->
  111 + <!--android:layout_width="wrap_content"-->
  112 + <!--android:layout_height="wrap_content"-->
  113 + <!--android:layout_below="@+id/end_like"-->
  114 + <!--android:layout_centerHorizontal="true"-->
  115 + <!--android:layout_marginTop="30dp"-->
  116 + <!--android:text="本次直播收货 0 中国币"-->
  117 + <!--android:textColor="@android:color/white"-->
  118 + <!--android:textSize="18sp" />-->
  119 +
  120 +
  121 + <Button
  122 + android:id="@+id/btn_end_close"
  123 + android:layout_width="wrap_content"
  124 + android:layout_height="42dp"
  125 + android:layout_below="@+id/end_view"
  126 + android:layout_centerHorizontal="true"
  127 + android:layout_marginTop="48dp"
  128 + android:background="@drawable/btn_record_end"
  129 + android:onClick="onClick"
  130 + android:text="知道了"
  131 + android:textColor="@android:color/white"
  132 + android:textSize="18sp" />
  133 +
  134 + <TextView
  135 + android:id="@+id/msg_hint"
  136 + android:layout_width="wrap_content"
  137 + android:layout_height="wrap_content"
  138 + android:layout_below="@id/btn_end_close"
  139 + android:layout_centerHorizontal="true"
  140 + android:layout_marginTop="20dp"
  141 + android:text="抱歉,连接已断开"
  142 + android:textColor="#ff0000"
  143 + android:visibility="gone" />
  144 +
  145 + </RelativeLayout>
  146 + </RelativeLayout>
  147 +
  148 + <LinearLayout
  149 + android:id="@+id/record_btns"
  150 + android:layout_width="wrap_content"
  151 + android:layout_height="wrap_content"
  152 + android:layout_alignParentEnd="true"
  153 + android:layout_alignParentRight="true"
  154 + android:orientation="horizontal"
  155 + android:visibility="@{isShow.recordBtns ? 0 : 8}">
  156 +
  157 + <ImageButton
  158 + android:id="@+id/btn_beautiful"
  159 + android:layout_width="43dp"
  160 + android:layout_height="43dp"
  161 + android:background="@null"
  162 + android:onClick="onClick"
  163 + android:padding="5dp"
  164 + android:scaleType="fitCenter"
  165 + android:src="@{isShow.beautifulBtn ? @drawable/icon_record_meiyan_on : @drawable/icon_record_meiyan_off}" />
  166 +
  167 + <ImageButton
  168 + android:id="@+id/btn_flip"
  169 + android:layout_width="43dp"
  170 + android:layout_height="43dp"
  171 + android:background="@null"
  172 + android:onClick="onClick"
  173 + android:padding="5dp"
  174 + android:scaleType="fitCenter"
  175 + android:src="@drawable/icon_record_flip" />
  176 +
  177 + <ImageButton
  178 + android:id="@+id/btn_flash"
  179 + android:layout_width="43dp"
  180 + android:layout_height="43dp"
  181 + android:background="@null"
  182 + android:onClick="onClick"
  183 + android:padding="5dp"
  184 + android:scaleType="fitCenter"
  185 + android:src="@drawable/icon_record_flash"
  186 + android:visibility="@{isShow.flashBtn ? 0 : 8}" />
  187 +
  188 + <ImageButton
  189 + android:id="@+id/btn_close"
  190 + android:layout_width="43dp"
  191 + android:layout_height="43dp"
  192 + android:layout_marginEnd="5dp"
  193 + android:layout_marginRight="5dp"
  194 + android:background="@null"
  195 + android:onClick="onClick"
  196 + android:padding="5dp"
  197 + android:scaleType="fitCenter"
  198 + android:src="@drawable/icon_record_close" />
  199 + </LinearLayout>
  200 +
  201 + <Button
  202 + android:id="@+id/startStream"
  203 + android:layout_width="wrap_content"
  204 + android:layout_height="wrap_content"
  205 + android:layout_centerInParent="true"
  206 + android:background="@drawable/anchor_retry_nor"
  207 + android:onClick="onClick"
  208 + android:padding="5dp"
  209 + android:text="开始直播"
  210 + android:textColor="@android:color/white"
  211 + android:visibility="@{isShow.startBtn ? 0 : 8}" />
  212 +
  213 + <RelativeLayout
  214 + android:id="@+id/retry_layout"
  215 + android:layout_width="wrap_content"
  216 + android:layout_height="wrap_content"
  217 + android:layout_centerInParent="true"
  218 + android:visibility="@{isShow.retryLayout ? 0 : 8}">
  219 +
  220 + <TextView
  221 + android:id="@+id/text_retry"
  222 + android:layout_width="wrap_content"
  223 + android:layout_height="wrap_content"
  224 + android:layout_centerHorizontal="true"
  225 + android:text="直播过程中出现了小问题"
  226 + android:textColor="@android:color/white"
  227 + android:textSize="18sp" />
  228 +
  229 + <TextView
  230 + android:id="@+id/auto_reconnect_msg"
  231 + android:layout_width="wrap_content"
  232 + android:layout_height="wrap_content"
  233 + android:layout_below="@+id/text_retry"
  234 + android:layout_centerHorizontal="true"
  235 + android:layout_marginTop="8dp"
  236 + android:text="@{isShow.autoRectTxt ? @string/reconnecting : @string/reconnect_done}"
  237 + android:textColor="#fb8640"
  238 + android:visibility="@{isShow.autoRecMsg ? 0 : 8}" />
  239 +
  240 + <TextView
  241 + android:id="@+id/button_retry"
  242 + android:layout_width="wrap_content"
  243 + android:layout_height="wrap_content"
  244 + android:layout_below="@+id/auto_reconnect_msg"
  245 + android:layout_centerHorizontal="true"
  246 + android:layout_marginTop="8dp"
  247 + android:background="@drawable/anchor_retry_nor"
  248 + android:onClick="onClick"
  249 + android:paddingBottom="8dp"
  250 + android:paddingLeft="15dp"
  251 + android:paddingRight="15dp"
  252 + android:paddingTop="8dp"
  253 + android:text="点击重试"
  254 + android:textColor="@android:color/white"
  255 + android:textSize="18sp" />
  256 +
  257 + </RelativeLayout>
  258 +
  259 + </RelativeLayout>
  260 +</layout>
0 261 \ No newline at end of file
... ...
app/src/main/res/layout/item_chat.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<layout xmlns:android="http://schemas.android.com/apk/res/android">
  3 +
  4 + <data>
  5 + <variable
  6 + name="chat"
  7 + type="com.cnlive.gundam.stream.model.ChatItem" />
  8 + </data>
  9 +
  10 + <RelativeLayout
  11 + android:layout_width="wrap_content"
  12 + android:layout_height="wrap_content"
  13 + android:paddingBottom="10dp"
  14 + android:paddingTop="10dp">
  15 +
  16 + <!--<ImageView-->
  17 + <!--android:id="@+id/chat_head"-->
  18 + <!--android:layout_width="43dp"-->
  19 + <!--android:layout_height="43dp"-->
  20 + <!--android:layout_centerVertical="true" />-->
  21 +
  22 + <TextView
  23 + android:id="@+id/chat_name"
  24 + android:layout_width="wrap_content"
  25 + android:layout_height="wrap_content"
  26 + android:layout_centerVertical="true"
  27 + android:layout_marginLeft="5dp"
  28 + android:text="@{chat.userName + @string/colon}"
  29 + android:textColor="#ff8800" />
  30 +
  31 + <TextView
  32 + android:id="@+id/chat_content"
  33 + android:layout_width="wrap_content"
  34 + android:layout_height="wrap_content"
  35 + android:layout_centerVertical="true"
  36 + android:layout_marginLeft="5dp"
  37 + android:layout_toRightOf="@+id/chat_name"
  38 + android:text="@{chat.content}"
  39 + android:textColor="@android:color/white" />
  40 +
  41 + </RelativeLayout>
  42 +
  43 +</layout>
0 44 \ No newline at end of file
... ...
app/src/main/res/values/strings.xml
... ... @@ -5,4 +5,11 @@
5 5 <string name="main_tab_live_tv">电视台</string>
6 6 <string name="main_tab_live">直播</string>
7 7 <string name="main_tab_mine">我的</string>
  8 +
  9 + <string name="reconnecting">正在尝试自动重连。。。</string>
  10 + <string name="reconnect_done">Oops 自动重连失败</string>
  11 + <string name="colon"> : </string>
  12 +
  13 + <!-- TODO: Remove or change this placeholder text -->
  14 + <string name="hello_blank_fragment">Hello blank fragment</string>
8 15 </resources>
... ...