├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── anpoz.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── playcode │ │ └── streambox │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── io │ │ │ └── playcode │ │ │ └── streambox │ │ │ ├── data │ │ │ ├── bean │ │ │ │ ├── BaseDanmu.kt │ │ │ │ ├── CommonStreamEntity.java │ │ │ │ ├── CommonStreamListEntity.java │ │ │ │ ├── PandaDanmuEntity.java │ │ │ │ ├── PandaStreamDanmuServerEntity.java │ │ │ │ ├── PandaStreamEntity.java │ │ │ │ ├── PandaStreamListEntity.java │ │ │ │ └── StreamInfoEntity.java │ │ │ └── source │ │ │ │ ├── API.java │ │ │ │ ├── AppRepository.java │ │ │ │ └── RetrofitHelper.java │ │ │ ├── event │ │ │ ├── PandaDanmuEvent.java │ │ │ └── StreamInfoEvent.java │ │ │ ├── ui │ │ │ ├── base │ │ │ │ ├── BasePresenter.java │ │ │ │ └── BaseView.java │ │ │ ├── chatroom │ │ │ │ ├── ChatroomContract.java │ │ │ │ ├── ChatroomFragment.java │ │ │ │ └── ChatroomPresenter.java │ │ │ ├── common │ │ │ │ ├── CommonCategoryFragment.java │ │ │ │ ├── CommonListContract.java │ │ │ │ ├── CommonListFragment.java │ │ │ │ └── CommonListPresenter.java │ │ │ ├── commonstream │ │ │ │ ├── CommonStreamActivity.java │ │ │ │ ├── CommonStreamContract.java │ │ │ │ └── CommonStreamPresenter.java │ │ │ ├── info │ │ │ │ ├── StreamInfoContract.java │ │ │ │ ├── StreamInfoFragment.java │ │ │ │ └── StreamInfoPresenter.java │ │ │ ├── main │ │ │ │ └── MainActivity.java │ │ │ ├── panda │ │ │ │ ├── PandaCategoryFragment.java │ │ │ │ ├── PandaListContract.java │ │ │ │ ├── PandaListFragment.java │ │ │ │ └── PandaListPresenter.java │ │ │ ├── pandastream │ │ │ │ ├── PandaStreamActivity.java │ │ │ │ ├── PandaStreamContract.java │ │ │ │ └── PandaStreamPresenter.java │ │ │ └── settings │ │ │ │ ├── SettingsActivity.java │ │ │ │ └── SettingsFragment.java │ │ │ ├── util │ │ │ ├── FormatUtil.java │ │ │ ├── ImageLoader.java │ │ │ └── PandaDanmuUtil.java │ │ │ └── widget │ │ │ └── DanmakuVideoPlayer.java │ └── res │ │ ├── drawable │ │ ├── bg_card_stream_info.xml │ │ ├── custom_enlarge.xml │ │ ├── custom_shrink.xml │ │ ├── ic_info_black_24dp.xml │ │ ├── ic_notifications_black_24dp.xml │ │ ├── ic_search_white_24dp.xml │ │ ├── ic_sync_black_24dp.xml │ │ ├── ic_visibility_grey_500_18dp.xml │ │ └── wrap_ic_visibility_grey_500_18dp.xml │ │ ├── layout │ │ ├── activity_common_stream.xml │ │ ├── activity_main.xml │ │ ├── activity_panda_stream.xml │ │ ├── activity_settings.xml │ │ ├── fragment_chatroom.xml │ │ ├── fragment_common_category.xml │ │ ├── fragment_common_list.xml │ │ ├── fragment_panda_category.xml │ │ ├── fragment_stream_info.xml │ │ ├── fragment_stream_list.xml │ │ ├── item_chat_list.xml │ │ ├── item_live_list.xml │ │ └── view_danmu_video.xml │ │ ├── menu │ │ ├── menu_bottom_navigation.xml │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── settings.xml │ └── test │ └── java │ └── io │ └── playcode │ └── streambox │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dictionaries/anpoz.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | danmaku 5 | danmu 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 21 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Android 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 54 | 55 | 56 | 57 | 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StreamBox(Deprecated) 2 | 因为偶然发现了有人已经做出了这种直播聚合类APP,而且做得要更优秀一些,所以本项目就没必要更新了~~~ 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 26 7 | buildToolsVersion "26.0.1" 8 | defaultConfig { 9 | applicationId "io.playcode.streambox" 10 | minSdkVersion 16 11 | targetSdkVersion 26 12 | versionCode 1 13 | versionName "0.0.1" 14 | vectorDrawables.useSupportLibrary = true 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | } 28 | 29 | dependencies { 30 | compile fileTree(include: ['*.jar'], dir: 'libs') 31 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 32 | exclude group: 'com.android.support', module: 'support-annotations' 33 | }) 34 | compile deps.appcompatv7 35 | compile deps.supportv4 36 | compile deps.recyclerviewv7 37 | compile deps.design 38 | compile deps.cardviewv7 39 | compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 40 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 41 | compile 'com.jakewharton:butterknife:8.5.1' 42 | compile 'com.blankj:alog:0.0.1' 43 | compile 'io.reactivex.rxjava2:rxjava:2.1.3' 44 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 45 | compile 'com.squareup.retrofit2:retrofit:2.3.0' 46 | compile 'com.squareup.retrofit2:converter-gson:2.3.0' 47 | compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0' 48 | compile 'org.byteam.superadapter:superadapter:3.6.7' 49 | compile 'com.github.bumptech.glide:glide:3.7.0' 50 | compile 'com.github.orangegangsters:swipy:1.2.3@aar' 51 | compile 'org.greenrobot:eventbus:3.0.0' 52 | compile 'com.shuyu:GSYVideoPlayer:1.6.4' 53 | compile 'com.android.support:support-vector-drawable:26.0.2' 54 | compile 'com.github.ctiao:DanmakuFlameMaster:0.8.3' 55 | compile 'com.github.ctiao:ndkbitmap-armv7a:0.8.3' 56 | testCompile 'junit:junit:4.12' 57 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' 58 | } 59 | repositories { 60 | mavenCentral() 61 | google() 62 | } 63 | -------------------------------------------------------------------------------- /app/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 H:\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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -keep class tv.danmaku.ijk.** { *; } 27 | -dontwarn tv.danmaku.ijk.** 28 | -keep class com.shuyu.gsyvideoplayer.** { *; } 29 | -dontwarn com.shuyu.gsyvideoplayer.** -------------------------------------------------------------------------------- /app/src/androidTest/java/io/playcode/streambox/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("io.playcode.streambox", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 31 | 32 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/bean/BaseDanmu.kt: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.bean 2 | 3 | /** 4 | * Created by anpoz on 2017/5/22. 5 | */ 6 | data class BaseDanmu(var text: String, 7 | var type: Int) -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/bean/CommonStreamEntity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.bean; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by anpoz on 2017/4/16. 7 | */ 8 | 9 | public class CommonStreamEntity { 10 | 11 | /** 12 | * msg : 13 | * result : {"enable":1,"game_type":"lol","is_followed":0,"live_id":"56040","live_img":"https://rpic.douyucdn.cn/a1704/16/09/56040_170416091651.jpg","live_name":"douyu","live_nickname":"主播油条","live_online":221390,"live_title":"油条:俄罗斯第一绝地求生玩家牛B条","live_type":"douyu","live_userimg":"","offline_time":"1492300808.3127","online_time":"1492305132.2509","push_time":"1492265401.9044","sort_num":221390,"stream_list":[{"type":"超清","url":"http://hdl3a.douyutv.com/live/56040rdE3glRTHoy.flv?wsSecret=4a7b08440f47198623e7cb6542b7438a&wsTime=1454396745"},{"type":"普清","url":"http://hdl3a.douyutv.com/live/56040rdE3glRTHoy_550.flv?wsSecret=8c8d7dd2f30ad206512327a84271f9f3&wsTime=1454396745"}]} 14 | * status : ok 15 | */ 16 | 17 | private String msg; 18 | private ResultEntity result; 19 | private String status; 20 | 21 | public String getMsg() { 22 | return msg; 23 | } 24 | 25 | public void setMsg(String msg) { 26 | this.msg = msg; 27 | } 28 | 29 | public ResultEntity getResult() { 30 | return result; 31 | } 32 | 33 | public void setResult(ResultEntity result) { 34 | this.result = result; 35 | } 36 | 37 | public String getStatus() { 38 | return status; 39 | } 40 | 41 | public void setStatus(String status) { 42 | this.status = status; 43 | } 44 | 45 | public static class ResultEntity { 46 | /** 47 | * enable : 1 48 | * game_type : lol 49 | * is_followed : 0 50 | * live_id : 56040 51 | * live_img : https://rpic.douyucdn.cn/a1704/16/09/56040_170416091651.jpg 52 | * live_name : douyu 53 | * live_nickname : 主播油条 54 | * live_online : 221390 55 | * live_title : 油条:俄罗斯第一绝地求生玩家牛B条 56 | * live_type : douyu 57 | * live_userimg : 58 | * offline_time : 1492300808.3127 59 | * online_time : 1492305132.2509 60 | * push_time : 1492265401.9044 61 | * sort_num : 221390 62 | * stream_list : [{"type":"超清","url":"http://hdl3a.douyutv.com/live/56040rdE3glRTHoy.flv?wsSecret=4a7b08440f47198623e7cb6542b7438a&wsTime=1454396745"},{"type":"普清","url":"http://hdl3a.douyutv.com/live/56040rdE3glRTHoy_550.flv?wsSecret=8c8d7dd2f30ad206512327a84271f9f3&wsTime=1454396745"}] 63 | */ 64 | 65 | private int enable; 66 | private String game_type; 67 | private int is_followed; 68 | private String live_id; 69 | private String live_img; 70 | private String live_name; 71 | private String live_nickname; 72 | private int live_online; 73 | private String live_title; 74 | private String live_type; 75 | private String live_userimg; 76 | private String offline_time; 77 | private String online_time; 78 | private String push_time; 79 | private int sort_num; 80 | private List stream_list; 81 | 82 | public int getEnable() { 83 | return enable; 84 | } 85 | 86 | public void setEnable(int enable) { 87 | this.enable = enable; 88 | } 89 | 90 | public String getGame_type() { 91 | return game_type; 92 | } 93 | 94 | public void setGame_type(String game_type) { 95 | this.game_type = game_type; 96 | } 97 | 98 | public int getIs_followed() { 99 | return is_followed; 100 | } 101 | 102 | public void setIs_followed(int is_followed) { 103 | this.is_followed = is_followed; 104 | } 105 | 106 | public String getLive_id() { 107 | return live_id; 108 | } 109 | 110 | public void setLive_id(String live_id) { 111 | this.live_id = live_id; 112 | } 113 | 114 | public String getLive_img() { 115 | return live_img; 116 | } 117 | 118 | public void setLive_img(String live_img) { 119 | this.live_img = live_img; 120 | } 121 | 122 | public String getLive_name() { 123 | return live_name; 124 | } 125 | 126 | public void setLive_name(String live_name) { 127 | this.live_name = live_name; 128 | } 129 | 130 | public String getLive_nickname() { 131 | return live_nickname; 132 | } 133 | 134 | public void setLive_nickname(String live_nickname) { 135 | this.live_nickname = live_nickname; 136 | } 137 | 138 | public int getLive_online() { 139 | return live_online; 140 | } 141 | 142 | public void setLive_online(int live_online) { 143 | this.live_online = live_online; 144 | } 145 | 146 | public String getLive_title() { 147 | return live_title; 148 | } 149 | 150 | public void setLive_title(String live_title) { 151 | this.live_title = live_title; 152 | } 153 | 154 | public String getLive_type() { 155 | return live_type; 156 | } 157 | 158 | public void setLive_type(String live_type) { 159 | this.live_type = live_type; 160 | } 161 | 162 | public String getLive_userimg() { 163 | return live_userimg; 164 | } 165 | 166 | public void setLive_userimg(String live_userimg) { 167 | this.live_userimg = live_userimg; 168 | } 169 | 170 | public String getOffline_time() { 171 | return offline_time; 172 | } 173 | 174 | public void setOffline_time(String offline_time) { 175 | this.offline_time = offline_time; 176 | } 177 | 178 | public String getOnline_time() { 179 | return online_time; 180 | } 181 | 182 | public void setOnline_time(String online_time) { 183 | this.online_time = online_time; 184 | } 185 | 186 | public String getPush_time() { 187 | return push_time; 188 | } 189 | 190 | public void setPush_time(String push_time) { 191 | this.push_time = push_time; 192 | } 193 | 194 | public int getSort_num() { 195 | return sort_num; 196 | } 197 | 198 | public void setSort_num(int sort_num) { 199 | this.sort_num = sort_num; 200 | } 201 | 202 | public List getStream_list() { 203 | return stream_list; 204 | } 205 | 206 | public void setStream_list(List stream_list) { 207 | this.stream_list = stream_list; 208 | } 209 | 210 | public static class StreamListEntity { 211 | /** 212 | * type : 超清 213 | * url : http://hdl3a.douyutv.com/live/56040rdE3glRTHoy.flv?wsSecret=4a7b08440f47198623e7cb6542b7438a&wsTime=1454396745 214 | */ 215 | 216 | private String type; 217 | private String url; 218 | 219 | public String getType() { 220 | return type; 221 | } 222 | 223 | public void setType(String type) { 224 | this.type = type; 225 | } 226 | 227 | public String getUrl() { 228 | return url; 229 | } 230 | 231 | public void setUrl(String url) { 232 | this.url = url; 233 | } 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/bean/PandaDanmuEntity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.bean; 2 | 3 | /** 4 | * Created by anpoz on 2017/4/15. 5 | */ 6 | 7 | public class PandaDanmuEntity { 8 | 9 | /** 10 | * type : 1 11 | * time : 1492968699 12 | * data : {"from":{"identity":"30","nickName":"0005丶vip打折卡","badge":"","rid":"40533958","msgcolor":"","level":"7","sp_identity":"0","__plat":"android","userName":""},"to":{"toroom":"415164"},"content":"666666666"} 13 | */ 14 | 15 | private String type; 16 | private int time; 17 | private DataEntity data; 18 | 19 | public String getType() { 20 | return type; 21 | } 22 | 23 | public void setType(String type) { 24 | this.type = type; 25 | } 26 | 27 | public int getTime() { 28 | return time; 29 | } 30 | 31 | public void setTime(int time) { 32 | this.time = time; 33 | } 34 | 35 | public DataEntity getData() { 36 | return data; 37 | } 38 | 39 | public void setData(DataEntity data) { 40 | this.data = data; 41 | } 42 | 43 | public static class DataEntity { 44 | /** 45 | * from : {"identity":"30","nickName":"0005丶vip打折卡","badge":"","rid":"40533958","msgcolor":"","level":"7","sp_identity":"0","__plat":"android","userName":""} 46 | * to : {"toroom":"415164"} 47 | * content : 666666666 48 | */ 49 | 50 | private FromEntity from; 51 | private ToEntity to; 52 | private String content; 53 | 54 | public FromEntity getFrom() { 55 | return from; 56 | } 57 | 58 | public void setFrom(FromEntity from) { 59 | this.from = from; 60 | } 61 | 62 | public ToEntity getTo() { 63 | return to; 64 | } 65 | 66 | public void setTo(ToEntity to) { 67 | this.to = to; 68 | } 69 | 70 | public String getContent() { 71 | return content; 72 | } 73 | 74 | public void setContent(String content) { 75 | this.content = content; 76 | } 77 | 78 | public static class FromEntity { 79 | /** 80 | * identity : 30 81 | * nickName : 0005丶vip打折卡 82 | * badge : 83 | * rid : 40533958 84 | * msgcolor : 85 | * level : 7 86 | * sp_identity : 0 87 | * __plat : android 88 | * userName : 89 | */ 90 | 91 | private String identity; 92 | private String nickName; 93 | private String badge; 94 | private String rid; 95 | private String msgcolor; 96 | private String level; 97 | private String sp_identity; 98 | private String __plat; 99 | private String userName; 100 | 101 | public String getIdentity() { 102 | return identity; 103 | } 104 | 105 | public void setIdentity(String identity) { 106 | this.identity = identity; 107 | } 108 | 109 | public String getNickName() { 110 | return nickName; 111 | } 112 | 113 | public void setNickName(String nickName) { 114 | this.nickName = nickName; 115 | } 116 | 117 | public String getBadge() { 118 | return badge; 119 | } 120 | 121 | public void setBadge(String badge) { 122 | this.badge = badge; 123 | } 124 | 125 | public String getRid() { 126 | return rid; 127 | } 128 | 129 | public void setRid(String rid) { 130 | this.rid = rid; 131 | } 132 | 133 | public String getMsgcolor() { 134 | return msgcolor; 135 | } 136 | 137 | public void setMsgcolor(String msgcolor) { 138 | this.msgcolor = msgcolor; 139 | } 140 | 141 | public String getLevel() { 142 | return level; 143 | } 144 | 145 | public void setLevel(String level) { 146 | this.level = level; 147 | } 148 | 149 | public String getSp_identity() { 150 | return sp_identity; 151 | } 152 | 153 | public void setSp_identity(String sp_identity) { 154 | this.sp_identity = sp_identity; 155 | } 156 | 157 | public String get__plat() { 158 | return __plat; 159 | } 160 | 161 | public void set__plat(String __plat) { 162 | this.__plat = __plat; 163 | } 164 | 165 | public String getUserName() { 166 | return userName; 167 | } 168 | 169 | public void setUserName(String userName) { 170 | this.userName = userName; 171 | } 172 | } 173 | 174 | public static class ToEntity { 175 | /** 176 | * toroom : 415164 177 | */ 178 | 179 | private String toroom; 180 | 181 | public String getToroom() { 182 | return toroom; 183 | } 184 | 185 | public void setToroom(String toroom) { 186 | this.toroom = toroom; 187 | } 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/bean/PandaStreamDanmuServerEntity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.bean; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by anpoz on 2017/4/14. 7 | */ 8 | 9 | public class PandaStreamDanmuServerEntity { 10 | 11 | /** 12 | * errno : 0 13 | * errmsg : 14 | * data : {"appid":"134224728","rid":3174618,"sign":"201effba6855f95a69098e7891a25abe","authType":"4","ts":1492139948000,"chat_addr_list":["115.159.247.243:3389","115.159.247.170:3389","115.159.247.231:3389"]} 15 | */ 16 | 17 | private int errno; 18 | private String errmsg; 19 | private DataEntity data; 20 | 21 | public int getErrno() { 22 | return errno; 23 | } 24 | 25 | public void setErrno(int errno) { 26 | this.errno = errno; 27 | } 28 | 29 | public String getErrmsg() { 30 | return errmsg; 31 | } 32 | 33 | public void setErrmsg(String errmsg) { 34 | this.errmsg = errmsg; 35 | } 36 | 37 | public DataEntity getData() { 38 | return data; 39 | } 40 | 41 | public void setData(DataEntity data) { 42 | this.data = data; 43 | } 44 | 45 | public static class DataEntity { 46 | /** 47 | * appid : 134224728 48 | * rid : 3174618 49 | * sign : 201effba6855f95a69098e7891a25abe 50 | * authType : 4 51 | * ts : 1492139948000 52 | * chat_addr_list : ["115.159.247.243:3389","115.159.247.170:3389","115.159.247.231:3389"] 53 | */ 54 | 55 | private String appid; 56 | private int rid; 57 | private String sign; 58 | private String authType; 59 | private long ts; 60 | private List chat_addr_list; 61 | 62 | public String getAppid() { 63 | return appid; 64 | } 65 | 66 | public void setAppid(String appid) { 67 | this.appid = appid; 68 | } 69 | 70 | public int getRid() { 71 | return rid; 72 | } 73 | 74 | public void setRid(int rid) { 75 | this.rid = rid; 76 | } 77 | 78 | public String getSign() { 79 | return sign; 80 | } 81 | 82 | public void setSign(String sign) { 83 | this.sign = sign; 84 | } 85 | 86 | public String getAuthType() { 87 | return authType; 88 | } 89 | 90 | public void setAuthType(String authType) { 91 | this.authType = authType; 92 | } 93 | 94 | public long getTs() { 95 | return ts; 96 | } 97 | 98 | public void setTs(long ts) { 99 | this.ts = ts; 100 | } 101 | 102 | public List getChat_addr_list() { 103 | return chat_addr_list; 104 | } 105 | 106 | public void setChat_addr_list(List chat_addr_list) { 107 | this.chat_addr_list = chat_addr_list; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/bean/PandaStreamEntity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.bean; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by anpoz on 2017/4/14. 7 | */ 8 | 9 | public class PandaStreamEntity { 10 | 11 | /** 12 | * errno : 0 13 | * errmsg : 14 | * data : {"info":{"hostinfo":{"rid":27636760,"name":"主播方正","avatar":"http://i8.pdim.gs/4cde730a75bb4a12287ed033793b7d98.jpg","bamboos":"12981781"},"roominfo":{"id":"769965","name":"北京工体滴滴","type":"1","classification":"户外直播","cate":"hwzb","bulletin":"水友qq2群605101608,2个龙虾上房管,房管Q私聊进微信群,微博正哥帅过十个吴彦祖,合作qq736320073","details":"","person_num":"49413","fans":"57192","pictures":{"img":"http://i6.pdim.gs/90/7a8bf37f7eff0977fc91062b6e03d5de/w338/h190.jpg"},"display_type":"1","start_time":"1492958978","end_time":"1492891053","room_type":"1","status":"2","style_type":"1","remind_content":"","remind_time":"0","remind_status":"0"},"userinfo":{"rid":0},"videoinfo":{"name":"dota","time":"14602","stream_addr":{"HD":"0","OD":"0","SD":"1"},"room_key":"370daaa1e9ec694600b3dfe962fcab04","plflag":"11_21","status":"2","sign":"38153f226814ed9969edd0419d0d30ee","ts":"&ts=58fcf80c&rid=-66778604","hardware":2,"scheme":"http","slaveflag":["2_4"],"watermark":"1"}}} 15 | * authseq : 16 | */ 17 | 18 | private int errno; 19 | private String errmsg; 20 | private DataEntity data; 21 | private String authseq; 22 | 23 | public int getErrno() { 24 | return errno; 25 | } 26 | 27 | public void setErrno(int errno) { 28 | this.errno = errno; 29 | } 30 | 31 | public String getErrmsg() { 32 | return errmsg; 33 | } 34 | 35 | public void setErrmsg(String errmsg) { 36 | this.errmsg = errmsg; 37 | } 38 | 39 | public DataEntity getData() { 40 | return data; 41 | } 42 | 43 | public void setData(DataEntity data) { 44 | this.data = data; 45 | } 46 | 47 | public String getAuthseq() { 48 | return authseq; 49 | } 50 | 51 | public void setAuthseq(String authseq) { 52 | this.authseq = authseq; 53 | } 54 | 55 | public static class DataEntity { 56 | /** 57 | * info : {"hostinfo":{"rid":27636760,"name":"主播方正","avatar":"http://i8.pdim.gs/4cde730a75bb4a12287ed033793b7d98.jpg","bamboos":"12981781"},"roominfo":{"id":"769965","name":"北京工体滴滴","type":"1","classification":"户外直播","cate":"hwzb","bulletin":"水友qq2群605101608,2个龙虾上房管,房管Q私聊进微信群,微博正哥帅过十个吴彦祖,合作qq736320073","details":"","person_num":"49413","fans":"57192","pictures":{"img":"http://i6.pdim.gs/90/7a8bf37f7eff0977fc91062b6e03d5de/w338/h190.jpg"},"display_type":"1","start_time":"1492958978","end_time":"1492891053","room_type":"1","status":"2","style_type":"1","remind_content":"","remind_time":"0","remind_status":"0"},"userinfo":{"rid":0},"videoinfo":{"name":"dota","time":"14602","stream_addr":{"HD":"0","OD":"0","SD":"1"},"room_key":"370daaa1e9ec694600b3dfe962fcab04","plflag":"11_21","status":"2","sign":"38153f226814ed9969edd0419d0d30ee","ts":"&ts=58fcf80c&rid=-66778604","hardware":2,"scheme":"http","slaveflag":["2_4"],"watermark":"1"}} 58 | */ 59 | 60 | private InfoEntity info; 61 | 62 | public InfoEntity getInfo() { 63 | return info; 64 | } 65 | 66 | public void setInfo(InfoEntity info) { 67 | this.info = info; 68 | } 69 | 70 | public static class InfoEntity { 71 | /** 72 | * hostinfo : {"rid":27636760,"name":"主播方正","avatar":"http://i8.pdim.gs/4cde730a75bb4a12287ed033793b7d98.jpg","bamboos":"12981781"} 73 | * roominfo : {"id":"769965","name":"北京工体滴滴","type":"1","classification":"户外直播","cate":"hwzb","bulletin":"水友qq2群605101608,2个龙虾上房管,房管Q私聊进微信群,微博正哥帅过十个吴彦祖,合作qq736320073","details":"","person_num":"49413","fans":"57192","pictures":{"img":"http://i6.pdim.gs/90/7a8bf37f7eff0977fc91062b6e03d5de/w338/h190.jpg"},"display_type":"1","start_time":"1492958978","end_time":"1492891053","room_type":"1","status":"2","style_type":"1","remind_content":"","remind_time":"0","remind_status":"0"} 74 | * userinfo : {"rid":0} 75 | * videoinfo : {"name":"dota","time":"14602","stream_addr":{"HD":"0","OD":"0","SD":"1"},"room_key":"370daaa1e9ec694600b3dfe962fcab04","plflag":"11_21","status":"2","sign":"38153f226814ed9969edd0419d0d30ee","ts":"&ts=58fcf80c&rid=-66778604","hardware":2,"scheme":"http","slaveflag":["2_4"],"watermark":"1"} 76 | */ 77 | 78 | private HostinfoEntity hostinfo; 79 | private RoominfoEntity roominfo; 80 | private UserinfoEntity userinfo; 81 | private VideoinfoEntity videoinfo; 82 | 83 | public HostinfoEntity getHostinfo() { 84 | return hostinfo; 85 | } 86 | 87 | public void setHostinfo(HostinfoEntity hostinfo) { 88 | this.hostinfo = hostinfo; 89 | } 90 | 91 | public RoominfoEntity getRoominfo() { 92 | return roominfo; 93 | } 94 | 95 | public void setRoominfo(RoominfoEntity roominfo) { 96 | this.roominfo = roominfo; 97 | } 98 | 99 | public UserinfoEntity getUserinfo() { 100 | return userinfo; 101 | } 102 | 103 | public void setUserinfo(UserinfoEntity userinfo) { 104 | this.userinfo = userinfo; 105 | } 106 | 107 | public VideoinfoEntity getVideoinfo() { 108 | return videoinfo; 109 | } 110 | 111 | public void setVideoinfo(VideoinfoEntity videoinfo) { 112 | this.videoinfo = videoinfo; 113 | } 114 | 115 | public static class HostinfoEntity { 116 | /** 117 | * rid : 27636760 118 | * name : 主播方正 119 | * avatar : http://i8.pdim.gs/4cde730a75bb4a12287ed033793b7d98.jpg 120 | * bamboos : 12981781 121 | */ 122 | 123 | private int rid; 124 | private String name; 125 | private String avatar; 126 | private String bamboos; 127 | 128 | public int getRid() { 129 | return rid; 130 | } 131 | 132 | public void setRid(int rid) { 133 | this.rid = rid; 134 | } 135 | 136 | public String getName() { 137 | return name; 138 | } 139 | 140 | public void setName(String name) { 141 | this.name = name; 142 | } 143 | 144 | public String getAvatar() { 145 | return avatar; 146 | } 147 | 148 | public void setAvatar(String avatar) { 149 | this.avatar = avatar; 150 | } 151 | 152 | public String getBamboos() { 153 | return bamboos; 154 | } 155 | 156 | public void setBamboos(String bamboos) { 157 | this.bamboos = bamboos; 158 | } 159 | } 160 | 161 | public static class RoominfoEntity { 162 | /** 163 | * id : 769965 164 | * name : 北京工体滴滴 165 | * type : 1 166 | * classification : 户外直播 167 | * cate : hwzb 168 | * bulletin : 水友qq2群605101608,2个龙虾上房管,房管Q私聊进微信群,微博正哥帅过十个吴彦祖,合作qq736320073 169 | * details : 170 | * person_num : 49413 171 | * fans : 57192 172 | * pictures : {"img":"http://i6.pdim.gs/90/7a8bf37f7eff0977fc91062b6e03d5de/w338/h190.jpg"} 173 | * display_type : 1 174 | * start_time : 1492958978 175 | * end_time : 1492891053 176 | * room_type : 1 177 | * status : 2 178 | * style_type : 1 179 | * remind_content : 180 | * remind_time : 0 181 | * remind_status : 0 182 | */ 183 | 184 | private String id; 185 | private String name; 186 | private String type; 187 | private String classification; 188 | private String cate; 189 | private String bulletin; 190 | private String details; 191 | private String person_num; 192 | private String fans; 193 | private PicturesEntity pictures; 194 | private String display_type; 195 | private String start_time; 196 | private String end_time; 197 | private String room_type; 198 | private String status; 199 | private String style_type; 200 | private String remind_content; 201 | private String remind_time; 202 | private String remind_status; 203 | 204 | public String getId() { 205 | return id; 206 | } 207 | 208 | public void setId(String id) { 209 | this.id = id; 210 | } 211 | 212 | public String getName() { 213 | return name; 214 | } 215 | 216 | public void setName(String name) { 217 | this.name = name; 218 | } 219 | 220 | public String getType() { 221 | return type; 222 | } 223 | 224 | public void setType(String type) { 225 | this.type = type; 226 | } 227 | 228 | public String getClassification() { 229 | return classification; 230 | } 231 | 232 | public void setClassification(String classification) { 233 | this.classification = classification; 234 | } 235 | 236 | public String getCate() { 237 | return cate; 238 | } 239 | 240 | public void setCate(String cate) { 241 | this.cate = cate; 242 | } 243 | 244 | public String getBulletin() { 245 | return bulletin; 246 | } 247 | 248 | public void setBulletin(String bulletin) { 249 | this.bulletin = bulletin; 250 | } 251 | 252 | public String getDetails() { 253 | return details; 254 | } 255 | 256 | public void setDetails(String details) { 257 | this.details = details; 258 | } 259 | 260 | public String getPerson_num() { 261 | return person_num; 262 | } 263 | 264 | public void setPerson_num(String person_num) { 265 | this.person_num = person_num; 266 | } 267 | 268 | public String getFans() { 269 | return fans; 270 | } 271 | 272 | public void setFans(String fans) { 273 | this.fans = fans; 274 | } 275 | 276 | public PicturesEntity getPictures() { 277 | return pictures; 278 | } 279 | 280 | public void setPictures(PicturesEntity pictures) { 281 | this.pictures = pictures; 282 | } 283 | 284 | public String getDisplay_type() { 285 | return display_type; 286 | } 287 | 288 | public void setDisplay_type(String display_type) { 289 | this.display_type = display_type; 290 | } 291 | 292 | public String getStart_time() { 293 | return start_time; 294 | } 295 | 296 | public void setStart_time(String start_time) { 297 | this.start_time = start_time; 298 | } 299 | 300 | public String getEnd_time() { 301 | return end_time; 302 | } 303 | 304 | public void setEnd_time(String end_time) { 305 | this.end_time = end_time; 306 | } 307 | 308 | public String getRoom_type() { 309 | return room_type; 310 | } 311 | 312 | public void setRoom_type(String room_type) { 313 | this.room_type = room_type; 314 | } 315 | 316 | public String getStatus() { 317 | return status; 318 | } 319 | 320 | public void setStatus(String status) { 321 | this.status = status; 322 | } 323 | 324 | public String getStyle_type() { 325 | return style_type; 326 | } 327 | 328 | public void setStyle_type(String style_type) { 329 | this.style_type = style_type; 330 | } 331 | 332 | public String getRemind_content() { 333 | return remind_content; 334 | } 335 | 336 | public void setRemind_content(String remind_content) { 337 | this.remind_content = remind_content; 338 | } 339 | 340 | public String getRemind_time() { 341 | return remind_time; 342 | } 343 | 344 | public void setRemind_time(String remind_time) { 345 | this.remind_time = remind_time; 346 | } 347 | 348 | public String getRemind_status() { 349 | return remind_status; 350 | } 351 | 352 | public void setRemind_status(String remind_status) { 353 | this.remind_status = remind_status; 354 | } 355 | 356 | public static class PicturesEntity { 357 | /** 358 | * img : http://i6.pdim.gs/90/7a8bf37f7eff0977fc91062b6e03d5de/w338/h190.jpg 359 | */ 360 | 361 | private String img; 362 | 363 | public String getImg() { 364 | return img; 365 | } 366 | 367 | public void setImg(String img) { 368 | this.img = img; 369 | } 370 | } 371 | } 372 | 373 | public static class UserinfoEntity { 374 | /** 375 | * rid : 0 376 | */ 377 | 378 | private int rid; 379 | 380 | public int getRid() { 381 | return rid; 382 | } 383 | 384 | public void setRid(int rid) { 385 | this.rid = rid; 386 | } 387 | } 388 | 389 | public static class VideoinfoEntity { 390 | /** 391 | * name : dota 392 | * time : 14602 393 | * stream_addr : {"HD":"0","OD":"0","SD":"1"} 394 | * room_key : 370daaa1e9ec694600b3dfe962fcab04 395 | * plflag : 11_21 396 | * status : 2 397 | * sign : 38153f226814ed9969edd0419d0d30ee 398 | * ts : &ts=58fcf80c&rid=-66778604 399 | * hardware : 2 400 | * scheme : http 401 | * slaveflag : ["2_4"] 402 | * watermark : 1 403 | */ 404 | 405 | private String name; 406 | private String time; 407 | private StreamAddrEntity stream_addr; 408 | private String room_key; 409 | private String plflag; 410 | private String status; 411 | private String sign; 412 | private String ts; 413 | private int hardware; 414 | private String scheme; 415 | private String watermark; 416 | private List slaveflag; 417 | 418 | public String getName() { 419 | return name; 420 | } 421 | 422 | public void setName(String name) { 423 | this.name = name; 424 | } 425 | 426 | public String getTime() { 427 | return time; 428 | } 429 | 430 | public void setTime(String time) { 431 | this.time = time; 432 | } 433 | 434 | public StreamAddrEntity getStream_addr() { 435 | return stream_addr; 436 | } 437 | 438 | public void setStream_addr(StreamAddrEntity stream_addr) { 439 | this.stream_addr = stream_addr; 440 | } 441 | 442 | public String getRoom_key() { 443 | return room_key; 444 | } 445 | 446 | public void setRoom_key(String room_key) { 447 | this.room_key = room_key; 448 | } 449 | 450 | public String getPlflag() { 451 | return plflag; 452 | } 453 | 454 | public void setPlflag(String plflag) { 455 | this.plflag = plflag; 456 | } 457 | 458 | public String getStatus() { 459 | return status; 460 | } 461 | 462 | public void setStatus(String status) { 463 | this.status = status; 464 | } 465 | 466 | public String getSign() { 467 | return sign; 468 | } 469 | 470 | public void setSign(String sign) { 471 | this.sign = sign; 472 | } 473 | 474 | public String getTs() { 475 | return ts; 476 | } 477 | 478 | public void setTs(String ts) { 479 | this.ts = ts; 480 | } 481 | 482 | public int getHardware() { 483 | return hardware; 484 | } 485 | 486 | public void setHardware(int hardware) { 487 | this.hardware = hardware; 488 | } 489 | 490 | public String getScheme() { 491 | return scheme; 492 | } 493 | 494 | public void setScheme(String scheme) { 495 | this.scheme = scheme; 496 | } 497 | 498 | public String getWatermark() { 499 | return watermark; 500 | } 501 | 502 | public void setWatermark(String watermark) { 503 | this.watermark = watermark; 504 | } 505 | 506 | public List getSlaveflag() { 507 | return slaveflag; 508 | } 509 | 510 | public void setSlaveflag(List slaveflag) { 511 | this.slaveflag = slaveflag; 512 | } 513 | 514 | public static class StreamAddrEntity { 515 | /** 516 | * HD : 0 517 | * OD : 0 518 | * SD : 1 519 | */ 520 | 521 | private String HD; 522 | private String OD; 523 | private String SD; 524 | 525 | public String getHD() { 526 | return HD; 527 | } 528 | 529 | public void setHD(String HD) { 530 | this.HD = HD; 531 | } 532 | 533 | public String getOD() { 534 | return OD; 535 | } 536 | 537 | public void setOD(String OD) { 538 | this.OD = OD; 539 | } 540 | 541 | public String getSD() { 542 | return SD; 543 | } 544 | 545 | public void setSD(String SD) { 546 | this.SD = SD; 547 | } 548 | } 549 | } 550 | } 551 | } 552 | } 553 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/bean/StreamInfoEntity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.bean; 2 | 3 | /** 4 | * Created by anpoz on 2017/4/16. 5 | */ 6 | 7 | public class StreamInfoEntity { 8 | private String live_id; 9 | private String live_img; 10 | private String live_nickname; 11 | private String live_online; 12 | private String live_title; 13 | private String live_type; 14 | private String push_time; 15 | 16 | public String getLive_id() { 17 | return live_id; 18 | } 19 | 20 | public void setLive_id(String live_id) { 21 | this.live_id = live_id; 22 | } 23 | 24 | public String getLive_img() { 25 | return live_img; 26 | } 27 | 28 | public void setLive_img(String live_img) { 29 | this.live_img = live_img; 30 | } 31 | 32 | public String getLive_nickname() { 33 | return live_nickname; 34 | } 35 | 36 | public void setLive_nickname(String live_nickname) { 37 | this.live_nickname = live_nickname; 38 | } 39 | 40 | public String getLive_online() { 41 | return live_online; 42 | } 43 | 44 | public void setLive_online(String live_online) { 45 | this.live_online = live_online; 46 | } 47 | 48 | public String getLive_title() { 49 | return live_title; 50 | } 51 | 52 | public void setLive_title(String live_title) { 53 | this.live_title = live_title; 54 | } 55 | 56 | public String getLive_type() { 57 | return live_type; 58 | } 59 | 60 | public void setLive_type(String live_type) { 61 | this.live_type = live_type; 62 | } 63 | 64 | public String getPush_time() { 65 | return push_time; 66 | } 67 | 68 | public void setPush_time(String push_time) { 69 | this.push_time = push_time; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/source/API.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.source; 2 | 3 | import io.playcode.streambox.data.bean.CommonStreamEntity; 4 | import io.playcode.streambox.data.bean.CommonStreamListEntity; 5 | import io.playcode.streambox.data.bean.PandaStreamDanmuServerEntity; 6 | import io.playcode.streambox.data.bean.PandaStreamListEntity; 7 | import io.playcode.streambox.data.bean.PandaStreamEntity; 8 | import io.reactivex.Observable; 9 | import retrofit2.http.Field; 10 | import retrofit2.http.FormUrlEncoded; 11 | import retrofit2.http.GET; 12 | import retrofit2.http.POST; 13 | import retrofit2.http.Query; 14 | 15 | /** 16 | * Created by anpoz on 2017/4/13. 17 | */ 18 | 19 | public interface API { 20 | //http://api.m.panda.tv/ajax_get_live_list_by_cate?cate=war3&pageno=1&pagenum=20&sproom=1&banner=1&slider=1&__version=3.0.6.3203&__plat=android&__channel=wandoujia 21 | @GET("ajax_get_live_list_by_cate?pageno=1&pagenum=20&sproom=1&banner=1&slider=1&__version=3.0.6.3203&__plat=android&__channel=wandoujia") 22 | Observable getPandaStreamList(@Query("cate") String category, 23 | @Query("pageno") String pageno, 24 | @Query("pagenum") String pagenum); 25 | 26 | //http://room.api.m.panda.tv/index.php?method=room.shareapi&roomid=7000 27 | @GET("index.php") 28 | Observable getPandaStreamRoom(@Query("method") String method, 29 | @Query("roomid") String roomid); 30 | 31 | //http://api.m.panda.tv/ajax_get_liveroom_baseinfo?roomid=769965&slaveflag=1&type=json&__version=3.0.6.3203&__plat=android&__channel=wandoujia 32 | @GET("ajax_get_liveroom_baseinfo?slaveflag=1&type=json&__version=3.0.6.3203&__plat=android&__channel=wandoujia") 33 | Observable getPandaStreamRoomNewApi(@Query("roomid") String roomid); 34 | 35 | //http://riven.panda.tv/chatroom/getinfo?roomid=7000 36 | @GET("chatroom/getinfo") 37 | Observable getDanmuServer(@Query("roomid") String id); 38 | 39 | //http://api.maxjia.com/api/live/list/?offset=0&limit=20&live_type=douyu&game_type=lol 40 | @GET("api/live/list/") 41 | Observable getCommonStreamList(@Query("offset") int offset, 42 | @Query("limit") String limit, 43 | @Query("live_type") String live_type, 44 | @Query("game_type") String game_type); 45 | 46 | //http://api.maxjia.com/api/live/detail/?live_type=douyu&live_id=56040&game_type=lol 47 | @GET("/api/live/detail/") 48 | Observable getCommonStream(@Query("live_type") String live_type, 49 | @Query("live_id") String live_id, 50 | @Query("game_type") String game_type); 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/source/AppRepository.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.source; 2 | 3 | import io.playcode.streambox.data.bean.CommonStreamEntity; 4 | import io.playcode.streambox.data.bean.CommonStreamListEntity; 5 | import io.playcode.streambox.data.bean.PandaStreamDanmuServerEntity; 6 | import io.playcode.streambox.data.bean.PandaStreamListEntity; 7 | import io.playcode.streambox.data.bean.PandaStreamEntity; 8 | import io.reactivex.Observable; 9 | 10 | /** 11 | * Created by anpoz on 2017/4/13. 12 | */ 13 | 14 | public class AppRepository { 15 | private static AppRepository mAppRepository; 16 | private static final String PANDA_LIST_BASE_URL = "http://api.m.panda.tv/"; 17 | private static final String PANDA_ROOM_BASE_URL = "http://room.api.m.panda.tv/"; 18 | private static final String PANDA_RIVEN_BASE_URL = "http://riven.panda.tv/"; 19 | private static final String LIVE_BASE_URL = "http://api.maxjia.com/"; 20 | private static final String PAGE_LIMIT = "20"; 21 | 22 | private AppRepository() { 23 | 24 | } 25 | 26 | public static AppRepository getInstance() { 27 | if (mAppRepository == null) { 28 | synchronized (AppRepository.class) { 29 | if (mAppRepository == null) { 30 | mAppRepository = new AppRepository(); 31 | } 32 | } 33 | } 34 | return mAppRepository; 35 | } 36 | 37 | public Observable getPandaStreamList(String category, String pageno) { 38 | return RetrofitHelper.getInstance() 39 | .configRetrofit(API.class, PANDA_LIST_BASE_URL) 40 | .getPandaStreamList(category, pageno, PAGE_LIMIT); 41 | } 42 | 43 | public Observable getPandaStreamRoom(String roomid) { 44 | return RetrofitHelper.getInstance() 45 | .configRetrofit(API.class, PANDA_ROOM_BASE_URL) 46 | .getPandaStreamRoom("room.shareapi", roomid); 47 | } 48 | 49 | public Observable getPandaStreamRoomNewApi(String roomid) { 50 | return RetrofitHelper.getInstance() 51 | .configRetrofit(API.class, PANDA_LIST_BASE_URL) 52 | .getPandaStreamRoomNewApi(roomid); 53 | } 54 | 55 | public Observable getPandaDanmuServer(String roomid) { 56 | return RetrofitHelper.getInstance() 57 | .configRetrofit(API.class, PANDA_RIVEN_BASE_URL) 58 | .getDanmuServer(roomid); 59 | } 60 | 61 | public Observable getCommonStreamList(int offset, 62 | String live_type, 63 | String game_type) { 64 | return RetrofitHelper.getInstance() 65 | .configRetrofit(API.class, LIVE_BASE_URL) 66 | .getCommonStreamList(offset, PAGE_LIMIT, live_type, game_type); 67 | } 68 | 69 | public Observable getCommonStreamDetail(String live_type, 70 | String live_id, 71 | String game_type) { 72 | return RetrofitHelper.getInstance() 73 | .configRetrofit(API.class, LIVE_BASE_URL) 74 | .getCommonStream(live_type, live_id, game_type); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/data/source/RetrofitHelper.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.data.source; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import okhttp3.OkHttpClient; 6 | import retrofit2.Retrofit; 7 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 8 | import retrofit2.converter.gson.GsonConverterFactory; 9 | 10 | /** 11 | * Created by anpoz on 2017/4/13. 12 | */ 13 | 14 | public class RetrofitHelper { 15 | private static final int DEFAULT_TIMEOUT = 5; 16 | 17 | private static RetrofitHelper retrofitHelper; 18 | 19 | public static RetrofitHelper getInstance() { 20 | if (retrofitHelper == null) { 21 | synchronized (RetrofitHelper.class) { 22 | if (retrofitHelper == null) { 23 | retrofitHelper = new RetrofitHelper(); 24 | } 25 | } 26 | } 27 | return retrofitHelper; 28 | } 29 | 30 | public T configRetrofit(Class service, String baseUrl) { 31 | Retrofit retrofit = new Retrofit.Builder() 32 | .baseUrl(baseUrl) 33 | .client(configClient()) 34 | .addConverterFactory(GsonConverterFactory.create()) 35 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 36 | .build(); 37 | return retrofit.create(service); 38 | } 39 | 40 | private OkHttpClient configClient() { 41 | OkHttpClient.Builder builder = new OkHttpClient.Builder() 42 | .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) 43 | .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); 44 | return builder.build(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/event/PandaDanmuEvent.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.event; 2 | 3 | import io.playcode.streambox.data.bean.PandaDanmuEntity; 4 | 5 | /** 6 | * Created by anpoz on 2017/4/15. 7 | */ 8 | 9 | public class PandaDanmuEvent { 10 | private PandaDanmuEntity mPandaDanmuEntity; 11 | private String live_id; 12 | 13 | public PandaDanmuEvent(PandaDanmuEntity pandaDanmuEntity, String live_id) { 14 | mPandaDanmuEntity = pandaDanmuEntity; 15 | this.live_id = live_id; 16 | } 17 | 18 | public PandaDanmuEntity getPandaDanmuEntity() { 19 | return mPandaDanmuEntity; 20 | } 21 | 22 | public void setPandaDanmuEntity(PandaDanmuEntity pandaDanmuEntity) { 23 | mPandaDanmuEntity = pandaDanmuEntity; 24 | } 25 | 26 | public String getLive_id() { 27 | return live_id; 28 | } 29 | 30 | public void setLive_id(String live_id) { 31 | this.live_id = live_id; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/event/StreamInfoEvent.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.event; 2 | 3 | import io.playcode.streambox.data.bean.StreamInfoEntity; 4 | 5 | /** 6 | * Created by anpoz on 2017/4/16. 7 | */ 8 | 9 | public class StreamInfoEvent { 10 | private StreamInfoEntity mStreamInfoEntity; 11 | 12 | public StreamInfoEvent(StreamInfoEntity streamInfoEntity) { 13 | mStreamInfoEntity = streamInfoEntity; 14 | } 15 | 16 | public StreamInfoEntity getStreamInfoEntity() { 17 | return mStreamInfoEntity; 18 | } 19 | 20 | public void setStreamInfoEntity(StreamInfoEntity streamInfoEntity) { 21 | mStreamInfoEntity = streamInfoEntity; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.base; 2 | 3 | /** 4 | * Created by anpoz on 2017/4/11. 5 | */ 6 | 7 | public interface BasePresenter { 8 | void subscribe(); 9 | 10 | void unSubscribe(); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/base/BaseView.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.base; 2 | 3 | /** 4 | * Created by anpoz on 2017/4/11. 5 | */ 6 | 7 | public interface BaseView { 8 | void setPresenter(T presenter); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/chatroom/ChatroomContract.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.chatroom; 2 | 3 | import java.util.List; 4 | 5 | import io.playcode.streambox.ui.base.BasePresenter; 6 | import io.playcode.streambox.ui.base.BaseView; 7 | 8 | /** 9 | * Created by anpoz on 2017/4/15. 10 | */ 11 | 12 | public interface ChatroomContract { 13 | interface Presenter extends BasePresenter { 14 | 15 | } 16 | 17 | interface View extends BaseView { 18 | void addDanmu(); 19 | void addCommit(List charSequenceList); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/chatroom/ChatroomFragment.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.chatroom; 2 | 3 | 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.annotation.LayoutRes; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import org.byteam.superadapter.SuperAdapter; 15 | import org.byteam.superadapter.SuperViewHolder; 16 | 17 | import java.util.List; 18 | 19 | import butterknife.BindView; 20 | import butterknife.ButterKnife; 21 | import butterknife.Unbinder; 22 | import io.playcode.streambox.R; 23 | 24 | /** 25 | * A simple {@link Fragment} subclass. 26 | */ 27 | public class ChatroomFragment extends Fragment implements ChatroomContract.View { 28 | 29 | 30 | @BindView(R.id.rv_panda_chatroom) 31 | RecyclerView mRvPandaChatroom; 32 | Unbinder unbinder; 33 | 34 | private ChatroomContract.Presenter mPresenter; 35 | 36 | public ChatroomFragment() { 37 | // Required empty public constructor 38 | } 39 | 40 | 41 | @Override 42 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 43 | Bundle savedInstanceState) { 44 | // Inflate the layout for this fragment 45 | View view = inflater.inflate(R.layout.fragment_chatroom, container, false); 46 | unbinder = ButterKnife.bind(this, view); 47 | new ChatroomPresenter(this); 48 | mPresenter.subscribe(); 49 | 50 | //init 51 | mRvPandaChatroom.setHasFixedSize(true); 52 | mRvPandaChatroom.setLayoutManager(new LinearLayoutManager(getContext())); 53 | 54 | return view; 55 | } 56 | 57 | @Override 58 | public void onDestroyView() { 59 | mPresenter.unSubscribe(); 60 | super.onDestroyView(); 61 | unbinder.unbind(); 62 | } 63 | 64 | @Override 65 | public void setPresenter(ChatroomContract.Presenter presenter) { 66 | mPresenter = presenter; 67 | } 68 | 69 | @Override 70 | public void addDanmu() { 71 | 72 | } 73 | 74 | @Override 75 | public void addCommit(List charSequenceList) { 76 | if (getContext() != null) { 77 | mRvPandaChatroom.setAdapter(new ListAdapter(getContext(), charSequenceList, R.layout.item_chat_list)); 78 | mRvPandaChatroom.scrollToPosition(charSequenceList.size() - 1); 79 | } 80 | } 81 | 82 | private static class ListAdapter extends SuperAdapter { 83 | 84 | public ListAdapter(Context context, List items, @LayoutRes int layoutResId) { 85 | super(context, items, layoutResId); 86 | } 87 | 88 | @Override 89 | public void onBind(SuperViewHolder holder, int viewType, int layoutPosition, CharSequence item) { 90 | holder.setText(R.id.tv_commit, item); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/chatroom/ChatroomPresenter.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.chatroom; 2 | 3 | import android.graphics.Color; 4 | import android.text.Spannable; 5 | import android.text.SpannableStringBuilder; 6 | import android.text.Spanned; 7 | import android.text.TextUtils; 8 | import android.text.style.ForegroundColorSpan; 9 | 10 | import com.blankj.aloglibrary.ALog; 11 | 12 | import org.greenrobot.eventbus.EventBus; 13 | import org.greenrobot.eventbus.Subscribe; 14 | import org.greenrobot.eventbus.ThreadMode; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | import io.playcode.streambox.data.bean.PandaDanmuEntity; 20 | import io.playcode.streambox.event.PandaDanmuEvent; 21 | import io.playcode.streambox.event.StreamInfoEvent; 22 | 23 | /** 24 | * Created by anpoz on 2017/4/15. 25 | */ 26 | 27 | public class ChatroomPresenter implements ChatroomContract.Presenter { 28 | private ChatroomContract.View mView; 29 | private List mCharSequenceList; 30 | private final ForegroundColorSpan mSpanRoleSupervisor;//超管 31 | private final ForegroundColorSpan mSpanRoleManager;//房管 32 | private final ForegroundColorSpan mSpanRoleAnchor;//主播 33 | private final ForegroundColorSpan mSpanNickname;//名称 34 | private final ForegroundColorSpan mSpanBamboo;//名称 35 | private final ForegroundColorSpan mSpanDanmu;//弹幕 36 | 37 | private String live_id; 38 | 39 | private String last_content; 40 | private static final String DANMU_TYPE = "1"; 41 | private static final String BAMBOO_TYPE = "206"; 42 | private static final String AUDIENCE_TYPE = "207"; 43 | private static final String TU_HAO_TYPE = "306"; 44 | private static final String MANAGER = "60"; 45 | private static final String SP_MANAGER = "120"; 46 | private static final String HOSTER = "90"; 47 | 48 | public ChatroomPresenter(ChatroomContract.View view) { 49 | mView = view; 50 | mView.setPresenter(this); 51 | mCharSequenceList = new ArrayList<>(); 52 | 53 | mSpanRoleSupervisor = new ForegroundColorSpan(Color.parseColor("#E57373")); 54 | mSpanRoleManager = new ForegroundColorSpan(Color.parseColor("#FF8A65")); 55 | mSpanRoleAnchor = new ForegroundColorSpan(Color.parseColor("#F06292")); 56 | mSpanNickname = new ForegroundColorSpan(Color.parseColor("#607D8B")); 57 | mSpanBamboo = new ForegroundColorSpan(Color.parseColor("#BA68C8")); 58 | mSpanDanmu = new ForegroundColorSpan(Color.parseColor("#424242")); 59 | } 60 | 61 | @Override 62 | public void subscribe() { 63 | if (!EventBus.getDefault().isRegistered(this)) 64 | EventBus.getDefault().register(this); 65 | } 66 | 67 | @Override 68 | public void unSubscribe() { 69 | EventBus.getDefault().unregister(this); 70 | } 71 | 72 | @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) 73 | public void onStreamInfoEvent(StreamInfoEvent event) { 74 | live_id = event.getStreamInfoEntity().getLive_id(); 75 | } 76 | 77 | @Subscribe(threadMode = ThreadMode.MAIN) 78 | public void onPandaDanmuEvent(PandaDanmuEvent pandaDanmuEvent) { 79 | if (TextUtils.equals(pandaDanmuEvent.getPandaDanmuEntity().getData().getContent(), last_content) && 80 | TextUtils.equals(pandaDanmuEvent.getPandaDanmuEntity().getType(), DANMU_TYPE)) { 81 | return; 82 | } 83 | last_content = pandaDanmuEvent.getPandaDanmuEntity().getData().getContent(); 84 | 85 | //避免串房 86 | if (!TextUtils.equals(live_id, pandaDanmuEvent.getLive_id())) { 87 | return; 88 | } 89 | 90 | PandaDanmuEntity danmu = pandaDanmuEvent.getPandaDanmuEntity(); 91 | String nickname = danmu.getData().getFrom().getNickName(); 92 | String identity = danmu.getData().getFrom().getIdentity(); 93 | String content = danmu.getData().getContent(); 94 | SpannableStringBuilder builder = new SpannableStringBuilder(); 95 | if (TextUtils.equals(danmu.getType(), DANMU_TYPE)) {//弹幕 96 | if (TextUtils.equals(danmu.getData().getFrom().getSp_identity(), SP_MANAGER)) {//超管 97 | builder.append("[超管]"); 98 | builder.append(nickname); 99 | builder.append(":"); 100 | builder.append(content); 101 | builder.setSpan(mSpanRoleSupervisor, 0, 4, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 102 | builder.setSpan(mSpanNickname, 4, nickname.length() + 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 103 | builder.setSpan(mSpanDanmu, nickname.length() + 4, 104 | nickname.length() + 4 + content.length() + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 105 | mCharSequenceList.add(builder); 106 | } else if (TextUtils.equals(identity, MANAGER)) {//房管 107 | builder.append("[房管]"); 108 | builder.append(nickname); 109 | builder.append(":"); 110 | builder.append(content); 111 | builder.setSpan(mSpanRoleManager, 0, 4, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 112 | builder.setSpan(mSpanNickname, 4, nickname.length() + 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 113 | builder.setSpan(mSpanDanmu, nickname.length() + 4, 114 | nickname.length() + 4 + content.length() + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 115 | mCharSequenceList.add(builder); 116 | } else if (TextUtils.equals(identity, HOSTER)) {//主播 117 | builder.append("[主播]"); 118 | builder.append(nickname); 119 | builder.append(":"); 120 | builder.append(content); 121 | builder.setSpan(mSpanRoleAnchor, 0, 4, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 122 | builder.setSpan(mSpanNickname, 4, nickname.length() + 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 123 | builder.setSpan(mSpanDanmu, nickname.length() + 4, 124 | nickname.length() + 4 + content.length() + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 125 | mCharSequenceList.add(builder); 126 | } else { 127 | builder.append(nickname); 128 | builder.append(":"); 129 | builder.append(content); 130 | builder.setSpan(mSpanNickname, 0, nickname.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 131 | builder.setSpan(mSpanDanmu, nickname.length(), 132 | nickname.length() + content.length() + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 133 | mCharSequenceList.add(builder); 134 | } 135 | } else if (TextUtils.equals(danmu.getType(), BAMBOO_TYPE)) {//竹子 136 | builder.append(nickname); 137 | builder.append("赠送给主播【"); 138 | builder.append(content); 139 | builder.append("】个竹子"); 140 | builder.setSpan(mSpanNickname, 0, nickname.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 141 | builder.setSpan(mSpanBamboo, nickname.length() + 6, content.length() + nickname.length() + 6, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 142 | mCharSequenceList.add(builder); 143 | } else if (TextUtils.equals(danmu.getType(), TU_HAO_TYPE)) {//礼物 144 | ALog.d(nickname + "送了一波礼物"); 145 | } else if (TextUtils.equals(danmu.getType(), AUDIENCE_TYPE)) {//人数更新 146 | builder.append("实时人数更新["); 147 | builder.append(content); 148 | builder.append("]人"); 149 | builder.setSpan(mSpanNickname, 7, content.length() + 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 150 | mCharSequenceList.add(builder); 151 | } 152 | mView.addCommit(mCharSequenceList); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/common/CommonCategoryFragment.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.common; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.TabLayout; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.app.FragmentManager; 7 | import android.support.v4.app.FragmentPagerAdapter; 8 | import android.support.v4.view.ViewPager; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import butterknife.BindView; 17 | import butterknife.ButterKnife; 18 | import butterknife.Unbinder; 19 | import io.playcode.streambox.R; 20 | import io.playcode.streambox.ui.panda.PandaCategoryFragment; 21 | import io.playcode.streambox.ui.panda.PandaListFragment; 22 | 23 | public class CommonCategoryFragment extends Fragment { 24 | private static final String LIVE_TYPE = "live type"; 25 | @BindView(R.id.tab_cate) 26 | TabLayout mTabCate; 27 | @BindView(R.id.vp_cate) 28 | ViewPager mVpCate; 29 | Unbinder unbinder; 30 | private String liveType; 31 | 32 | public CommonCategoryFragment() { 33 | // Required empty public constructor 34 | } 35 | 36 | public static CommonCategoryFragment newInstance(String liveType) { 37 | CommonCategoryFragment fragment = new CommonCategoryFragment(); 38 | Bundle args = new Bundle(); 39 | args.putString(LIVE_TYPE, liveType); 40 | fragment.setArguments(args); 41 | return fragment; 42 | } 43 | 44 | @Override 45 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 46 | Bundle savedInstanceState) { 47 | liveType = getArguments().getString(LIVE_TYPE); 48 | View view = inflater.inflate(R.layout.fragment_common_category, container, false); 49 | unbinder = ButterKnife.bind(this, view); 50 | init(); 51 | return view; 52 | } 53 | 54 | private void init() { 55 | mVpCate.setAdapter(new PagerAdapter(getChildFragmentManager(), liveType)); 56 | mTabCate.setupWithViewPager(mVpCate); 57 | } 58 | 59 | @Override 60 | public void onDestroyView() { 61 | super.onDestroyView(); 62 | unbinder.unbind(); 63 | } 64 | 65 | private static class PagerAdapter extends FragmentPagerAdapter { 66 | private List mFragmentList; 67 | private List mTitles; 68 | 69 | public PagerAdapter(FragmentManager fm, String liveType) { 70 | super(fm); 71 | mTitles = new ArrayList<>(); 72 | mFragmentList = new ArrayList<>(); 73 | mFragmentList.add(CommonListFragment.newInstance(liveType, "lol")); 74 | mFragmentList.add(CommonListFragment.newInstance(liveType, "ow")); 75 | mFragmentList.add(CommonListFragment.newInstance(liveType, "dota2")); 76 | mFragmentList.add(CommonListFragment.newInstance(liveType, "hs")); 77 | mFragmentList.add(CommonListFragment.newInstance(liveType, "csgo")); 78 | 79 | mTitles.add("英雄联盟"); 80 | mTitles.add("守望先锋"); 81 | mTitles.add("Dota2"); 82 | mTitles.add("炉石传说"); 83 | mTitles.add("CS:GO"); 84 | } 85 | 86 | @Override 87 | public CharSequence getPageTitle(int position) { 88 | return mTitles.get(position); 89 | } 90 | 91 | @Override 92 | public Fragment getItem(int position) { 93 | return mFragmentList.get(position); 94 | } 95 | 96 | @Override 97 | public int getCount() { 98 | return mFragmentList.size(); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/common/CommonListContract.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.common; 2 | 3 | import java.util.List; 4 | 5 | import io.playcode.streambox.data.bean.CommonStreamListEntity; 6 | import io.playcode.streambox.ui.base.BasePresenter; 7 | import io.playcode.streambox.ui.base.BaseView; 8 | 9 | /** 10 | * Created by anpoz on 2017/4/16. 11 | */ 12 | 13 | public interface CommonListContract { 14 | interface Presenter extends BasePresenter { 15 | void setLiveType(String liveType); 16 | 17 | void setGameType(String gameType); 18 | 19 | void requestRefresh(); 20 | 21 | void requestUpdate(); 22 | } 23 | 24 | interface View extends BaseView { 25 | void update(List resultEntityList); 26 | 27 | void showError(String error); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/common/CommonListFragment.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.common; 2 | 3 | 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.annotation.LayoutRes; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v7.widget.GridLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import com.blankj.aloglibrary.ALog; 16 | import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout; 17 | import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection; 18 | 19 | import org.byteam.superadapter.SuperAdapter; 20 | import org.byteam.superadapter.SuperViewHolder; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import butterknife.BindView; 26 | import butterknife.ButterKnife; 27 | import butterknife.Unbinder; 28 | import io.playcode.streambox.R; 29 | import io.playcode.streambox.data.bean.CommonStreamListEntity; 30 | import io.playcode.streambox.ui.commonstream.CommonStreamActivity; 31 | import io.playcode.streambox.util.FormatUtil; 32 | import io.playcode.streambox.util.ImageLoader; 33 | 34 | public class CommonListFragment extends Fragment implements CommonListContract.View { 35 | private static final String LIVE_TYPE = "live type"; 36 | private static final String GAME_TYPE = "game type"; 37 | @BindView(R.id.rv_common_list) 38 | RecyclerView mRvCommonList; 39 | @BindView(R.id.swipyrefreshlayout) 40 | SwipyRefreshLayout mSwipyrefreshlayout; 41 | Unbinder unbinder; 42 | 43 | private CommonListContract.Presenter mPresenter; 44 | 45 | public CommonListFragment() { 46 | // Required empty public constructor 47 | } 48 | 49 | public static CommonListFragment newInstance(String liveType, String gameType) { 50 | CommonListFragment fragment = new CommonListFragment(); 51 | Bundle args = new Bundle(); 52 | args.putString(LIVE_TYPE, liveType); 53 | args.putString(GAME_TYPE, gameType); 54 | fragment.setArguments(args); 55 | return fragment; 56 | } 57 | 58 | @Override 59 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 60 | Bundle savedInstanceState) { 61 | // Inflate the layout for this fragment 62 | String liveType = getArguments().getString(LIVE_TYPE); 63 | String gameType = getArguments().getString(GAME_TYPE); 64 | 65 | new CommonListPresenter(this); 66 | mPresenter.setLiveType(liveType); 67 | mPresenter.setGameType(gameType); 68 | mPresenter.subscribe(); 69 | mPresenter.requestRefresh(); 70 | 71 | View view = inflater.inflate(R.layout.fragment_common_list, container, false); 72 | unbinder = ButterKnife.bind(this, view); 73 | init(); 74 | return view; 75 | } 76 | 77 | private void init() { 78 | mRvCommonList.setHasFixedSize(true); 79 | mRvCommonList.setLayoutManager(new GridLayoutManager(getContext(), 2)); 80 | 81 | mSwipyrefreshlayout.setOnRefreshListener(direction -> { 82 | if (direction == SwipyRefreshLayoutDirection.TOP) {//上拉 83 | mPresenter.requestRefresh(); 84 | } else {//下拉 85 | mPresenter.requestUpdate(); 86 | } 87 | }); 88 | } 89 | 90 | @Override 91 | public void onDestroyView() { 92 | mPresenter.unSubscribe(); 93 | super.onDestroyView(); 94 | unbinder.unbind(); 95 | } 96 | 97 | @Override 98 | public void setPresenter(CommonListContract.Presenter presenter) { 99 | this.mPresenter = presenter; 100 | } 101 | 102 | @Override 103 | public void update(List resultEntityList) { 104 | mSwipyrefreshlayout.setRefreshing(false); 105 | mRvCommonList.setAdapter(new ListAdapter(getContext(), resultEntityList, R.layout.item_live_list)); 106 | 107 | if (resultEntityList.size() > 20) { 108 | int endPosition = resultEntityList.size() - resultEntityList.size() % 20; 109 | mRvCommonList.scrollToPosition(endPosition); 110 | } 111 | } 112 | 113 | @Override 114 | public void showError(String error) { 115 | mSwipyrefreshlayout.setRefreshing(false); 116 | Snackbar.make(mRvCommonList, error, Snackbar.LENGTH_LONG).show(); 117 | } 118 | 119 | private class ListAdapter extends SuperAdapter { 120 | 121 | public ListAdapter(Context context, List items, @LayoutRes int layoutResId) { 122 | super(context, items, layoutResId); 123 | } 124 | 125 | @Override 126 | public void onBind(SuperViewHolder holder, int viewType, int layoutPosition, CommonStreamListEntity.ResultEntity item) { 127 | holder.setText(R.id.tv_room_title, item.getLive_title()); 128 | holder.setText(R.id.tv_person_num, FormatUtil.formatPersonNum(String.valueOf(item.getLive_online()))); 129 | holder.setText(R.id.tv_nickname, item.getLive_nickname()); 130 | ImageLoader.withCenterCrop(getContext(), item.getLive_img(), holder.findViewById(R.id.iv_room_cover)); 131 | holder.findViewById(R.id.card_root).setOnClickListener( 132 | v -> CommonStreamActivity.startActivity(getActivity(), 133 | item.getLive_name(), 134 | item.getLive_id(), 135 | item.getGame_type())); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/common/CommonListPresenter.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.common; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import io.playcode.streambox.data.bean.CommonStreamListEntity; 9 | import io.playcode.streambox.data.source.AppRepository; 10 | import io.reactivex.Observer; 11 | import io.reactivex.android.schedulers.AndroidSchedulers; 12 | import io.reactivex.disposables.CompositeDisposable; 13 | import io.reactivex.disposables.Disposable; 14 | import io.reactivex.schedulers.Schedulers; 15 | 16 | /** 17 | * Created by anpoz on 2017/4/16. 18 | */ 19 | 20 | public class CommonListPresenter implements CommonListContract.Presenter { 21 | private CommonListContract.View mView; 22 | private String liveType; 23 | private String gameType; 24 | private List mResultEntityList; 25 | private int offset; 26 | private CompositeDisposable mCompositeDisposable; 27 | 28 | public CommonListPresenter(CommonListContract.View view) { 29 | mView = view; 30 | mView.setPresenter(this); 31 | mResultEntityList = new ArrayList<>(); 32 | offset = 0; 33 | mCompositeDisposable = new CompositeDisposable(); 34 | } 35 | 36 | @Override 37 | public void subscribe() { 38 | 39 | } 40 | 41 | @Override 42 | public void unSubscribe() { 43 | mCompositeDisposable.dispose(); 44 | } 45 | 46 | @Override 47 | public void setLiveType(String liveType) { 48 | this.liveType = liveType; 49 | } 50 | 51 | @Override 52 | public void setGameType(String gameType) { 53 | this.gameType = gameType; 54 | } 55 | 56 | @Override 57 | public void requestRefresh() { 58 | offset = 0; 59 | AppRepository.getInstance() 60 | .getCommonStreamList(offset, liveType, gameType) 61 | .subscribeOn(Schedulers.io()) 62 | .observeOn(AndroidSchedulers.mainThread()) 63 | .subscribe(new Observer() { 64 | @Override 65 | public void onSubscribe(Disposable d) { 66 | mCompositeDisposable.add(d); 67 | } 68 | 69 | @Override 70 | public void onNext(CommonStreamListEntity commonStreamListEntity) { 71 | mResultEntityList.clear(); 72 | mResultEntityList.addAll(commonStreamListEntity.getResult()); 73 | mView.update(mResultEntityList); 74 | } 75 | 76 | @Override 77 | public void onError(Throwable e) { 78 | if (TextUtils.equals(e.getMessage(), "timeout")) { 79 | mView.showError("网络错误,刷新超时"); 80 | } else { 81 | mView.showError("网络错误"); 82 | } 83 | } 84 | 85 | @Override 86 | public void onComplete() { 87 | 88 | } 89 | }); 90 | } 91 | 92 | @Override 93 | public void requestUpdate() { 94 | offset++; 95 | AppRepository.getInstance() 96 | .getCommonStreamList(offset, liveType, gameType) 97 | .subscribeOn(Schedulers.io()) 98 | .observeOn(AndroidSchedulers.mainThread()) 99 | .subscribe(new Observer() { 100 | @Override 101 | public void onSubscribe(Disposable d) { 102 | mCompositeDisposable.add(d); 103 | } 104 | 105 | @Override 106 | public void onNext(CommonStreamListEntity commonStreamListEntity) { 107 | mResultEntityList.clear(); 108 | mResultEntityList.addAll(commonStreamListEntity.getResult()); 109 | mView.update(mResultEntityList); 110 | } 111 | 112 | @Override 113 | public void onError(Throwable e) { 114 | if (TextUtils.equals(e.getMessage(), "timeout")) { 115 | mView.showError("网络错误,刷新超时"); 116 | } else { 117 | mView.showError("网络错误"); 118 | } 119 | } 120 | 121 | @Override 122 | public void onComplete() { 123 | 124 | } 125 | }); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/commonstream/CommonStreamActivity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.commonstream; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.os.Bundle; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.design.widget.TabLayout; 9 | import android.support.v4.app.Fragment; 10 | import android.support.v4.app.FragmentManager; 11 | import android.support.v4.app.FragmentPagerAdapter; 12 | import android.support.v4.view.ViewPager; 13 | import android.support.v7.app.AppCompatActivity; 14 | 15 | import com.blankj.aloglibrary.ALog; 16 | import com.shuyu.gsyvideoplayer.GSYVideoPlayer; 17 | import com.shuyu.gsyvideoplayer.utils.GSYVideoType; 18 | import com.shuyu.gsyvideoplayer.utils.OrientationUtils; 19 | import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import butterknife.BindView; 25 | import butterknife.ButterKnife; 26 | import io.playcode.streambox.R; 27 | import io.playcode.streambox.ui.chatroom.ChatroomFragment; 28 | import io.playcode.streambox.ui.info.StreamInfoFragment; 29 | 30 | public class CommonStreamActivity extends AppCompatActivity implements CommonStreamContract.View { 31 | 32 | @BindView(R.id.player) 33 | StandardGSYVideoPlayer mPlayer; 34 | @BindView(R.id.tab_room_switch) 35 | TabLayout mTabRoomSwitch; 36 | @BindView(R.id.vp_switch) 37 | ViewPager mVpSwitch; 38 | 39 | private static final String TAG_LIVE_ID = "tag live id"; 40 | private static final String TAG_LIVE_TYPE = "tag live type"; 41 | private static final String TAG_GAME_TYPE = "tag game type"; 42 | private CommonStreamContract.Presenter mPresenter; 43 | private OrientationUtils mOrientationUtils; 44 | 45 | public static void startActivity(Activity activity, String live_type, String live_id, String game_type) { 46 | Intent intent = new Intent(activity, CommonStreamActivity.class); 47 | intent.putExtra(TAG_LIVE_ID, live_id); 48 | intent.putExtra(TAG_LIVE_TYPE, live_type); 49 | intent.putExtra(TAG_GAME_TYPE, game_type); 50 | activity.startActivity(intent); 51 | } 52 | 53 | @Override 54 | protected void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | setContentView(R.layout.activity_common_stream); 57 | ButterKnife.bind(this); 58 | 59 | SharedPreferences preferences = getSharedPreferences(getPackageName() + "_preferences", MODE_PRIVATE); 60 | boolean mediaCodec = preferences.getBoolean(getString(R.string.enableMediaCodec), false); 61 | boolean needShowWifiTip = preferences.getBoolean(getString(R.string.enableNeedShowWifiTip), true); 62 | boolean rotateViewAuto = preferences.getBoolean(getString(R.string.enableRotateViewAuto), false); 63 | boolean showFullAnimation = preferences.getBoolean(getString(R.string.enableShowFullAnimation), false); 64 | 65 | mOrientationUtils = new OrientationUtils(this, mPlayer); 66 | mPlayer.setIsTouchWiget(true); 67 | mPlayer.setRotateViewAuto(rotateViewAuto); 68 | mPlayer.setLockLand(true); 69 | mPlayer.setShowFullAnimation(showFullAnimation); 70 | mPlayer.setNeedShowWifiTip(needShowWifiTip); 71 | mPlayer.setNeedLockFull(true); 72 | if (mediaCodec) { 73 | GSYVideoType.enableMediaCodec(); 74 | } else { 75 | GSYVideoType.disableMediaCodec(); 76 | } 77 | mPlayer.getFullscreenButton().setOnClickListener(v -> { 78 | mOrientationUtils.resolveByClick(); 79 | mPlayer.startWindowFullscreen(CommonStreamActivity.this, true, true); 80 | }); 81 | 82 | String liveId = getIntent().getStringExtra(TAG_LIVE_ID); 83 | String liveType = getIntent().getStringExtra(TAG_LIVE_TYPE); 84 | String gameType = getIntent().getStringExtra(TAG_GAME_TYPE); 85 | new CommonStreamPresenter(this); 86 | mPresenter.setLiveId(liveId); 87 | mPresenter.setLiveType(liveType); 88 | mPresenter.setGameType(gameType); 89 | mPresenter.subscribe(); 90 | 91 | mVpSwitch.setAdapter(new PagerAdapter(getSupportFragmentManager())); 92 | mTabRoomSwitch.setupWithViewPager(mVpSwitch); 93 | } 94 | 95 | @Override 96 | public void onBackPressed() { 97 | if (mOrientationUtils != null) { 98 | mOrientationUtils.backToProtVideo(); 99 | } 100 | if (StandardGSYVideoPlayer.backFromWindowFull(this)) { 101 | return; 102 | } 103 | super.onBackPressed(); 104 | } 105 | 106 | @Override 107 | protected void onDestroy() { 108 | mPresenter.unSubscribe(); 109 | GSYVideoPlayer.releaseAllVideos(); 110 | mOrientationUtils.releaseListener(); 111 | super.onDestroy(); 112 | } 113 | 114 | @Override 115 | protected void onPause() { 116 | super.onPause(); 117 | } 118 | 119 | @Override 120 | public void setPresenter(CommonStreamContract.Presenter presenter) { 121 | mPresenter = presenter; 122 | } 123 | 124 | @Override 125 | public void updateStreamAddress(String url, String title) { 126 | ALog.d(url); 127 | try { 128 | mPlayer.setUp(url,false,null,title); 129 | mPlayer.startPlayLogic(); 130 | } catch (Exception e) { 131 | e.printStackTrace(); 132 | showError("线路不佳,直播流无法播放"); 133 | } 134 | } 135 | 136 | @Override 137 | public void showError(String error) { 138 | Snackbar.make(mVpSwitch, error, Snackbar.LENGTH_LONG) 139 | .setAction("知道啦", v -> CommonStreamActivity.this.finish()) 140 | .show(); 141 | } 142 | 143 | private static class PagerAdapter extends FragmentPagerAdapter { 144 | private List mFragmentList; 145 | private String[] titles = {"聊天", "主播"}; 146 | 147 | public PagerAdapter(FragmentManager fm) { 148 | super(fm); 149 | mFragmentList = new ArrayList<>(); 150 | mFragmentList.add(new ChatroomFragment()); 151 | mFragmentList.add(new StreamInfoFragment()); 152 | } 153 | 154 | @Override 155 | public Fragment getItem(int position) { 156 | return mFragmentList.get(position); 157 | } 158 | 159 | @Override 160 | public CharSequence getPageTitle(int position) { 161 | return titles[position]; 162 | } 163 | 164 | @Override 165 | public int getCount() { 166 | return mFragmentList.size(); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/commonstream/CommonStreamContract.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.commonstream; 2 | 3 | import io.playcode.streambox.ui.base.BasePresenter; 4 | import io.playcode.streambox.ui.base.BaseView; 5 | 6 | /** 7 | * Created by anpoz on 2017/4/16. 8 | */ 9 | 10 | public interface CommonStreamContract { 11 | interface Presenter extends BasePresenter { 12 | void setLiveId(String liveId); 13 | 14 | void setLiveType(String liveType); 15 | 16 | void setGameType(String gameType); 17 | } 18 | 19 | interface View extends BaseView { 20 | void updateStreamAddress(String url, String title); 21 | void showError(String error); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/commonstream/CommonStreamPresenter.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.commonstream; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.blankj.aloglibrary.ALog; 6 | 7 | import org.greenrobot.eventbus.EventBus; 8 | import org.w3c.dom.Text; 9 | 10 | import io.playcode.streambox.data.bean.CommonStreamEntity; 11 | import io.playcode.streambox.data.bean.StreamInfoEntity; 12 | import io.playcode.streambox.data.source.AppRepository; 13 | import io.playcode.streambox.event.StreamInfoEvent; 14 | import io.reactivex.Observable; 15 | import io.reactivex.ObservableSource; 16 | import io.reactivex.Observer; 17 | import io.reactivex.android.schedulers.AndroidSchedulers; 18 | import io.reactivex.annotations.NonNull; 19 | import io.reactivex.disposables.CompositeDisposable; 20 | import io.reactivex.disposables.Disposable; 21 | import io.reactivex.functions.Function; 22 | import io.reactivex.functions.Predicate; 23 | import io.reactivex.schedulers.Schedulers; 24 | 25 | /** 26 | * Created by anpoz on 2017/4/16. 27 | */ 28 | 29 | public class CommonStreamPresenter implements CommonStreamContract.Presenter { 30 | private CommonStreamContract.View mView; 31 | private String liveId; 32 | private String liveType; 33 | private String gameType; 34 | private String address; 35 | private CompositeDisposable mCompositeDisposable; 36 | 37 | public CommonStreamPresenter(CommonStreamContract.View view) { 38 | mView = view; 39 | mView.setPresenter(this); 40 | mCompositeDisposable = new CompositeDisposable(); 41 | } 42 | 43 | @Override 44 | public void subscribe() { 45 | AppRepository.getInstance() 46 | .getCommonStreamDetail(liveType, liveId, gameType) 47 | .subscribeOn(Schedulers.io()) 48 | .observeOn(AndroidSchedulers.mainThread()) 49 | .subscribe(new Observer() { 50 | @Override 51 | public void onSubscribe(Disposable d) { 52 | mCompositeDisposable.add(d); 53 | } 54 | 55 | @Override 56 | public void onNext(CommonStreamEntity commonStreamEntity) { 57 | if (TextUtils.equals("failed", commonStreamEntity.getStatus()) || 58 | commonStreamEntity.getResult().getStream_list() == null || 59 | commonStreamEntity.getResult().getStream_list().size() == 0) { 60 | mView.showError("无法获取播放地址,主播或已下播"); 61 | } else { 62 | address = commonStreamEntity.getResult().getStream_list().get(0).getUrl(); 63 | ALog.d(address); 64 | StreamInfoEntity infoEntity = new StreamInfoEntity(); 65 | infoEntity.setLive_title(commonStreamEntity.getResult().getLive_title()); 66 | infoEntity.setLive_type(liveType); 67 | infoEntity.setPush_time(commonStreamEntity.getResult().getPush_time()); 68 | infoEntity.setLive_id(commonStreamEntity.getResult().getLive_id()); 69 | infoEntity.setLive_online(commonStreamEntity.getResult().getLive_online() + ""); 70 | infoEntity.setLive_nickname(commonStreamEntity.getResult().getLive_nickname()); 71 | infoEntity.setLive_img(commonStreamEntity.getResult().getLive_img()); 72 | EventBus.getDefault().postSticky(new StreamInfoEvent(infoEntity)); 73 | mView.updateStreamAddress(address, commonStreamEntity.getResult().getLive_title()); 74 | } 75 | } 76 | 77 | @Override 78 | public void onError(Throwable e) { 79 | e.printStackTrace(); 80 | } 81 | 82 | @Override 83 | public void onComplete() { 84 | 85 | } 86 | }); 87 | } 88 | 89 | @Override 90 | public void unSubscribe() { 91 | mCompositeDisposable.dispose(); 92 | } 93 | 94 | @Override 95 | public void setLiveId(String liveId) { 96 | this.liveId = liveId; 97 | } 98 | 99 | //发现有的平台会出现首字母大写的情况,但获取详细的api不接受有大写的参数 100 | @Override 101 | public void setLiveType(String liveType) { 102 | this.liveType = liveType.toLowerCase(); 103 | } 104 | 105 | @Override 106 | public void setGameType(String gameType) { 107 | this.gameType = gameType; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/info/StreamInfoContract.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.info; 2 | 3 | import io.playcode.streambox.data.bean.StreamInfoEntity; 4 | import io.playcode.streambox.ui.base.BasePresenter; 5 | import io.playcode.streambox.ui.base.BaseView; 6 | 7 | /** 8 | * Created by anpoz on 2017/4/16. 9 | */ 10 | 11 | public interface StreamInfoContract { 12 | interface Presenter extends BasePresenter { 13 | 14 | } 15 | 16 | interface View extends BaseView { 17 | void updateInfo(StreamInfoEntity infoEntity); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/info/StreamInfoFragment.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.info; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.blankj.aloglibrary.ALog; 13 | 14 | import java.text.SimpleDateFormat; 15 | import java.util.Date; 16 | import java.util.Locale; 17 | import java.util.TimeZone; 18 | import java.util.logging.SimpleFormatter; 19 | 20 | import butterknife.BindView; 21 | import butterknife.ButterKnife; 22 | import butterknife.Unbinder; 23 | import io.playcode.streambox.R; 24 | import io.playcode.streambox.data.bean.StreamInfoEntity; 25 | import io.playcode.streambox.util.ImageLoader; 26 | 27 | /** 28 | * A simple {@link Fragment} subclass. 29 | */ 30 | public class StreamInfoFragment extends Fragment implements StreamInfoContract.View { 31 | 32 | 33 | @BindView(R.id.iv_room_cover) 34 | ImageView mIvRoomCover; 35 | @BindView(R.id.tv_nickname) 36 | TextView mTvNickname; 37 | @BindView(R.id.tv_room_title) 38 | TextView mTvRoomTitle; 39 | @BindView(R.id.tv_stream_time) 40 | TextView mTvStreamTime; 41 | @BindView(R.id.tv_person_num) 42 | TextView mTvPersonNum; 43 | @BindView(R.id.tv_room_id) 44 | TextView mTvRoomId; 45 | Unbinder unbinder; 46 | 47 | private StreamInfoContract.Presenter mPresenter; 48 | 49 | 50 | public StreamInfoFragment() { 51 | // Required empty public constructor 52 | } 53 | 54 | 55 | @Override 56 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 57 | Bundle savedInstanceState) { 58 | // Inflate the layout for this fragment 59 | View view = inflater.inflate(R.layout.fragment_stream_info, container, false); 60 | unbinder = ButterKnife.bind(this, view); 61 | new StreamInfoPresenter(this); 62 | mPresenter.subscribe(); 63 | return view; 64 | } 65 | 66 | @Override 67 | public void onDestroyView() { 68 | mPresenter.unSubscribe(); 69 | super.onDestroyView(); 70 | mPresenter.unSubscribe(); 71 | unbinder.unbind(); 72 | } 73 | 74 | @Override 75 | public void setPresenter(StreamInfoContract.Presenter presenter) { 76 | mPresenter = presenter; 77 | } 78 | 79 | @Override 80 | public void updateInfo(StreamInfoEntity infoEntity) { 81 | mTvRoomTitle.setText(infoEntity.getLive_title()); 82 | mTvNickname.setText(infoEntity.getLive_nickname()); 83 | mTvRoomId.setText("房间ID:" + infoEntity.getLive_id()); 84 | mTvPersonNum.setText("在线人数:" + infoEntity.getLive_online()); 85 | ImageLoader.withCenterCrop(getContext(), infoEntity.getLive_img(), mIvRoomCover); 86 | 87 | // TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); 88 | // SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA); 89 | // mTvStreamTime.setText(formatter.format(new Date(Long.valueOf(infoEntity.getPush_time())))); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/info/StreamInfoPresenter.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.info; 2 | 3 | import org.greenrobot.eventbus.EventBus; 4 | import org.greenrobot.eventbus.Subscribe; 5 | import org.greenrobot.eventbus.ThreadMode; 6 | 7 | import io.playcode.streambox.event.StreamInfoEvent; 8 | 9 | /** 10 | * Created by anpoz on 2017/4/16. 11 | */ 12 | 13 | public class StreamInfoPresenter implements StreamInfoContract.Presenter { 14 | private StreamInfoContract.View mView; 15 | 16 | public StreamInfoPresenter(StreamInfoContract.View view) { 17 | mView = view; 18 | mView.setPresenter(this); 19 | } 20 | 21 | @Override 22 | public void subscribe() { 23 | if (!EventBus.getDefault().isRegistered(this)) 24 | EventBus.getDefault().register(this); 25 | } 26 | 27 | @Override 28 | public void unSubscribe() { 29 | EventBus.getDefault().unregister(this); 30 | } 31 | 32 | @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) 33 | public void onStreamInfoEvent(StreamInfoEvent event) { 34 | mView.updateInfo(event.getStreamInfoEntity()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/main/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.main; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.design.widget.BottomNavigationView; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentManager; 8 | import android.support.v4.app.FragmentPagerAdapter; 9 | import android.support.v4.view.ViewPager; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.support.v7.app.AppCompatDelegate; 12 | import android.support.v7.widget.Toolbar; 13 | import android.view.Menu; 14 | import android.view.MenuItem; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | import butterknife.BindView; 20 | import butterknife.ButterKnife; 21 | import io.playcode.streambox.R; 22 | import io.playcode.streambox.ui.common.CommonCategoryFragment; 23 | import io.playcode.streambox.ui.panda.PandaCategoryFragment; 24 | import io.playcode.streambox.ui.settings.SettingsActivity; 25 | 26 | public class MainActivity extends AppCompatActivity { 27 | 28 | static { 29 | AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); 30 | } 31 | 32 | @BindView(R.id.toolbar) 33 | Toolbar mToolbar; 34 | @BindView(R.id.viewpager) 35 | ViewPager mViewpager; 36 | @BindView(R.id.bottom_navigation) 37 | BottomNavigationView mBottomNavigation; 38 | 39 | private MenuItem preMenuItem; 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | ButterKnife.bind(this); 46 | init(); 47 | } 48 | 49 | @Override 50 | public boolean onCreateOptionsMenu(Menu menu) { 51 | getMenuInflater().inflate(R.menu.menu_main, menu); 52 | return super.onCreateOptionsMenu(menu); 53 | } 54 | 55 | private void init() { 56 | setSupportActionBar(mToolbar); 57 | 58 | mToolbar.setOnMenuItemClickListener(item -> { 59 | switch (item.getItemId()) { 60 | case R.id.menu_settings: 61 | Intent intent = new Intent(MainActivity.this, SettingsActivity.class); 62 | startActivity(intent); 63 | return true; 64 | case R.id.menu_about: 65 | return true; 66 | case R.id.menu_search: 67 | return true; 68 | } 69 | return false; 70 | }); 71 | 72 | mViewpager.setAdapter(new PagerAdapter(getSupportFragmentManager())); 73 | //关联viewpager与BottomNavigationView 74 | mViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 75 | @Override 76 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 77 | 78 | } 79 | 80 | @Override 81 | public void onPageSelected(int position) { 82 | if (preMenuItem != null) { 83 | preMenuItem.setChecked(false); 84 | } 85 | else { 86 | mBottomNavigation.getMenu().getItem(0).setChecked(false); 87 | } 88 | mBottomNavigation.getMenu().getItem(position).setChecked(true); 89 | preMenuItem = mBottomNavigation.getMenu().getItem(position); 90 | } 91 | 92 | @Override 93 | public void onPageScrollStateChanged(int state) { 94 | 95 | } 96 | }); 97 | mBottomNavigation.setOnNavigationItemSelectedListener(item -> { 98 | switch (item.getItemId()) { 99 | case R.id.menu_panda: 100 | mViewpager.setCurrentItem(0); 101 | break; 102 | case R.id.menu_douyu: 103 | mViewpager.setCurrentItem(1); 104 | break; 105 | case R.id.menu_quanmin: 106 | mViewpager.setCurrentItem(2); 107 | break; 108 | case R.id.menu_zhanqi: 109 | mViewpager.setCurrentItem(3); 110 | break; 111 | } 112 | return false; 113 | }); 114 | } 115 | 116 | private static class PagerAdapter extends FragmentPagerAdapter { 117 | private List mFragmentList; 118 | 119 | public PagerAdapter(FragmentManager fm) { 120 | super(fm); 121 | mFragmentList = new ArrayList<>(); 122 | mFragmentList.add(new PandaCategoryFragment()); 123 | mFragmentList.add(CommonCategoryFragment.newInstance("douyu")); 124 | mFragmentList.add(CommonCategoryFragment.newInstance("quanmin")); 125 | mFragmentList.add(CommonCategoryFragment.newInstance("zhanqi")); 126 | } 127 | 128 | @Override 129 | public Fragment getItem(int position) { 130 | return mFragmentList.get(position); 131 | } 132 | 133 | @Override 134 | public int getCount() { 135 | return mFragmentList.size(); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/panda/PandaCategoryFragment.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.panda; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.design.widget.TabLayout; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentManager; 8 | import android.support.v4.app.FragmentPagerAdapter; 9 | import android.support.v4.view.ViewPager; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import butterknife.BindView; 18 | import butterknife.ButterKnife; 19 | import butterknife.Unbinder; 20 | import io.playcode.streambox.R; 21 | 22 | /** 23 | * A simple {@link Fragment} subclass. 24 | */ 25 | public class PandaCategoryFragment extends Fragment { 26 | 27 | 28 | @BindView(R.id.tab_cate) 29 | TabLayout mTabCate; 30 | @BindView(R.id.vp_cate) 31 | ViewPager mVpCate; 32 | Unbinder unbinder; 33 | 34 | public PandaCategoryFragment() { 35 | // Required empty public constructor 36 | } 37 | 38 | @Override 39 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 40 | Bundle savedInstanceState) { 41 | // Inflate the layout for this fragment 42 | View view = inflater.inflate(R.layout.fragment_panda_category, container, false); 43 | unbinder = ButterKnife.bind(this, view); 44 | init(); 45 | return view; 46 | } 47 | 48 | private void init() { 49 | mVpCate.setAdapter(new PagerAdapter(getChildFragmentManager())); 50 | mTabCate.setupWithViewPager(mVpCate); 51 | } 52 | 53 | @Override 54 | public void onDestroyView() { 55 | super.onDestroyView(); 56 | unbinder.unbind(); 57 | } 58 | 59 | private static class PagerAdapter extends FragmentPagerAdapter { 60 | private List mFragmentList; 61 | private List mTitles; 62 | 63 | public PagerAdapter(FragmentManager fm) { 64 | super(fm); 65 | mFragmentList = new ArrayList<>(); 66 | mTitles = new ArrayList<>(); 67 | 68 | mFragmentList.add(PandaListFragment.newInstance("lol")); 69 | mFragmentList.add(PandaListFragment.newInstance("hwzb")); 70 | mFragmentList.add(PandaListFragment.newInstance("yzdr")); 71 | mFragmentList.add(PandaListFragment.newInstance("overwatch")); 72 | mFragmentList.add(PandaListFragment.newInstance("kingglory")); 73 | mFragmentList.add(PandaListFragment.newInstance("hearthstone")); 74 | mFragmentList.add(PandaListFragment.newInstance("zhuji")); 75 | 76 | mTitles.add("英雄联盟"); 77 | mTitles.add("户外直播"); 78 | mTitles.add("熊猫星秀"); 79 | mTitles.add("守望先锋"); 80 | mTitles.add("王者荣耀"); 81 | mTitles.add("炉石传说"); 82 | mTitles.add("主机游戏"); 83 | } 84 | 85 | @Override 86 | public CharSequence getPageTitle(int position) { 87 | return mTitles.get(position); 88 | } 89 | 90 | @Override 91 | public Fragment getItem(int position) { 92 | return mFragmentList.get(position); 93 | } 94 | 95 | @Override 96 | public int getCount() { 97 | return mFragmentList.size(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/panda/PandaListContract.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.panda; 2 | 3 | import java.util.List; 4 | 5 | import io.playcode.streambox.data.bean.PandaStreamListEntity; 6 | import io.playcode.streambox.ui.base.BasePresenter; 7 | import io.playcode.streambox.ui.base.BaseView; 8 | 9 | /** 10 | * Created by anpoz on 2017/4/13. 11 | */ 12 | 13 | public interface PandaListContract { 14 | interface Presenter extends BasePresenter { 15 | void setGameType(String gameType); 16 | void requestRefresh(); 17 | void requestUpdate(); 18 | } 19 | 20 | interface View extends BaseView { 21 | void update(List pandaRoomEntities); 22 | void showError(String error); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/panda/PandaListFragment.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.panda; 2 | 3 | 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.annotation.LayoutRes; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v7.widget.GridLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout; 16 | import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection; 17 | 18 | import org.byteam.superadapter.SuperAdapter; 19 | import org.byteam.superadapter.SuperViewHolder; 20 | 21 | import java.util.List; 22 | 23 | import butterknife.BindView; 24 | import butterknife.ButterKnife; 25 | import butterknife.Unbinder; 26 | import io.playcode.streambox.R; 27 | import io.playcode.streambox.data.bean.PandaStreamListEntity; 28 | import io.playcode.streambox.ui.pandastream.PandaStreamActivity; 29 | import io.playcode.streambox.util.FormatUtil; 30 | import io.playcode.streambox.util.ImageLoader; 31 | 32 | /** 33 | * A simple {@link Fragment} subclass. 34 | */ 35 | public class PandaListFragment extends Fragment implements PandaListContract.View { 36 | private static final String GAME_TYPE = "game type"; 37 | @BindView(R.id.rv_panda_list) 38 | RecyclerView mRvPandaList; 39 | Unbinder unbinder; 40 | @BindView(R.id.swipyrefreshlayout) 41 | SwipyRefreshLayout mSwipyrefreshlayout; 42 | private PandaListContract.Presenter mPresenter; 43 | 44 | public PandaListFragment() { 45 | // Required empty public constructor 46 | } 47 | 48 | public static PandaListFragment newInstance(String liveType) { 49 | PandaListFragment fragment = new PandaListFragment(); 50 | Bundle bundle = new Bundle(); 51 | bundle.putString(GAME_TYPE, liveType); 52 | fragment.setArguments(bundle); 53 | return fragment; 54 | } 55 | 56 | @Override 57 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 58 | Bundle savedInstanceState) { 59 | String type = (String) getArguments().getCharSequence(GAME_TYPE); 60 | 61 | new PandaListPresenter(this); 62 | mPresenter.setGameType(type); 63 | mPresenter.subscribe(); 64 | mPresenter.requestRefresh(); 65 | 66 | View view = inflater.inflate(R.layout.fragment_stream_list, container, false); 67 | unbinder = ButterKnife.bind(this, view); 68 | 69 | init(); 70 | return view; 71 | } 72 | 73 | private void init() { 74 | mRvPandaList.setHasFixedSize(true); 75 | mRvPandaList.setLayoutManager(new GridLayoutManager(getContext(), 2)); 76 | 77 | mSwipyrefreshlayout.setOnRefreshListener(direction -> { 78 | if (direction == SwipyRefreshLayoutDirection.TOP) {//上拉 79 | mPresenter.requestRefresh(); 80 | } else {//下拉 81 | mPresenter.requestUpdate(); 82 | } 83 | }); 84 | } 85 | 86 | @Override 87 | public void setPresenter(PandaListContract.Presenter presenter) { 88 | mPresenter = presenter; 89 | } 90 | 91 | @Override 92 | public void update(List pandaRoomEntities) { 93 | mSwipyrefreshlayout.setRefreshing(false); 94 | mRvPandaList.setAdapter(new ListAdapter(getContext(), pandaRoomEntities, R.layout.item_live_list)); 95 | mRvPandaList.setHasFixedSize(true); 96 | mRvPandaList.setLayoutManager(new GridLayoutManager(getContext(), 2)); 97 | if (pandaRoomEntities.size() > 20) { 98 | int endPosition = pandaRoomEntities.size() - pandaRoomEntities.size() % 20; 99 | mRvPandaList.scrollToPosition(endPosition); 100 | } 101 | } 102 | 103 | @Override 104 | public void showError(String error) { 105 | mSwipyrefreshlayout.setRefreshing(false); 106 | Snackbar.make(mRvPandaList, error, Snackbar.LENGTH_LONG).show(); 107 | } 108 | 109 | @Override 110 | public void onDestroyView() { 111 | mPresenter.unSubscribe(); 112 | super.onDestroyView(); 113 | unbinder.unbind(); 114 | } 115 | 116 | private class ListAdapter extends SuperAdapter { 117 | 118 | public ListAdapter(Context context, List items, @LayoutRes int layoutResId) { 119 | super(context, items, layoutResId); 120 | } 121 | 122 | @Override 123 | public void onBind(SuperViewHolder holder, int viewType, int layoutPosition, PandaStreamListEntity.DataEntity.ItemsEntity item) { 124 | holder.setText(R.id.tv_room_title, item.getName()); 125 | holder.setText(R.id.tv_person_num, FormatUtil.formatPersonNum(item.getPerson_num())); 126 | holder.setText(R.id.tv_nickname, item.getUserinfo().getNickName()); 127 | ImageLoader.withCenterCrop(getContext(), item.getPictures().getImg(), holder.findViewById(R.id.iv_room_cover)); 128 | holder.findViewById(R.id.card_root).setOnClickListener(v -> PandaStreamActivity.startActivity(getActivity(), item.getId())); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/panda/PandaListPresenter.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.panda; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import io.playcode.streambox.data.bean.PandaStreamListEntity; 10 | import io.playcode.streambox.data.source.AppRepository; 11 | import io.reactivex.Observable; 12 | import io.reactivex.Observer; 13 | import io.reactivex.android.schedulers.AndroidSchedulers; 14 | import io.reactivex.annotations.NonNull; 15 | import io.reactivex.disposables.CompositeDisposable; 16 | import io.reactivex.disposables.Disposable; 17 | import io.reactivex.functions.Consumer; 18 | import io.reactivex.schedulers.Schedulers; 19 | 20 | /** 21 | * Created by anpoz on 2017/4/13. 22 | */ 23 | 24 | public class PandaListPresenter implements PandaListContract.Presenter { 25 | private PandaListContract.View mView; 26 | private String gameType; 27 | private CompositeDisposable mCompositeDisposable; 28 | private int curPageNo; 29 | private List mPandaRoomEntities; 30 | 31 | public PandaListPresenter(PandaListContract.View view) { 32 | mView = view; 33 | mView.setPresenter(this); 34 | mCompositeDisposable = new CompositeDisposable(); 35 | curPageNo = 1; 36 | mPandaRoomEntities = new ArrayList<>(); 37 | } 38 | 39 | @Override 40 | public void subscribe() { 41 | 42 | } 43 | 44 | @Override 45 | public void unSubscribe() { 46 | mCompositeDisposable.dispose(); 47 | } 48 | 49 | @Override 50 | public void setGameType(String gameType) { 51 | this.gameType = gameType; 52 | } 53 | 54 | @Override 55 | public void requestRefresh() { 56 | curPageNo = 1; 57 | AppRepository.getInstance() 58 | .getPandaStreamList(gameType, curPageNo + "") 59 | .subscribeOn(Schedulers.io()) 60 | .observeOn(AndroidSchedulers.mainThread()) 61 | .subscribe(new Observer() { 62 | @Override 63 | public void onSubscribe(Disposable d) { 64 | mCompositeDisposable.add(d); 65 | } 66 | 67 | @Override 68 | public void onNext(PandaStreamListEntity pandaStreamListEntity) { 69 | mPandaRoomEntities.clear(); 70 | mPandaRoomEntities.addAll(pandaStreamListEntity.getData().getItems()); 71 | mView.update(mPandaRoomEntities); 72 | } 73 | 74 | @Override 75 | public void onError(Throwable e) { 76 | if (TextUtils.equals(e.getMessage(), "timeout")) { 77 | mView.showError("网络错误,刷新超时"); 78 | } else { 79 | mView.showError("网络错误"); 80 | } 81 | } 82 | 83 | @Override 84 | public void onComplete() { 85 | 86 | } 87 | }); 88 | } 89 | 90 | @Override 91 | public void requestUpdate() { 92 | curPageNo++; 93 | AppRepository.getInstance() 94 | .getPandaStreamList(gameType, curPageNo + "") 95 | .subscribeOn(Schedulers.io()) 96 | .observeOn(AndroidSchedulers.mainThread()) 97 | .subscribe(new Observer() { 98 | @Override 99 | public void onSubscribe(Disposable d) { 100 | mCompositeDisposable.add(d); 101 | } 102 | 103 | @Override 104 | public void onNext(PandaStreamListEntity pandaStreamListEntity) { 105 | mPandaRoomEntities.addAll(pandaStreamListEntity.getData().getItems()); 106 | mView.update(mPandaRoomEntities); 107 | } 108 | 109 | @Override 110 | public void onError(Throwable e) { 111 | if (TextUtils.equals(e.getMessage(), "timeout")) { 112 | mView.showError("网络错误,刷新超时"); 113 | } else { 114 | mView.showError("网络错误"); 115 | } 116 | } 117 | 118 | @Override 119 | public void onComplete() { 120 | 121 | } 122 | }); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/pandastream/PandaStreamActivity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.pandastream; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.os.Bundle; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.design.widget.TabLayout; 9 | import android.support.v4.app.Fragment; 10 | import android.support.v4.app.FragmentManager; 11 | import android.support.v4.app.FragmentPagerAdapter; 12 | import android.support.v4.view.ViewPager; 13 | import android.support.v7.app.AppCompatActivity; 14 | 15 | import com.blankj.aloglibrary.ALog; 16 | import com.shuyu.gsyvideoplayer.GSYVideoPlayer; 17 | import com.shuyu.gsyvideoplayer.utils.GSYVideoType; 18 | import com.shuyu.gsyvideoplayer.utils.OrientationUtils; 19 | import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import butterknife.BindView; 25 | import butterknife.ButterKnife; 26 | import io.playcode.streambox.R; 27 | import io.playcode.streambox.data.bean.BaseDanmu; 28 | import io.playcode.streambox.ui.chatroom.ChatroomFragment; 29 | import io.playcode.streambox.ui.info.StreamInfoFragment; 30 | import io.playcode.streambox.widget.DanmakuVideoPlayer; 31 | 32 | public class PandaStreamActivity extends AppCompatActivity implements PandaStreamContract.View { 33 | 34 | @BindView(R.id.tab_room_switch) 35 | TabLayout mTabRoomSwitch; 36 | @BindView(R.id.vp_switch) 37 | ViewPager mVpSwitch; 38 | @BindView(R.id.player) 39 | DanmakuVideoPlayer mPlayer; 40 | 41 | private static final String TAG_ROOM_ID = "tag room id"; 42 | 43 | private PandaStreamContract.Presenter mPresenter; 44 | private OrientationUtils mOrientationUtils; 45 | 46 | public static void startActivity(Activity activity, String id) { 47 | Intent intent = new Intent(activity, PandaStreamActivity.class); 48 | intent.putExtra(TAG_ROOM_ID, id); 49 | activity.startActivity(intent); 50 | } 51 | 52 | @Override 53 | protected void onCreate(Bundle savedInstanceState) { 54 | super.onCreate(savedInstanceState); 55 | setContentView(R.layout.activity_panda_stream); 56 | ButterKnife.bind(this); 57 | SharedPreferences preferences = getSharedPreferences(getPackageName() + "_preferences", MODE_PRIVATE); 58 | boolean mediaCodec = preferences.getBoolean(getString(R.string.enableMediaCodec), false); 59 | boolean needShowWifiTip = preferences.getBoolean(getString(R.string.enableNeedShowWifiTip), true); 60 | boolean rotateViewAuto = preferences.getBoolean(getString(R.string.enableRotateViewAuto), false); 61 | boolean showFullAnimation = preferences.getBoolean(getString(R.string.enableShowFullAnimation), false); 62 | 63 | mOrientationUtils = new OrientationUtils(this, mPlayer); 64 | mPlayer.setIsTouchWiget(true); 65 | mPlayer.setRotateViewAuto(rotateViewAuto); 66 | mPlayer.setLockLand(true); 67 | mPlayer.setShowFullAnimation(showFullAnimation); 68 | mPlayer.setNeedShowWifiTip(needShowWifiTip); 69 | mPlayer.setNeedLockFull(true); 70 | if (mediaCodec) { 71 | GSYVideoType.enableMediaCodec(); 72 | } 73 | else { 74 | GSYVideoType.disableMediaCodec(); 75 | } 76 | mPlayer.getFullscreenButton().setOnClickListener(v -> { 77 | mOrientationUtils.resolveByClick(); 78 | mPlayer.startWindowFullscreen(PandaStreamActivity.this, true, true); 79 | }); 80 | 81 | String id = getIntent().getStringExtra(TAG_ROOM_ID); 82 | new PandaStreamPresenter(this); 83 | mPresenter.subscribe(); 84 | mPresenter.setRoomId(id); 85 | 86 | mVpSwitch.setAdapter(new PagerAdapter(getSupportFragmentManager())); 87 | mTabRoomSwitch.setupWithViewPager(mVpSwitch); 88 | } 89 | 90 | @Override 91 | public void onBackPressed() { 92 | if (mOrientationUtils != null) { 93 | mOrientationUtils.backToProtVideo(); 94 | } 95 | if (StandardGSYVideoPlayer.backFromWindowFull(this)) { 96 | return; 97 | } 98 | super.onBackPressed(); 99 | } 100 | 101 | @Override 102 | protected void onDestroy() { 103 | mPresenter.unSubscribe(); 104 | GSYVideoPlayer.releaseAllVideos(); 105 | mOrientationUtils.releaseListener(); 106 | super.onDestroy(); 107 | } 108 | 109 | @Override 110 | protected void onPause() { 111 | super.onPause(); 112 | } 113 | 114 | @Override 115 | public void setPresenter(PandaStreamContract.Presenter presenter) { 116 | mPresenter = presenter; 117 | } 118 | 119 | @Override 120 | public void updateStreamAddress(String url, String title) { 121 | ALog.d(url); 122 | try { 123 | mPlayer.setUp(url, false, null, title); 124 | mPlayer.startPlayLogic(); 125 | } catch (Exception e) { 126 | e.printStackTrace(); 127 | Snackbar.make(mVpSwitch, "线路不佳,直播流无法播放", Snackbar.LENGTH_LONG) 128 | .setAction("知道啦", v -> PandaStreamActivity.this.finish()) 129 | .show(); 130 | } 131 | } 132 | 133 | @Override 134 | public void addDanmu(BaseDanmu danmu) { 135 | mPlayer.addDanmaku(danmu); 136 | } 137 | 138 | private static class PagerAdapter extends FragmentPagerAdapter { 139 | private List mFragmentList; 140 | private String[] titles = {"聊天", "主播"}; 141 | 142 | PagerAdapter(FragmentManager fm) { 143 | super(fm); 144 | mFragmentList = new ArrayList<>(); 145 | mFragmentList.add(new ChatroomFragment()); 146 | mFragmentList.add(new StreamInfoFragment()); 147 | } 148 | 149 | @Override 150 | public Fragment getItem(int position) { 151 | return mFragmentList.get(position); 152 | } 153 | 154 | @Override 155 | public CharSequence getPageTitle(int position) { 156 | return titles[position]; 157 | } 158 | 159 | @Override 160 | public int getCount() { 161 | return mFragmentList.size(); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/pandastream/PandaStreamContract.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.pandastream; 2 | 3 | import io.playcode.streambox.data.bean.BaseDanmu; 4 | import io.playcode.streambox.ui.base.BasePresenter; 5 | import io.playcode.streambox.ui.base.BaseView; 6 | 7 | /** 8 | * Created by anpoz on 2017/4/14. 9 | */ 10 | 11 | public interface PandaStreamContract { 12 | interface Presenter extends BasePresenter { 13 | void setRoomId(String id); 14 | } 15 | 16 | interface View extends BaseView { 17 | void updateStreamAddress(String url, String title); 18 | void addDanmu(BaseDanmu danmu); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/pandastream/PandaStreamPresenter.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.pandastream; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.blankj.aloglibrary.ALog; 6 | import com.google.gson.Gson; 7 | 8 | import org.greenrobot.eventbus.EventBus; 9 | import org.reactivestreams.Publisher; 10 | import org.reactivestreams.Subscription; 11 | 12 | import java.io.DataInputStream; 13 | import java.io.DataOutputStream; 14 | import java.io.IOException; 15 | import java.net.Socket; 16 | import java.net.SocketException; 17 | import java.nio.ByteBuffer; 18 | 19 | import io.playcode.streambox.data.bean.BaseDanmu; 20 | import io.playcode.streambox.data.bean.PandaDanmuEntity; 21 | import io.playcode.streambox.data.bean.PandaStreamDanmuServerEntity; 22 | import io.playcode.streambox.data.bean.PandaStreamEntity; 23 | import io.playcode.streambox.data.bean.StreamInfoEntity; 24 | import io.playcode.streambox.data.source.AppRepository; 25 | import io.playcode.streambox.event.PandaDanmuEvent; 26 | import io.playcode.streambox.event.StreamInfoEvent; 27 | import io.playcode.streambox.util.PandaDanmuUtil; 28 | import io.reactivex.BackpressureStrategy; 29 | import io.reactivex.Flowable; 30 | import io.reactivex.FlowableOnSubscribe; 31 | import io.reactivex.FlowableSubscriber; 32 | import io.reactivex.Observable; 33 | import io.reactivex.ObservableOnSubscribe; 34 | import io.reactivex.Observer; 35 | import io.reactivex.android.schedulers.AndroidSchedulers; 36 | import io.reactivex.annotations.NonNull; 37 | import io.reactivex.disposables.CompositeDisposable; 38 | import io.reactivex.disposables.Disposable; 39 | import io.reactivex.functions.Function; 40 | import io.reactivex.schedulers.Schedulers; 41 | 42 | /** 43 | * Created by anpoz on 2017/4/14. 44 | */ 45 | 46 | public class PandaStreamPresenter implements PandaStreamContract.Presenter { 47 | private PandaStreamContract.View mView; 48 | private CompositeDisposable mCompositeDisposable; 49 | private String roomId; 50 | private String address; 51 | private Subscription mSubscription; 52 | private Socket mSocket; 53 | private DataInputStream mInputStream; 54 | private DataOutputStream mOutputStream; 55 | private boolean isSocketConnect; 56 | 57 | public PandaStreamPresenter(PandaStreamContract.View view) { 58 | mView = view; 59 | mView.setPresenter(this); 60 | mCompositeDisposable = new CompositeDisposable(); 61 | isSocketConnect = false; 62 | } 63 | 64 | @Override 65 | public void subscribe() { 66 | 67 | } 68 | 69 | @Override 70 | public void unSubscribe() { 71 | isSocketConnect = false; 72 | mCompositeDisposable.dispose(); 73 | if (mSubscription != null) { 74 | mSubscription.cancel(); 75 | } 76 | EventBus.getDefault().removeStickyEvent(PandaDanmuEvent.class); 77 | } 78 | 79 | @Override 80 | public void setRoomId(String id) { 81 | roomId = id; 82 | ALog.d(id); 83 | AppRepository.getInstance() 84 | .getPandaStreamRoomNewApi(roomId) 85 | .subscribeOn(Schedulers.io()) 86 | .observeOn(AndroidSchedulers.mainThread()) 87 | .subscribe(new Observer() { 88 | @Override 89 | public void onSubscribe(Disposable d) { 90 | mCompositeDisposable.add(d); 91 | } 92 | 93 | @Override 94 | public void onNext(PandaStreamEntity pandaStreamEntity) { 95 | //根据对熊猫tv新客户端的抓包分析,使用拼接的方式获取更稳定的直播流 96 | //标清_small.flv,高清_mid.flv 97 | //http://pl21.live.panda.tv/live_panda/370daaa1e9ec694600b3dfe962fcab04.flv?sign=38153f226814ed9969edd0419d0d30ee&time=&ts=58fcf80c&rid=-66778604 98 | String cdn = pandaStreamEntity.getData().getInfo().getVideoinfo().getPlflag().split("_")[1]; 99 | address = "http://pl" + cdn + ".live.panda.tv/live_panda/" + 100 | pandaStreamEntity.getData().getInfo().getVideoinfo().getRoom_key() + 101 | ".flv" + 102 | "?sign=" + pandaStreamEntity.getData().getInfo().getVideoinfo().getSign() + 103 | "&time=" + pandaStreamEntity.getData().getInfo().getVideoinfo().getTs(); 104 | ALog.d(address); 105 | StreamInfoEntity infoEntity = new StreamInfoEntity(); 106 | infoEntity.setLive_id(pandaStreamEntity.getData().getInfo().getRoominfo().getId()); 107 | infoEntity.setLive_img(pandaStreamEntity.getData().getInfo().getHostinfo().getAvatar()); 108 | infoEntity.setLive_nickname(pandaStreamEntity.getData().getInfo().getHostinfo().getName()); 109 | infoEntity.setLive_title(pandaStreamEntity.getData().getInfo().getRoominfo().getName()); 110 | infoEntity.setLive_online(pandaStreamEntity.getData().getInfo().getRoominfo().getPerson_num()); 111 | infoEntity.setPush_time(pandaStreamEntity.getData().getInfo().getRoominfo().getStart_time()); 112 | infoEntity.setLive_type("pandatv"); 113 | EventBus.getDefault().postSticky(new StreamInfoEvent(infoEntity)); 114 | mView.updateStreamAddress(address, pandaStreamEntity.getData().getInfo().getRoominfo().getName()); 115 | } 116 | 117 | @Override 118 | public void onError(Throwable e) { 119 | e.printStackTrace(); 120 | } 121 | 122 | @Override 123 | public void onComplete() { 124 | 125 | } 126 | }); 127 | 128 | AppRepository.getInstance() 129 | .getPandaDanmuServer(id) 130 | .subscribeOn(Schedulers.io()) 131 | .observeOn(AndroidSchedulers.mainThread()) 132 | .subscribe(new Observer() { 133 | @Override 134 | public void onSubscribe(Disposable d) { 135 | mCompositeDisposable.add(d); 136 | } 137 | 138 | @Override 139 | public void onNext(PandaStreamDanmuServerEntity pandaStreamDanmuServerEntity) { 140 | connectDanmuSocket(pandaStreamDanmuServerEntity); 141 | } 142 | 143 | @Override 144 | public void onError(Throwable e) { 145 | e.printStackTrace(); 146 | } 147 | 148 | @Override 149 | public void onComplete() { 150 | 151 | } 152 | }); 153 | } 154 | 155 | private void connectDanmuSocket(PandaStreamDanmuServerEntity danmuServerEntity) { 156 | String[] address = danmuServerEntity.getData().getChat_addr_list().get(0).split(":"); 157 | isSocketConnect = true; 158 | Observable.create((ObservableOnSubscribe) e -> { 159 | mSocket = new Socket(address[0], Integer.valueOf(address[1])); 160 | mInputStream = new DataInputStream(mSocket.getInputStream()); 161 | mOutputStream = new DataOutputStream(mSocket.getOutputStream()); 162 | mOutputStream.write(PandaDanmuUtil.getConnectData(danmuServerEntity.getData())); 163 | mOutputStream.flush(); 164 | byte[] bytes; 165 | try { 166 | while (isSocketConnect) { 167 | bytes = new byte[512]; 168 | mInputStream.read(bytes); 169 | e.onNext(ByteBuffer.wrap(bytes)); 170 | } 171 | } catch (SocketException e1) { 172 | e.onError(e1); 173 | } 174 | e.onComplete(); 175 | }) 176 | .subscribeOn(Schedulers.io()) 177 | .subscribe(new Observer() { 178 | @Override 179 | public void onSubscribe(Disposable d) { 180 | mCompositeDisposable.add(d); 181 | } 182 | 183 | @Override 184 | public void onNext(ByteBuffer s) { 185 | parseDanmu(s.array()); 186 | } 187 | 188 | @Override 189 | public void onError(Throwable e) { 190 | e.printStackTrace(); 191 | } 192 | 193 | @Override 194 | public void onComplete() { 195 | try { 196 | if (mSocket != null && mSocket.isConnected()) { 197 | mSocket.close(); 198 | } 199 | if (mInputStream != null) { 200 | mInputStream.close(); 201 | } 202 | if (mOutputStream != null) { 203 | mOutputStream.close(); 204 | } 205 | } catch (IOException e) { 206 | e.printStackTrace(); 207 | } 208 | } 209 | }); 210 | } 211 | 212 | private void parseDanmu(byte[] data) { 213 | Flowable.create((FlowableOnSubscribe) e -> { 214 | //弹幕协议头 215 | if (data[0] == PandaDanmuUtil.RECEIVE_MSG[0] && 216 | data[1] == PandaDanmuUtil.RECEIVE_MSG[1] && 217 | data[2] == PandaDanmuUtil.RECEIVE_MSG[2] && 218 | data[3] == PandaDanmuUtil.RECEIVE_MSG[3]) { 219 | 220 | //{"type":"1","time":1477356608,"data":{"from":{"__plat":"android","identity":"30","level":"4","msgcolor":"","nickName":"看了还说了","rid":"45560306","sp_identity":"0","userName":""},"to":{"toroom":"15161"},"content":"我去"}} 221 | String content = new String(data, "UTF-8"); 222 | 223 | int danmuFromIndex = content.indexOf("{\"type"); 224 | int danmuToIndex = content.indexOf("}}"); 225 | 226 | String danmu;//存放弹幕 227 | 228 | danmu = content.substring(danmuFromIndex, danmuToIndex + 2); 229 | if (TextUtils.isEmpty(danmu)) {//为空不发射事件 230 | e.onComplete(); 231 | } 232 | e.onNext(danmu); 233 | e.onComplete(); 234 | } 235 | else if (data[0] == PandaDanmuUtil.HEART_BEAT_RESPONSE[0] &&//心跳包 236 | data[1] == PandaDanmuUtil.HEART_BEAT_RESPONSE[1] && 237 | data[2] == PandaDanmuUtil.HEART_BEAT_RESPONSE[2] && 238 | data[3] == PandaDanmuUtil.HEART_BEAT_RESPONSE[3]) { 239 | e.onComplete(); 240 | } 241 | }, BackpressureStrategy.DROP) 242 | .flatMap(new Function>() { 243 | @Override 244 | public Publisher apply(@NonNull String s) throws Exception { 245 | return Flowable.create(e -> { 246 | //解析弹幕Json 247 | PandaDanmuEntity danmu = new Gson().fromJson(s, PandaDanmuEntity.class); 248 | e.onNext(danmu); 249 | }, BackpressureStrategy.BUFFER); 250 | } 251 | }) 252 | .subscribeOn(Schedulers.io()) 253 | .observeOn(AndroidSchedulers.mainThread()) 254 | .subscribe(new FlowableSubscriber() { 255 | @Override 256 | public void onSubscribe(@NonNull Subscription s) { 257 | mSubscription = s; 258 | s.request(10); 259 | } 260 | 261 | @Override 262 | public void onNext(PandaDanmuEntity danmuEntity) { 263 | EventBus.getDefault().post(new PandaDanmuEvent(danmuEntity, roomId)); 264 | if (TextUtils.equals("1", danmuEntity.getType())) 265 | mView.addDanmu(new BaseDanmu(danmuEntity.getData().getContent(), 1)); 266 | } 267 | 268 | @Override 269 | public void onError(Throwable t) { 270 | t.printStackTrace(); 271 | } 272 | 273 | @Override 274 | public void onComplete() { 275 | 276 | } 277 | }); 278 | 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/settings/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.settings; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.MenuItem; 6 | 7 | import io.playcode.streambox.R; 8 | 9 | public class SettingsActivity extends AppCompatActivity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_settings); 15 | if (getSupportActionBar() != null) { 16 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 17 | } 18 | } 19 | 20 | @Override 21 | public boolean onOptionsItemSelected(MenuItem item) { 22 | if (item.getItemId() == android.R.id.home) { 23 | finish(); 24 | return true; 25 | } 26 | return super.onOptionsItemSelected(item); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/ui/settings/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.ui.settings; 2 | 3 | import android.os.Bundle; 4 | import android.preference.Preference; 5 | import android.preference.PreferenceFragment; 6 | import android.preference.PreferenceScreen; 7 | import android.support.annotation.Nullable; 8 | 9 | import io.playcode.streambox.R; 10 | 11 | /** 12 | * Created by anpoz on 2017/4/23. 13 | */ 14 | 15 | public class SettingsFragment extends PreferenceFragment { 16 | @Override 17 | public void onCreate(@Nullable Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | addPreferencesFromResource(R.xml.settings); 20 | } 21 | 22 | @Override 23 | public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 24 | return super.onPreferenceTreeClick(preferenceScreen, preference); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/util/FormatUtil.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.util; 2 | 3 | /** 4 | * Created by anpoz on 2017/4/16. 5 | */ 6 | 7 | public class FormatUtil { 8 | public static String formatPersonNum(String src) { 9 | String personNum; 10 | if (src.length() <= 4) { 11 | personNum = src; 12 | } else if (src.length() == 5) { 13 | if (src.charAt(1) == '0') { 14 | personNum = src.charAt(0) + "万"; 15 | } else { 16 | personNum = src.charAt(0) + "." + src.charAt(1) + "万"; 17 | } 18 | } else { 19 | personNum = src.substring(0, src.length() - 4) + "万"; 20 | } 21 | return personNum; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/util/ImageLoader.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.util; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.bumptech.glide.Glide; 7 | 8 | /** 9 | * Created by anpoz on 2017/4/12. 10 | */ 11 | 12 | public class ImageLoader { 13 | public static void with(Context context, String imageUrl, ImageView imageView) { 14 | Glide.with(context) 15 | .load(imageUrl) 16 | .crossFade() 17 | .into(imageView); 18 | } 19 | 20 | public static void withCenterCrop(Context context, String imageUrl, ImageView imageView) { 21 | Glide.with(context) 22 | .load(imageUrl) 23 | .crossFade() 24 | .centerCrop() 25 | .into(imageView); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/util/PandaDanmuUtil.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.util; 2 | 3 | import io.playcode.streambox.data.bean.PandaStreamDanmuServerEntity; 4 | 5 | /** 6 | * Created by anpoz on 2017/4/15. 7 | */ 8 | 9 | public class PandaDanmuUtil { 10 | 11 | public final static byte[] START_FLAG = {0x00, 0x06, 0x00, 0x02}; //连接弹幕服务器帧头 12 | public final static byte[] RESPONSE = {0x00, 0x06, 0x00, 0x06}; //连接弹幕服务器响应 13 | public final static byte[] KEEP_ALIVE = {0x00, 0x06, 0x00, 0x00}; //与弹幕服务器心跳心跳保持 14 | public final static byte[] RECEIVE_MSG = {0x00, 0x06, 0x00, 0x03}; //接收到弹幕消息的帧头 15 | public final static byte[] HEART_BEAT_RESPONSE = {0x00, 0x06, 0x00, 0x01};//心跳保持服务器返回的值 16 | public final static int IGNORE_BYTE_LENGTH = 16;//弹幕消息体忽略的字节数 17 | public final static int MAX_AUTO_CONNECT_TIME = 5;//自动断线重连次数 18 | 19 | /** 20 | * 建立连接请求数据包 21 | * 22 | * @param bean 23 | * @return 24 | */ 25 | public static byte[] getConnectData(PandaStreamDanmuServerEntity.DataEntity bean) { 26 | String contentMsg = "u:" + bean.getRid() + "" + 27 | "@" + bean.getAppid() + "" + 28 | "\nk:1" + 29 | "\nt:300" + 30 | "\nts:" + bean.getTs() + "" + 31 | "\nsign:" + bean.getSign() + "" + 32 | "\nauthtype:" + bean.getAuthType(); 33 | byte content[] = contentMsg.getBytes(); 34 | byte length[] = {(byte) (content.length >> 8), (byte) (content.length & 0xff)}; 35 | byte sendMessage[] = new byte[START_FLAG.length + 2 + content.length]; 36 | /** 37 | * 1.帧头 38 | * 2.content[]的数据长度 39 | * 3.content[] 40 | */ 41 | System.arraycopy(START_FLAG, 0, sendMessage, 0, START_FLAG.length); 42 | System.arraycopy(length, 0, sendMessage, START_FLAG.length, length.length); 43 | System.arraycopy(content, 0, sendMessage, START_FLAG.length + length.length, content.length); 44 | return sendMessage; 45 | } 46 | 47 | public static byte[] getHeartData() { 48 | return KEEP_ALIVE; 49 | } 50 | 51 | public static int byte2Int(byte[] bytes) { 52 | int value = 0; 53 | byte temp; 54 | 55 | for (int i = 0; i < bytes.length; i++) { 56 | temp = bytes[i]; 57 | value += (temp & 0xFF) << (8 * i); 58 | } 59 | return value; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/io/playcode/streambox/widget/DanmakuVideoPlayer.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.support.v7.widget.SwitchCompat; 6 | import android.util.AttributeSet; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.shuyu.gsyvideoplayer.GSYVideoManager; 11 | import com.shuyu.gsyvideoplayer.GSYVideoPlayer; 12 | import com.shuyu.gsyvideoplayer.utils.Debuger; 13 | import com.shuyu.gsyvideoplayer.video.GSYBaseVideoPlayer; 14 | import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; 15 | 16 | import java.util.HashMap; 17 | 18 | import io.playcode.streambox.R; 19 | import io.playcode.streambox.data.bean.BaseDanmu; 20 | import master.flame.danmaku.controller.IDanmakuView; 21 | import master.flame.danmaku.danmaku.model.BaseDanmaku; 22 | import master.flame.danmaku.danmaku.model.DanmakuTimer; 23 | import master.flame.danmaku.danmaku.model.IDisplayer; 24 | import master.flame.danmaku.danmaku.model.android.DanmakuContext; 25 | import master.flame.danmaku.danmaku.model.android.Danmakus; 26 | import master.flame.danmaku.danmaku.parser.BaseDanmakuParser; 27 | import master.flame.danmaku.ui.widget.DanmakuView; 28 | 29 | /** 30 | * Created by guoshuyu on 2017/2/16. 31 | *

32 | * 配置弹幕使用的播放器,目前使用的是本地模拟数据。 33 | *

34 | * 模拟数据的弹幕时常比较短,后面的时长点是没有数据的。 35 | *

36 | * 注意:这只是一个例子,演示如何集合弹幕,需要完善如弹出输入弹幕等的,可以自行完善。 37 | * 注意:b站的弹幕so只有v5 v7 x86、没有64,所以记得配置上ndk过滤。 38 | */ 39 | 40 | public class DanmakuVideoPlayer extends StandardGSYVideoPlayer { 41 | 42 | private BaseDanmakuParser mParser;//解析器对象 43 | private IDanmakuView mDanmakuView;//弹幕view 44 | private DanmakuContext mDanmakuContext; 45 | 46 | private SwitchCompat mToggleDanmaku; 47 | 48 | public DanmakuVideoPlayer(Context context, Boolean fullFlag) { 49 | super(context, fullFlag); 50 | } 51 | 52 | public DanmakuVideoPlayer(Context context) { 53 | super(context); 54 | } 55 | 56 | public DanmakuVideoPlayer(Context context, AttributeSet attrs) { 57 | super(context, attrs); 58 | } 59 | 60 | @Override 61 | public int getLayoutId() { 62 | return R.layout.view_danmu_video; 63 | } 64 | 65 | 66 | @Override 67 | protected void init(Context context) { 68 | super.init(context); 69 | mDanmakuView = (DanmakuView) findViewById(R.id.danmaku_view); 70 | mToggleDanmaku = (SwitchCompat) findViewById(R.id.switch_danmu); 71 | //初始化弹幕显示 72 | initDanmaku(); 73 | 74 | mToggleDanmaku.setOnCheckedChangeListener((buttonView, isChecked) -> resolveDanmakuShow()); 75 | } 76 | 77 | @Override 78 | public void onPrepared() { 79 | super.onPrepared(); 80 | onPrepareDanmaku(this); 81 | } 82 | 83 | @Override 84 | public void onVideoPause() { 85 | super.onVideoPause(); 86 | if (mDanmakuView != null && mDanmakuView.isPrepared()) { 87 | mDanmakuView.pause(); 88 | } 89 | } 90 | 91 | @Override 92 | public void onVideoResume() { 93 | super.onVideoResume(); 94 | if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) { 95 | mDanmakuView.resume(); 96 | } 97 | } 98 | 99 | 100 | @Override 101 | public void onCompletion() { 102 | releaseDanmaku(this); 103 | } 104 | 105 | /** 106 | * 处理播放器在全屏切换时,弹幕显示的逻辑 107 | * 需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态 108 | */ 109 | @Override 110 | public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { 111 | GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar); 112 | if (gsyBaseVideoPlayer != null) { 113 | DanmakuVideoPlayer gsyVideoPlayer = (DanmakuVideoPlayer) gsyBaseVideoPlayer; 114 | //对弹幕设置偏移记录 115 | gsyVideoPlayer.getToggleDanmaku().setChecked(getToggleDanmaku().isChecked()); 116 | onPrepareDanmaku(gsyVideoPlayer); 117 | } 118 | return gsyBaseVideoPlayer; 119 | } 120 | 121 | /** 122 | * 处理播放器在退出全屏时,弹幕显示的逻辑 123 | * 需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态 124 | */ 125 | @Override 126 | protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) { 127 | super.resolveNormalVideoShow(oldF, vp, gsyVideoPlayer); 128 | if (gsyVideoPlayer != null) { 129 | DanmakuVideoPlayer gsyDanmaVideoPlayer = (DanmakuVideoPlayer) gsyVideoPlayer; 130 | getToggleDanmaku().setChecked(gsyDanmaVideoPlayer.getToggleDanmaku().isChecked()); 131 | if (gsyDanmaVideoPlayer.getDanmakuView() != null && 132 | gsyDanmaVideoPlayer.getDanmakuView().isPrepared()) { 133 | resolveDanmakuSeek(this, gsyDanmaVideoPlayer.getCurrentPositionWhenPlaying()); 134 | resolveDanmakuShow(); 135 | releaseDanmaku(gsyDanmaVideoPlayer); 136 | } 137 | } 138 | } 139 | 140 | 141 | private void initDanmaku() { 142 | // 设置最大显示行数 143 | HashMap maxLinesPair = new HashMap<>(); 144 | maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 5); // 滚动弹幕最大显示5行 145 | // 设置是否禁止重叠 146 | HashMap overlappingEnablePair = new HashMap<>(); 147 | overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true); 148 | overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true); 149 | 150 | mDanmakuContext = DanmakuContext.create(); 151 | mDanmakuContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3) 152 | .setDuplicateMergingEnabled(false) 153 | .setScrollSpeedFactor(1.2f) 154 | .setScaleTextSize(1.2f) 155 | .setMaximumLines(maxLinesPair) 156 | .preventOverlapping(overlappingEnablePair); 157 | if (mDanmakuView != null) { 158 | mParser = new BaseDanmakuParser() { 159 | @Override 160 | protected Danmakus parse() { 161 | return new Danmakus(); 162 | } 163 | }; 164 | mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() { 165 | @Override 166 | public void updateTimer(DanmakuTimer timer) { 167 | } 168 | 169 | @Override 170 | public void drawingFinished() { 171 | 172 | } 173 | 174 | @Override 175 | public void danmakuShown(BaseDanmaku danmaku) { 176 | } 177 | 178 | @Override 179 | public void prepared() { 180 | if (getDanmakuView() != null) { 181 | getDanmakuView().start(); 182 | resolveDanmakuShow(); 183 | } 184 | } 185 | }); 186 | mDanmakuView.enableDanmakuDrawingCache(true); 187 | } 188 | } 189 | 190 | /** 191 | * 弹幕的显示与关闭 192 | */ 193 | private void resolveDanmakuShow() { 194 | post(() -> { 195 | if (getToggleDanmaku().isChecked()) { 196 | if (!getDanmakuView().isShown()) 197 | getDanmakuView().show(); 198 | } 199 | else { 200 | if (getDanmakuView().isShown()) { 201 | getDanmakuView().hide(); 202 | } 203 | } 204 | }); 205 | } 206 | 207 | /** 208 | * 开始播放弹幕 209 | */ 210 | private void onPrepareDanmaku(DanmakuVideoPlayer gsyVideoPlayer) { 211 | if (gsyVideoPlayer.getDanmakuView() != null && !gsyVideoPlayer.getDanmakuView().isPrepared()) { 212 | gsyVideoPlayer.getDanmakuView().prepare(gsyVideoPlayer.getParser(), 213 | gsyVideoPlayer.getDanmakuContext()); 214 | } 215 | } 216 | 217 | /** 218 | * 弹幕偏移 219 | */ 220 | private void resolveDanmakuSeek(DanmakuVideoPlayer gsyVideoPlayer, long time) { 221 | if (GSYVideoManager.instance().getMediaPlayer() != null && mHadPlay 222 | && gsyVideoPlayer.getDanmakuView() != null && gsyVideoPlayer.getDanmakuView().isPrepared()) { 223 | gsyVideoPlayer.getDanmakuView().seekTo(time); 224 | } 225 | } 226 | 227 | /** 228 | * 释放弹幕控件 229 | */ 230 | private void releaseDanmaku(DanmakuVideoPlayer danmakuVideoPlayer) { 231 | if (danmakuVideoPlayer != null && danmakuVideoPlayer.getDanmakuView() != null) { 232 | Debuger.printfError("release Danmaku!"); 233 | danmakuVideoPlayer.getDanmakuView().release(); 234 | } 235 | } 236 | 237 | public BaseDanmakuParser getParser() { 238 | return mParser; 239 | } 240 | 241 | public DanmakuContext getDanmakuContext() { 242 | return mDanmakuContext; 243 | } 244 | 245 | public IDanmakuView getDanmakuView() { 246 | return mDanmakuView; 247 | } 248 | 249 | public SwitchCompat getToggleDanmaku() { 250 | return mToggleDanmaku; 251 | } 252 | 253 | /** 254 | * 模拟添加弹幕数据 255 | */ 256 | public void addDanmaku(BaseDanmu baseDanmu) { 257 | BaseDanmaku danmaku = mDanmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL); 258 | if (danmaku == null || mDanmakuView == null) { 259 | return; 260 | } 261 | danmaku.text = baseDanmu.getText(); 262 | danmaku.padding = 5; 263 | danmaku.priority = 8; // 可能会被各种过滤器过滤并隐藏显示,所以提高等级 264 | danmaku.isLive = true; 265 | danmaku.setTime(mDanmakuView.getCurrentTime() + 500); 266 | danmaku.textSize = 16f * (mParser.getDisplayer().getDensity() - 0.6f); 267 | switch (baseDanmu.getType()) { 268 | case 1: 269 | danmaku.textColor = Color.WHITE; 270 | break; 271 | case 2: 272 | danmaku.textColor = Color.parseColor("#F06292"); 273 | break; 274 | case 3: 275 | danmaku.textColor = Color.parseColor("#FF8A65"); 276 | break; 277 | case 4: 278 | danmaku.textColor = Color.parseColor("#E57373"); 279 | break; 280 | default: 281 | danmaku.textColor = Color.WHITE; 282 | } 283 | mDanmakuView.addDanmaku(danmaku); 284 | } 285 | 286 | } 287 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_card_stream_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/custom_enlarge.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/custom_shrink.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sync_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_visibility_grey_500_18dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/wrap_ic_visibility_grey_500_18dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_common_stream.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 21 | 22 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 25 | 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_panda_stream.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 21 | 22 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_chatroom.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_common_category.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_common_list.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_panda_category.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_stream_info.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 23 | 24 | 31 | 32 | 45 | 46 | 60 | 61 | 76 | 77 | 90 | 91 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_stream_list.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_chat_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_live_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 17 | 18 | 23 | 24 | 36 | 37 | 43 | 44 | 58 | 59 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_danmu_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 29 | 30 | 42 | 43 | 51 | 52 | 63 | 64 | 76 | 77 | 82 | 83 | 88 | 89 | 100 | 101 | 102 | 107 | 108 | 115 | 116 | 122 | 123 | 130 | 131 | 138 | 139 | 140 | 147 | 148 | 155 | 156 | 157 | 166 | 167 | 177 | 178 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_bottom_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 |

3 | 9 | 15 | 16 | 22 | 23 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 13 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anpoz/StreamBox/873d922b45812dd925e73f745fb5a9abe795f3e2/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anpoz/StreamBox/873d922b45812dd925e73f745fb5a9abe795f3e2/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anpoz/StreamBox/873d922b45812dd925e73f745fb5a9abe795f3e2/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anpoz/StreamBox/873d922b45812dd925e73f745fb5a9abe795f3e2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anpoz/StreamBox/873d922b45812dd925e73f745fb5a9abe795f3e2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #9E9E9E 4 | #616161 5 | #F5F5F5 6 | #607D8B 7 | #212121 8 | #757575 9 | #212121 10 | #BDBDBD 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | StreamBox 3 | 设置 4 | enableMediaCodec 5 | enableRotateViewAuto 6 | enableShowFullAnimation 7 | enableNeedShowWifiTip 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/xml/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | 12 | 17 | 18 | 23 | 24 | 30 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/test/java/io/playcode/streambox/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package io.playcode.streambox; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.1.2-4' 5 | repositories { 6 | jcenter() 7 | google() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.0-beta5' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | jcenter() 21 | maven { url 'https://jitpack.io' } 22 | flatDir{ 23 | dirs 'libs' 24 | } 25 | } 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } 31 | 32 | ext { 33 | supportVersion = "26.0.2" 34 | deps = [ 35 | appcompatv7 : "com.android.support:appcompat-v7:$supportVersion", 36 | supportv4 : "com.android.support:support-v4:$supportVersion", 37 | recyclerviewv7: "com.android.support:recyclerview-v7:$supportVersion", 38 | design : "com.android.support:design:$supportVersion", 39 | cardviewv7 : "com.android.support:cardview-v7:$supportVersion" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Thu Apr 13 11:33:24 CST 2017 16 | systemProp.https.proxyPort=25378 17 | systemProp.http.proxyHost=127.0.0.1 18 | org.gradle.jvmargs=-Xmx1536m 19 | systemProp.https.proxyHost=127.0.0.1 20 | systemProp.http.proxyPort=25378 21 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anpoz/StreamBox/873d922b45812dd925e73f745fb5a9abe795f3e2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Sep 12 18:14:32 GMT+08:00 2017 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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------