├── README.md ├── demo ├── app │ ├── build.gradle │ ├── libs │ │ └── skinktsshortvideo-release.aar │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── ksyun │ │ │ └── ts │ │ │ └── ShortVideoDemo │ │ │ ├── BaseActivity.java │ │ │ ├── ExampleApplication.java │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MineActivity.java │ │ │ ├── SettingActivity.java │ │ │ ├── SplashActivity.java │ │ │ ├── auth │ │ │ └── AuthInterceptor.java │ │ │ ├── model │ │ │ ├── AuthInfo.java │ │ │ ├── ErrorModel.java │ │ │ └── User.java │ │ │ ├── request │ │ │ ├── BodyBuild.java │ │ │ ├── RequestManager.java │ │ │ ├── RequestModel.java │ │ │ └── RequestService.java │ │ │ └── ui │ │ │ ├── CrashHandler.java │ │ │ ├── DefaultOnClick.java │ │ │ ├── GlideCircleTransform.java │ │ │ ├── SelectWindow.java │ │ │ ├── UserLocalization.java │ │ │ └── Utils.java │ │ └── res │ │ ├── anim │ │ ├── pop_enter_anim.xml │ │ └── pop_exit_anim.xml │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-ldpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ ├── black_backimage.png │ │ ├── ic_launcher.png │ │ ├── me_back.png │ │ ├── me_setting.png │ │ ├── player_user_image.png │ │ ├── record_icon.png │ │ ├── sex_man.png │ │ ├── sex_woman.png │ │ ├── splash_back.png │ │ ├── user_camera_icon.png │ │ ├── user_icon.png │ │ └── user_woman_icon.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxxhdpi │ │ └── ic_launcher.png │ │ ├── drawable │ │ ├── login_affirm_btn.xml │ │ ├── login_get_phone_captcha_back.xml │ │ ├── select_window_text_bottom_back.xml │ │ ├── select_window_text_cancel_back.xml │ │ └── select_window_text_top_back.xml │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_mine.xml │ │ ├── activity_recommend.xml │ │ ├── activity_setting.xml │ │ ├── activity_splash.xml │ │ ├── item_upload_video.xml │ │ ├── layout_select_window.xml │ │ └── layout_title_view.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimen.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── file_paths.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle └── libs /README.md: -------------------------------------------------------------------------------- 1 | # 金山云短视频解决方案 KSVSShortVideo_Android 2 | [ ![Download](https://api.bintray.com/packages/ksvc/ksvs/ShortVideoKit/images/download.svg) ](https://bintray.com/ksvc/ksvs/ShortVideoKit/_latestVersion) 3 | 4 | 5 | ## 1 简述 6 | 短视频解决方案专为客户提供端到云到端的一站式解决方案。集视频采集、编辑、上传、转码、存储、智能推荐算法、播放SDK等于一体的真正意义上的一站式SAAS级解决方案。 7 | 8 | 短视频SDK提供了视频采集,实时美颜,专业滤镜,支持视频画面比例裁剪,支持断点续拍,录制变速,多视频导入等功能。 9 | 10 | 短视频服务也支持定制化,用户自行定制UI和界面,充分与业务整合,实现业务的快速发展。 11 | 金山云短视频服务,以重运营,轻研发的服务理念,让您避免复杂的架构设计和编程开发,极大地降低技术对接成本。 12 | ![image1](https://raw.githubusercontent.com/wiki/ksvc/KSVSShortVideoKit_Android/images/8.png) 13 | 14 | 15 | ## 2 项目架构 16 | 短视频解决方案架构图如下: 17 | 18 | ![image](https://raw.githubusercontent.com/wiki/ksvc/KSVSShortVideoKit_Android/images/framework.png) 19 | ### 2.1 架构流程描述 20 | * APP 集成时,需要先鉴权才能使用短视频解决方案后续功能 21 | * 短视频解决方案包含推荐页、播放页、录制页等等特别漂亮绚丽的展示效果 22 | * 解决方案也包含上传头像、查询删除已上传视频等接口 23 | * 解决方案借助于短视频SDK、金山云存储等实现短视频录制、编辑、上传、播放等功能 24 | * 短视频解决方案有一个高大上的视频推荐算法,为每个用户推荐喜爱的视频 25 | 26 | ### 2.2 鉴权流程 27 | 短视频解决方案有一个更加安全、合理的鉴权方案,保证APP以及用户数据的安全。具体的鉴权流程如下: 28 | 1. 申请SDK Token并且调用SDK提供的鉴权接口 29 | 2. APP 向解决方案提供User Token,可以增加二次鉴权逻辑。(注:User Token 可以不提供) 30 | 3. 解决方案向SDK Server 验证SDK Token是否正确 31 | 4. SDK Server 向APP Server验证User Token 是否正确 32 | 33 | 鉴权流程图解如下: 34 | 35 | ![image](https://raw.githubusercontent.com/wiki/ksvc/KSVSShortVideoKit_Android/images/auth.png) 36 | 37 | ### 2.3 高大上的推荐算法 38 | 短视频解决方案提供完整的推荐算法,可以让每个用户都能看到自己喜爱的视频。推荐算法准确性相关因素: 39 | 1. 点赞信息 40 | 2. 播放记录 41 | 3. 用户上传视频时,视频名称、视频标签等对点赞都有影响 42 | 43 | ## 3 功能介绍 44 | * **短视频SDK** 45 | 46 | * [x] 实时美颜:支持录制时,开启关闭美颜效果 47 | * [x] 实时滤镜:支持录制时添加滤镜效果 48 | * [x] 定时拍:录制时延迟拍摄 49 | * [x] 闪光灯: 50 | * [x] 变焦: 51 | * [x] 对焦、爆光度 52 | * [x] 摄像头切换 53 | * [x] 断点续拍、回删:支持断点拍摄及任意一段视频的删除 54 | * [x] 单视频导入 55 | * [x] 视频裁剪:支持视频时长裁剪,支持视频画面比例裁剪,支持填充和裁剪两种模式 56 | * [x] 实时音乐 57 | * [x] 音量调节:支持原声、背景音乐调整,支持静音处理 58 | * [x] 录制变速:录制时支持变速功能,支持音乐变速功能 59 | * [x] 滤镜:支持编辑时添加滤镜效果 60 | * [x] 自定义时长:支持自定义设置最短和最长录制时长 61 | 62 | * **金山云存储** 63 | 64 | * **金山云转码** 65 | 66 | * **智能推荐** 67 | 68 | ## 4 接入流程 69 | 70 | ### 4.1 申请流程 71 | 1. 若购买短视频解决方案套餐包,需进入[金山云短视频解决方案官网](https://www.ksyun.com/post/solution/KSVS),点击“购买套餐包”,确认购买,填写表单信息,授权token会以邮件的形式提供。 72 | 2. 若单独购买短视频SDK,联系金山云销售进行授权申请,或者直接拨打:62927777 转 5120 73 | 74 | ### 4.2 集成流程 75 | 1. 从github下载AAR文件或者直接使用jcenter依赖。 76 | 77 | ``` 78 | github 地址:https://github.com/ksvc/KSVSShortVideoKit_Android 79 | gradle 依赖: compile 'com.ksyun.ts:ShortVideoKit:1.0.0' 80 | ``` 81 | 82 | 1. 通过jcenter依赖其他相关项目 83 | 84 | ``` 85 | // ui 需要 86 | compile 'com.android.support:appcompat-v7:26.+' 87 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 88 | compile 'com.github.bumptech.glide:glide:3.7.0' 89 | compile 'com.android.support:design:26.+' 90 | compile 'com.android.support:support-v4:26.+' 91 | compile 'com.jwenfeng.pulltorefresh:library:1.2.7' 92 | 93 | // 短视频SDK 94 | compile 'com.ksyun.media:libksysv-java:2.0.0' 95 | compile 'com.ksyun.media:libksysv-arm64:2.0.0' 96 | compile 'com.ksyun.media:libksysv-armv7a:2.0.0' 97 | compile 'com.ksyun.media:libksysv-x86:2.0.0' 98 | // KS3 上传需要 99 | compile 'com.android.volley:volley:1.0.0' 100 | compile 'com.ksyun.ks3:ks3androidsdk:1.4.1' 101 | // 魔方贴纸 102 | compile 'com.ksyun.mc:libkmcfilter_sensetime:1.0.5' 103 | compile 'com.ksyun.mc:SenseTimeAR:1.0.4' 104 | // kts需要 105 | compile 'io.reactivex.rxjava2:rxjava:2.0.1' 106 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 107 | compile 'com.squareup.retrofit2:retrofit:2.3.0' 108 | compile 'com.squareup.retrofit2:converter-gson:2.3.0' 109 | compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' 110 | compile 'com.squareup.okhttp3:okhttp:3.9.0' 111 | 112 | ``` 113 | 114 | 1. 在AndroidManifest文件中,注册所需要的权限 115 | 116 | ```xml 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | ``` 136 | 137 | 1. 在AndroidManifest文件中,申请相关的Activity 138 | ```xml 139 | 143 | 144 | 145 | 149 | 150 | 151 | 155 | 156 | 157 | 161 | 162 | 163 | 167 | 168 | 169 | 173 | 174 | 175 | 179 | 180 | 181 | 185 | 186 | 187 | 188 | ``` 189 | 190 | 1. 具体的接口使用,请在WIKI查看:[wiki](https://github.com/ksvc/KSVSShortVideoKit_Android/wiki) 191 | 192 | ## 5 反馈与建议 193 | ### 5.1 反馈模版 194 | |类型|描述| 195 | |:--:|:--:| 196 | |SDK名称 |KSVSShortVideoKit_Android| 197 | |SDK版本 |v1.0.0| 198 | |设备型号 |oppo r9s| 199 | |OS版本 |Android 6.0.1| 200 | |问题描述 |描述问题出现的现象| 201 | |操作描述 |描述经过如何操作出现上述问题| 202 | |额外附件|文本形式控制台log、crash报告、其他辅助信息(界面截屏或录像等)| 203 | ### 5.2 短视频解决方案咨询 204 | 金山云官方产品客服,帮您快速了解对接金山云短视频解决方案: 205 | 206 | ![image](https://raw.githubusercontent.com/wiki/ksvc/KSVSShortVideoKit_Android/images/wechat.png) 207 | ### 5.3 联系方式 208 | * 主页:[金山云](http://www.ksyun.com/) 209 | * 邮箱: zengfanping@kingsoft.com 210 | * QQ讨论群: 211 | * 574179720 视频云技术交流群 212 | * 620036233 视频云Android技术交流 213 | * 以上两个加一个QQ群即可 214 | * Issues: https://github.com/ksvc/KSVSShortVideoKit_Android/issues 215 | -------------------------------------------------------------------------------- /demo/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.0" 6 | defaultConfig { 7 | applicationId "com.ksyun.ts.ShortVideoDemo" 8 | minSdkVersion 19 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0.0" 12 | } 13 | buildTypes { 14 | debug { 15 | minifyEnabled false 16 | debuggable true 17 | testCoverageEnabled false 18 | } 19 | release { 20 | minifyEnabled true 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | sourceSets { 25 | main { 26 | jniLibs.srcDir 'libs' 27 | } 28 | } 29 | } 30 | 31 | repositories { 32 | flatDir { 33 | dirs 'libs' 34 | } 35 | } 36 | 37 | dependencies { 38 | compile fileTree(dir: 'libs', include: ['*.jar']) 39 | compile(name:'skinktsshortvideo-release', ext: 'aar') 40 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 41 | exclude group: 'com.android.support', module: 'support-annotations' 42 | }) 43 | testCompile 'junit:junit:4.12' 44 | debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' 45 | releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' 46 | compile 'com.tencent.bugly:crashreport:latest.release' 47 | compile 'com.tencent.bugly:nativecrashreport:latest.release' 48 | 49 | // ui 需要 50 | compile 'com.android.support:appcompat-v7:26.+' 51 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 52 | compile 'com.github.bumptech.glide:glide:3.7.0' 53 | compile 'com.android.support:design:26.+' 54 | compile 'com.android.support:support-v4:26.+' 55 | compile 'com.jwenfeng.pulltorefresh:library:1.2.7' 56 | 57 | // 短视频SDK 58 | compile 'com.ksyun.media:libksysv-java:2.0.0' 59 | compile 'com.ksyun.media:libksysv-arm64:2.0.0' 60 | compile 'com.ksyun.media:libksysv-armv7a:2.0.0' 61 | compile 'com.ksyun.media:libksysv-x86:2.0.0' 62 | // KS3 上传需要 63 | compile 'com.android.volley:volley:1.0.0' 64 | compile 'com.ksyun.ks3:ks3androidsdk:1.4.1' 65 | // 魔方贴纸 66 | compile 'com.ksyun.mc:libkmcfilter_sensetime:1.0.5' 67 | compile 'com.ksyun.mc:SenseTimeAR:1.0.4' 68 | // kts需要 69 | compile 'io.reactivex.rxjava2:rxjava:2.0.1' 70 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 71 | compile 'com.squareup.retrofit2:retrofit:2.3.0' 72 | compile 'com.squareup.retrofit2:converter-gson:2.3.0' 73 | compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' 74 | compile 'com.squareup.okhttp3:okhttp:3.9.0' 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /demo/app/libs/skinktsshortvideo-release.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/libs/skinktsshortvideo-release.aar -------------------------------------------------------------------------------- /demo/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 /Users/xiaoqiang/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 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 | -optimizationpasses 5 27 | -dontusemixedcaseclassnames 28 | -dontskipnonpubliclibraryclasses 29 | -dontpreverify 30 | -allowaccessmodification 31 | -verbose 32 | -optimizations !code/simplification/arithmetic,!field/*,field/propagation/value,!class/merging/*,!code/allocation/variable 33 | 34 | -ignorewarnings 35 | -keepattributes EnclosingMethod 36 | -keepattributes InnerClasses 37 | -keepattributes *Annotation* 38 | -dontoptimize 39 | 40 | -keep public class * extends android.app.Activity 41 | -keep public class * extends android.app.Application 42 | -keep public class * extends android.app.Service 43 | -keep public class * extends android.content.BroadcastReceiver 44 | -keep public class * extends android.content.ContentProvider 45 | -keep public class * extends android.app.backup.BackupAgentHelper 46 | -keep public class * extends android.preference.Preference 47 | -keep public class * extends android.view.View 48 | -keep public class com.android.vending.licensing.ILicensingService 49 | -keep class android.support.** {*;} 50 | 51 | -keepclasseswithmembernames class * { 52 | native ; 53 | } 54 | -keep class com.ksyun.ts.shortvideo.** { 55 | *; 56 | } 57 | -keep class com.ksyun.ts.skin.** { 58 | *; 59 | } 60 | 61 | # OkHttp3 62 | -dontwarn okhttp3.** 63 | -keep class okhttp3.**{*;} 64 | -keep class okio.**{*;} 65 | -dontwarn okio.** 66 | # Retrofit 67 | -dontwarn retrofit2.** 68 | -keep class retrofit2.** { *; } 69 | # RxJava RxAndroid 70 | -keep class io.reactivex.** { 71 | *; 72 | } 73 | -dontwarn io.reactivex.** 74 | #GSON 75 | -dontwarn com.google.gson.** 76 | -keep class com.google.gson.** { *;} 77 | # 播放 78 | -dontwarn com.ksyun.media.** 79 | -keep class com.ksyun.media.** { *;} 80 | # KS3 81 | -dontwarn com.ksyun.ks3.** 82 | -dontwarn org.apache.** 83 | -keep class com.ksyun.ks3.** { *;} 84 | -keep class org.apache.** { *;} 85 | 86 | -dontwarn com.squareup.leakcanary.** 87 | -keep class com.squareup.leakcanary.** { *;} 88 | -dontwarn com.tencent.bugly.** 89 | -keep class com.tencent.bugly.** { *;} 90 | 91 | -keep class com.ksy.statlibrary.** { 92 | *; 93 | } 94 | 95 | -keep class com.sensetime.sensear.** { 96 | *; 97 | } 98 | 99 | -keep class com.sensetime.sensear.** { 100 | *; 101 | } 102 | 103 | -keep class com.googlecode.mp4parser.** { 104 | *; 105 | } 106 | 107 | -keep class com.mp4parser.** { 108 | *; 109 | } 110 | 111 | -keep class com.coremedia.iso.** { 112 | *; 113 | } 114 | 115 | -keep class com.ksyun.ts.ShortVideoDemo.** { 116 | *; 117 | } 118 | -------------------------------------------------------------------------------- /demo/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 37 | 40 | 41 | 42 | 46 | 47 | 48 | 52 | 53 | 54 | 58 | 59 | 60 | 64 | 65 | 66 | 70 | 71 | 72 | 76 | 77 | 78 | 82 | 83 | 84 | 88 | 89 | 90 | 94 | 95 | 96 | 100 | 101 | 102 | 106 | 107 | 108 | 112 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo; 2 | 3 | import android.os.Build; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.FragmentActivity; 7 | import android.view.WindowManager; 8 | 9 | /** 10 | * Created by xiaoqiang on 2017/11/20. 11 | */ 12 | 13 | public class BaseActivity extends FragmentActivity { 14 | 15 | protected final static String TAG = BaseActivity.class.getName(); 16 | protected final static String USER_PARAMS = "USER_PARAMS"; 17 | protected static String mUserToken; 18 | protected static String mUserSecret; 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 24 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 25 | // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 26 | // WindowManager.LayoutParams.FLAG_FULLSCREEN); 27 | } 28 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 29 | 30 | } 31 | 32 | @Override 33 | protected void onDestroy() { 34 | super.onDestroy(); 35 | } 36 | 37 | /** 38 | * 在5.0以上手机,因为设置的模式是status 栏透明,所以需要对Title视图增加Padding设置 39 | * 40 | * @return 41 | */ 42 | protected int getBorderTop() { 43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 44 | return getResources().getDimensionPixelOffset(R.dimen.status_hight); 45 | } else { 46 | return 0; 47 | } 48 | } 49 | 50 | public static String getUserToken() { 51 | return mUserToken; 52 | } 53 | 54 | public static String getUserSecret() { 55 | return mUserSecret; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/ExampleApplication.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo; 2 | 3 | import android.app.Application; 4 | 5 | import com.ksyun.ts.ShortVideoDemo.ui.CrashHandler; 6 | import com.ksyun.ts.shortvideo.common.util.KLog; 7 | import com.ksyun.ts.shortvideo.kit.IKSVSShortVideoAuth; 8 | import com.ksyun.ts.skin.KSVSShortVideoKitManager; 9 | import com.ksyun.ts.skin.util.ToastUtils; 10 | import com.squareup.leakcanary.LeakCanary; 11 | import com.tencent.bugly.crashreport.CrashReport; 12 | 13 | /** 14 | * Created by xiaoqiang on 2017/12/5. 15 | */ 16 | 17 | public class ExampleApplication extends Application { 18 | private static final String TAG = ExampleApplication.class.getName(); 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | // setupLeakCanary(); 24 | 25 | CrashHandler crashHandler = CrashHandler.getInstance(); 26 | crashHandler.init(this); 27 | 28 | /* 29 | * 初始化Bugly,需要传入注册时申请的APPID,第三个参数为SDK调试模式开关; 30 | * 建议在测试阶段建议设置成true,发布时设置为false。 31 | * Bugly为应用崩溃日志收集工具,开发者可根据实际情况选择不集成或依赖其它Bug收集工具 32 | */ 33 | CrashReport.initCrashReport(getApplicationContext(), "4e98881bde", false); 34 | 35 | KSVSShortVideoKitManager.addAuthorizeListener(ExampleApplication.this, mAuthListener); 36 | 37 | } 38 | 39 | protected void setupLeakCanary() { 40 | if (LeakCanary.isInAnalyzerProcess(this)) { 41 | // This process is dedicated to LeakCanary for heap analysis. 42 | // You should not init your app in this process. 43 | return; 44 | } 45 | LeakCanary.install(this); 46 | } 47 | 48 | private IKSVSShortVideoAuth.IKSVSShortVideoAuthListener mAuthListener = 49 | new IKSVSShortVideoAuth.IKSVSShortVideoAuthListener() { 50 | @Override 51 | public void onSuccess() { 52 | } 53 | 54 | @Override 55 | public void onFailed(int error, String message) { 56 | KLog.e(TAG, "鉴权失败,错误码:" + error + ",错误原因:" + message); 57 | ToastUtils.showToast(ExampleApplication.this, R.string.login_auth_error); 58 | } 59 | }; 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.support.annotation.Nullable; 9 | import android.text.Editable; 10 | import android.text.TextUtils; 11 | import android.text.TextWatcher; 12 | import android.view.View; 13 | import android.view.Window; 14 | import android.widget.Button; 15 | import android.widget.EditText; 16 | 17 | import com.ksyun.ts.ShortVideoDemo.model.ErrorModel; 18 | import com.ksyun.ts.ShortVideoDemo.model.User; 19 | import com.ksyun.ts.ShortVideoDemo.request.RequestManager; 20 | import com.ksyun.ts.ShortVideoDemo.request.RequestModel; 21 | import com.ksyun.ts.ShortVideoDemo.ui.DefaultOnClick; 22 | import com.ksyun.ts.ShortVideoDemo.ui.UserLocalization; 23 | import com.ksyun.ts.shortvideo.common.util.KLog; 24 | import com.ksyun.ts.shortvideo.kit.IKSVSShortVideoAuth; 25 | import com.ksyun.ts.skin.KSVSShortVideoKitManager; 26 | import com.ksyun.ts.skin.common.KSVSDialodManager; 27 | import com.ksyun.ts.skin.util.ToastUtils; 28 | 29 | import io.reactivex.functions.Consumer; 30 | 31 | /** 32 | * Created by xiaoqiang on 2017/11/20. 33 | */ 34 | 35 | public class LoginActivity extends BaseActivity { 36 | 37 | private final static int MAX_TIMER = 60; 38 | private final static String USER_AUTH_TOKEN = "AKLTAkF1cPsvQsyn3DFZSxLBaf"; 39 | private final static String USER_AUTH_SECRET = "ON8XoXxgwQ/WHgxKvKzkdryPs54AcK2NN/B/zzFMLgTawTv823lbjcnvSv/5Z3OC3w=="; 40 | 41 | private EditText mPhoneEditor; 42 | private Button mBtnCaptcha; 43 | private Button mBtnLogin; 44 | private EditText mCaptcha; 45 | private Handler mHandler; 46 | private int mCurrentTimer = MAX_TIMER; 47 | private String mCaptchaHint; 48 | private RequestManager mRequestManager; 49 | private Dialog mLoadingDialog; 50 | private KSVSShortVideoKitManager mKitManager; 51 | private User mUser; 52 | 53 | public static void logout(Activity context) { 54 | new UserLocalization(context).clearAuthData(); 55 | Intent intent = new Intent(context, LoginActivity.class); 56 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 57 | Intent.FLAG_ACTIVITY_CLEAR_TASK); 58 | context.startActivity(intent); 59 | context.finish(); 60 | } 61 | 62 | @Override 63 | protected void onCreate(@Nullable Bundle savedInstanceState) { 64 | super.onCreate(savedInstanceState); 65 | requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题 66 | setContentView(R.layout.activity_login); 67 | initView(); 68 | } 69 | 70 | private void initView() { 71 | mHandler = new Handler(); 72 | mPhoneEditor = findViewById(R.id.edit_phone); 73 | mPhoneEditor.addTextChangedListener(mTextWatch); 74 | mBtnCaptcha = findViewById(R.id.btn_get_phone); 75 | mBtnLogin = findViewById(R.id.btn_login); 76 | mCaptcha = findViewById(R.id.edit_captcha); 77 | mBtnCaptcha.setOnClickListener(mOnClick); 78 | mBtnLogin.setOnClickListener(mOnClick); 79 | mCaptchaHint = getResources().getString(R.string.login_get_phone); 80 | mRequestManager = new RequestManager(LoginActivity.this); 81 | } 82 | 83 | private Runnable mTimer = new Runnable() { 84 | @Override 85 | public void run() { 86 | mCurrentTimer--; 87 | if (mCurrentTimer <= 0) { 88 | mCurrentTimer = 60; 89 | if (!TextUtils.isEmpty(mPhoneEditor.getText().toString()) && 90 | mPhoneEditor.getText().toString().length() == 11) { 91 | mBtnCaptcha.setEnabled(true); 92 | mBtnCaptcha.setTextColor(getResources().getColor(R.color.login_get_phone_correct)); 93 | } 94 | mBtnCaptcha.setText(mCaptchaHint); 95 | } else { 96 | mHandler.postDelayed(mTimer, 1000); 97 | mBtnCaptcha.setText(mCurrentTimer + "s"); 98 | } 99 | } 100 | }; 101 | 102 | @Override 103 | protected void onDestroy() { 104 | super.onDestroy(); 105 | KSVSShortVideoKitManager.removeAuthorizeListener(LoginActivity.this, mAuthListener); 106 | mCurrentTimer = MAX_TIMER; 107 | if (mHandler != null) 108 | mHandler.removeCallbacksAndMessages(null); 109 | mHandler = null; 110 | if (mRequestManager != null) { 111 | mRequestManager.releaseAllReuqest(); 112 | mRequestManager = null; 113 | } 114 | } 115 | 116 | private void sdkAuth() { 117 | KSVSShortVideoKitManager.addAuthorizeListener(LoginActivity.this, mAuthListener); 118 | KSVSShortVideoKitManager.authorize(LoginActivity.this, 119 | SplashActivity.SDK_AUTH_TOKEN, (mUser != null) ? mUser.getToken() : ""); 120 | 121 | } 122 | 123 | private IKSVSShortVideoAuth.IKSVSShortVideoAuthListener mAuthListener = 124 | new IKSVSShortVideoAuth.IKSVSShortVideoAuthListener() { 125 | @Override 126 | public void onSuccess() { 127 | if (mLoadingDialog != null) { 128 | mLoadingDialog.dismiss(); 129 | mLoadingDialog = null; 130 | } 131 | if (mUser != null) { 132 | new UserLocalization(LoginActivity.this).saveData(mUser); 133 | MainActivity.openMainActivity(LoginActivity.this, mUser); 134 | finish(); 135 | } 136 | } 137 | 138 | @Override 139 | public void onFailed(int error, String message) { 140 | KLog.e(TAG, "鉴权失败,错误码:" + error + ",错误原因:" + message); 141 | ToastUtils.showToast(LoginActivity.this, R.string.login_auth_error); 142 | if (mLoadingDialog != null) { 143 | mLoadingDialog.dismiss(); 144 | mLoadingDialog = null; 145 | } 146 | // 不管是登录过程中鉴权失败。还是正在使用中出现鉴权失败。 147 | } 148 | }; 149 | 150 | private DefaultOnClick mOnClick = new DefaultOnClick(null, new Consumer() { 151 | @Override 152 | public void accept(View view) throws Exception { 153 | mUserToken = USER_AUTH_TOKEN; 154 | mUserSecret = USER_AUTH_SECRET; 155 | if (view.getId() == R.id.btn_get_phone) { // 获取验证码 156 | mBtnCaptcha.setEnabled(false); 157 | mCaptchaHint = getResources().getString(R.string.login_reget_phone); 158 | mBtnCaptcha.setTextColor(getResources(). 159 | getColor(R.color.login_get_phone)); 160 | mHandler.post(mTimer); 161 | mRequestManager.getPhoneVerification(mPhoneEditor.getText().toString(), 162 | new RequestManager.IKSVSRequestListener() { 163 | @Override 164 | public void onSuccess(RequestModel requestModel) { 165 | 166 | } 167 | 168 | @Override 169 | public void onFailed(ErrorModel errorInfo) { 170 | ToastUtils.showToast(LoginActivity.this, 171 | R.string.login_get_captcha_error); 172 | } 173 | }); 174 | } else if (view.getId() == R.id.btn_login) { // 登录 175 | final String phone = mPhoneEditor.getText().toString(); 176 | String code = mCaptcha.getText().toString(); 177 | if (TextUtils.isEmpty(phone) || phone.length() != 11) { 178 | ToastUtils.showToast(LoginActivity.this, R.string.login_editor_phone_error); 179 | } else if (TextUtils.isEmpty(code) || code.length() != 4) { 180 | ToastUtils.showToast(LoginActivity.this, R.string.login_editor_captcha_error); 181 | } else { 182 | mLoadingDialog = KSVSDialodManager.showLoadingDialog(LoginActivity.this, null); 183 | mRequestManager.login(phone, code, new RequestManager.IKSVSRequestListener() { 184 | @Override 185 | public void onSuccess(User user) { 186 | mUser = user; 187 | sdkAuth(); 188 | } 189 | 190 | @Override 191 | public void onFailed(ErrorModel errorInfo) { 192 | if (mLoadingDialog != null) { 193 | mLoadingDialog.dismiss(); 194 | mLoadingDialog = null; 195 | } 196 | if (errorInfo.getCode().equalsIgnoreCase("VerificationCodeNotMatch")) { 197 | ToastUtils.showToast(LoginActivity.this, 198 | R.string.login_captcha_error); 199 | } else { 200 | ToastUtils.showToast(LoginActivity.this, 201 | R.string.login_login_error); 202 | } 203 | } 204 | }); 205 | } 206 | } 207 | } 208 | }); 209 | private TextWatcher mTextWatch = new TextWatcher() { 210 | @Override 211 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 212 | 213 | } 214 | 215 | @Override 216 | public void onTextChanged(CharSequence s, int start, int before, int count) { 217 | mCaptchaHint = getResources().getString(R.string.login_get_phone); 218 | if (s.length() == 11 && mCurrentTimer == MAX_TIMER) { 219 | mBtnCaptcha.setEnabled(true); 220 | mBtnCaptcha.setTextColor(getResources().getColor(R.color.login_get_phone_correct)); 221 | } else { 222 | mBtnCaptcha.setEnabled(false); 223 | mBtnCaptcha.setTextColor(getResources().getColor(R.color.login_get_phone)); 224 | } 225 | } 226 | 227 | @Override 228 | public void afterTextChanged(Editable s) { 229 | 230 | } 231 | }; 232 | 233 | 234 | } 235 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | import android.support.annotation.NonNull; 10 | import android.support.annotation.Nullable; 11 | import android.support.v4.app.ActivityCompat; 12 | import android.support.v4.app.Fragment; 13 | import android.support.v4.app.FragmentTransaction; 14 | import android.support.v4.content.ContextCompat; 15 | import android.view.View; 16 | import android.widget.ImageButton; 17 | import android.widget.LinearLayout; 18 | import android.widget.RelativeLayout; 19 | import android.widget.TextView; 20 | 21 | import com.bumptech.glide.Glide; 22 | import com.ksyun.ts.ShortVideoDemo.model.ErrorModel; 23 | import com.ksyun.ts.ShortVideoDemo.model.User; 24 | import com.ksyun.ts.ShortVideoDemo.request.RequestManager; 25 | import com.ksyun.ts.ShortVideoDemo.ui.DefaultOnClick; 26 | import com.ksyun.ts.ShortVideoDemo.ui.GlideCircleTransform; 27 | import com.ksyun.ts.ShortVideoDemo.ui.UserLocalization; 28 | import com.ksyun.ts.shortvideo.common.util.KLog; 29 | import com.ksyun.ts.skin.KSVSKitDataBuild; 30 | import com.ksyun.ts.skin.KSVSShortVideoKitManager; 31 | import com.ksyun.ts.skin.util.ToastUtils; 32 | 33 | import io.reactivex.functions.Consumer; 34 | 35 | /** 36 | * Created by xiaoqiang on 2017/11/17. 37 | */ 38 | 39 | public class MainActivity extends BaseActivity { 40 | private final static int PERMISSION_REQUEST_CAMERA_AUDIOREC = 1; 41 | private RelativeLayout mTitle; 42 | private Fragment mRecommendFragment; 43 | private User mUser; 44 | private ImageButton mUserIcon; 45 | private TextView mUserName; 46 | private ImageButton mRecordButton; 47 | private final static int REQUEST_OK = 1; 48 | private RequestManager mRequestManager; 49 | private KSVSKitDataBuild mKTSData; 50 | 51 | 52 | public static final void openMainActivity(Context context, User user) { 53 | Intent intent = new Intent(context, MainActivity.class); 54 | intent.putExtra(USER_PARAMS, user); 55 | context.startActivity(intent); 56 | } 57 | 58 | @Override 59 | protected void onCreate(@Nullable Bundle savedInstanceState) { 60 | super.onCreate(savedInstanceState); 61 | setContentView(R.layout.activity_recommend); 62 | mTitle = findViewById(R.id.rl_recommend_title); 63 | 64 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT 65 | , getResources().getDimensionPixelOffset(R.dimen.recomment_title_height)); 66 | params.topMargin = getBorderTop(); 67 | mTitle.setLayoutParams(params); 68 | mUser = (User) getIntent().getSerializableExtra(USER_PARAMS); 69 | mUserToken = mUser.getToken(); 70 | mUserSecret = mUser.getToken(); 71 | 72 | mUserIcon = findViewById(R.id.imgv_user_logo); 73 | mUserName = findViewById(R.id.tv_user_name); 74 | mRecordButton = findViewById(R.id.imgv_record); 75 | 76 | initUser(); 77 | 78 | mUserIcon.setOnClickListener(mOnClick); 79 | mUserName.setOnClickListener(mOnClick); 80 | mRequestManager = new RequestManager(this); 81 | mKTSData = new KSVSKitDataBuild() 82 | .bindMaxRecordTime(1 * 60 * 1000) 83 | .bindMinRecordTime(5 * 1000); 84 | initView(); 85 | } 86 | 87 | private void initView() { 88 | mRecordButton.setOnClickListener(mOnClick); 89 | 90 | /*** 开启推荐页 ***/ 91 | mRecommendFragment = KSVSShortVideoKitManager.newRecommendFragment(mUser.getUid()); 92 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 93 | transaction.add(R.id.fl_recommend, mRecommendFragment); 94 | transaction.commit(); 95 | 96 | mRequestManager.getUserInfo(new RequestManager.IKSVSRequestListener() { 97 | @Override 98 | public void onSuccess(User user) { 99 | mUser.setHeadUrl(user.getHeadUrl()); 100 | mUser.setNickname(user.getNickname()); 101 | mUser.setGender(user.getGender()); 102 | initUser(); 103 | new UserLocalization(MainActivity.this).saveData(mUser); 104 | } 105 | 106 | @Override 107 | public void onFailed(ErrorModel errorInfo) { 108 | if ("NotLoggedIn".equalsIgnoreCase(errorInfo.getCode())) { 109 | ToastUtils.showToast(MainActivity.this, R.string.logout_user); 110 | LoginActivity.logout(MainActivity.this); 111 | } 112 | } 113 | }); 114 | } 115 | 116 | @Override 117 | protected void onDestroy() { 118 | super.onDestroy(); 119 | 120 | if (mRequestManager != null) { 121 | mRequestManager.releaseAllReuqest(); 122 | } 123 | mRequestManager = null; 124 | } 125 | 126 | @Override 127 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 128 | super.onActivityResult(requestCode, resultCode, data); 129 | if (requestCode == REQUEST_OK && resultCode == RESULT_OK) { 130 | mUser = (User) data.getSerializableExtra(USER_PARAMS); 131 | initUser(); 132 | } 133 | } 134 | 135 | @Override 136 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 137 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 138 | switch (requestCode) { 139 | case PERMISSION_REQUEST_CAMERA_AUDIOREC: { 140 | // If request is cancelled, the result arrays are empty. 141 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 142 | KSVSShortVideoKitManager.startRecording(MainActivity.this, mUser.getUid(), 143 | mKTSData.build()); 144 | } else { 145 | ToastUtils.showToast(MainActivity.this, R.string.main_no_camera_permissions); 146 | KLog.e(TAG, "No CAMERA or AudioRecord permission"); 147 | } 148 | break; 149 | } 150 | } 151 | } 152 | 153 | private void startCameraPreviewWithPermCheck() { 154 | int cameraPerm = ContextCompat.checkSelfPermission(MainActivity.this, 155 | Manifest.permission.CAMERA); 156 | int audioPerm = ContextCompat.checkSelfPermission(MainActivity.this, 157 | Manifest.permission.RECORD_AUDIO); 158 | if (cameraPerm != PackageManager.PERMISSION_GRANTED || 159 | audioPerm != PackageManager.PERMISSION_GRANTED) { 160 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 161 | ToastUtils.showToast(MainActivity.this, R.string.main_no_camera_permissions); 162 | KLog.e(TAG, "No CAMERA or AudioRecord permission,check permission"); 163 | } else { 164 | String[] permissions = {Manifest.permission.CAMERA, 165 | Manifest.permission.RECORD_AUDIO, 166 | Manifest.permission.READ_EXTERNAL_STORAGE}; 167 | ActivityCompat.requestPermissions(MainActivity.this, permissions, 168 | PERMISSION_REQUEST_CAMERA_AUDIOREC); 169 | } 170 | } else { 171 | KSVSShortVideoKitManager.startRecording(MainActivity.this, mUser.getUid(), 172 | mKTSData.build()); 173 | } 174 | } 175 | 176 | private void initUser() { 177 | Glide.with(this) 178 | .load(mUser.getHeadUrl()) 179 | .placeholder(R.drawable.kts_player_user_image) 180 | .error(R.drawable.kts_player_user_image) 181 | .transform(new GlideCircleTransform(this)) 182 | .skipMemoryCache(true) 183 | .into(mUserIcon); 184 | mUserName.setText(mUser.getNickname()); 185 | } 186 | 187 | private DefaultOnClick mOnClick = new DefaultOnClick(null, new Consumer() { 188 | @Override 189 | public void accept(View view) throws Exception { 190 | if (view.getId() == R.id.imgv_record) { 191 | // 开启录制页 192 | startCameraPreviewWithPermCheck(); 193 | } else if (view.getId() == R.id.tv_user_name || 194 | view.getId() == R.id.imgv_user_logo) { 195 | MineActivity.openMeSettingActivityForResult(MainActivity.this, mUser, REQUEST_OK); 196 | } 197 | } 198 | }); 199 | } 200 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/MineActivity.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.support.annotation.Nullable; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.view.KeyEvent; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.Button; 15 | import android.widget.ImageView; 16 | import android.widget.TextView; 17 | 18 | import com.bumptech.glide.Glide; 19 | import com.ksyun.ts.ShortVideoDemo.model.User; 20 | import com.ksyun.ts.ShortVideoDemo.ui.DefaultOnClick; 21 | import com.ksyun.ts.ShortVideoDemo.ui.GlideCircleTransform; 22 | import com.ksyun.ts.shortvideo.common.model.ColumnInfo; 23 | import com.ksyun.ts.shortvideo.common.model.MediaInfo; 24 | import com.ksyun.ts.shortvideo.common.model.VideoInfos; 25 | import com.ksyun.ts.shortvideo.kit.IKSVSShortVideoData; 26 | import com.ksyun.ts.skin.KSVSShortVideoKitManager; 27 | import com.ksyun.ts.skin.common.KSVSDialodManager; 28 | import com.ksyun.ts.skin.common.TitleCommonView; 29 | import com.ksyun.ts.skin.ui.BlurImageView; 30 | import com.ksyun.ts.skin.ui.SwipeItemLayout; 31 | import com.ksyun.ts.skin.util.Constant; 32 | import com.ksyun.ts.skin.util.ContextUtil; 33 | import com.ksyun.ts.skin.util.ToastUtils; 34 | 35 | import java.text.SimpleDateFormat; 36 | import java.util.List; 37 | 38 | import io.reactivex.functions.Consumer; 39 | 40 | /** 41 | * Created by xiaoqiang on 2017/11/20. 42 | */ 43 | 44 | public class MineActivity extends BaseActivity { 45 | private TitleCommonView mCommonView; 46 | private final static int REQUESTCODE = 0; 47 | private User mUser; 48 | private GlideCircleTransform mCircleTransform; 49 | private ImageView mUserIcon; 50 | private TextView mUserName; 51 | private TextView mUserID; 52 | private ImageView mSexIcon; 53 | private LinearLayoutManager mLayoutManager; 54 | private RecyclerView mRemoteVideo; 55 | private RemoteVideoAdapter mRemoteAdapter; 56 | private KSVSShortVideoKitManager mKitManager; 57 | private Dialog mLoadingDialog; 58 | private TextView mUploadNum; 59 | private SimpleDateFormat mDataFormat; 60 | private List mMediaInfos; 61 | 62 | public final static void openMeSettingActivityForResult(Activity context, User user, int request) { 63 | Intent intent = new Intent(context, MineActivity.class); 64 | intent.putExtra(USER_PARAMS, user); 65 | context.startActivityForResult(intent, request); 66 | } 67 | 68 | @Override 69 | protected void onCreate(@Nullable Bundle savedInstanceState) { 70 | super.onCreate(savedInstanceState); 71 | setContentView(R.layout.activity_mine); 72 | mUser = (User) getIntent().getSerializableExtra(USER_PARAMS); 73 | mKitManager = new KSVSShortVideoKitManager(this); 74 | mDataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 75 | initView(); 76 | } 77 | 78 | private void initView() { 79 | mCommonView = findViewById(R.id.title_common_view); 80 | mCommonView.setPadding(0, getBorderTop(), 0, 0); 81 | mCommonView.setBackView(R.drawable.kts_white_back); 82 | mCommonView.setOtherView(R.drawable.me_setting); 83 | mCircleTransform = new GlideCircleTransform(this); 84 | mUserIcon = findViewById(R.id.imgv_user_logo); 85 | mUserName = findViewById(R.id.tv_user_name); 86 | mUserID = findViewById(R.id.tv_user_uid); 87 | mSexIcon = findViewById(R.id.imgv_sex_logo); 88 | 89 | mCommonView.setBackClick(new Consumer() { 90 | @Override 91 | public void accept(View view) throws Exception { 92 | onBackClick(); 93 | } 94 | }); 95 | mCommonView.setOtherClick(new Consumer() { 96 | @Override 97 | public void accept(View view) throws Exception { 98 | SettingActivity.openSettingActivityForResult(MineActivity.this, mUser, REQUESTCODE); 99 | } 100 | }); 101 | mUserIcon.setOnClickListener(new DefaultOnClick(null, new Consumer() { 102 | @Override 103 | public void accept(View view) throws Exception { 104 | SettingActivity.openSettingActivityForResult(MineActivity.this, mUser, REQUESTCODE); 105 | } 106 | })); 107 | uploadUser(); 108 | 109 | mLayoutManager = new LinearLayoutManager(MineActivity.this); 110 | mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); 111 | mRemoteVideo = findViewById(R.id.recv_remote_video); 112 | mRemoteVideo.setLayoutManager(mLayoutManager); 113 | mRemoteVideo.addOnItemTouchListener(new SwipeItemLayout.OnSwipeItemTouchListener(this)); 114 | mUploadNum = findViewById(R.id.tv_upload_video); 115 | 116 | mLoadingDialog = KSVSDialodManager.showLoadingDialog(MineActivity.this, null); 117 | String message = getResources().getString(R.string.me_upload_video_size); 118 | message = String.format(message, 0); 119 | mUploadNum.setText(message); 120 | /** 121 | * 作为Demo演示,只是给出前50个上传视频的演示 122 | */ 123 | mKitManager.getUploadVideos(mUser.getUid(), 1, Constant.MAX_LOAD_NUM, 124 | new IKSVSShortVideoData.IKSVSShortVideoRequestListener() { 125 | @Override 126 | public void onRequestDate(VideoInfos a) { 127 | mLoadingDialog.dismiss(); 128 | mLoadingDialog = null; 129 | String message = getResources().getString(R.string.me_upload_video_size); 130 | if (a != null && a.getVideos() != null) { 131 | mMediaInfos = a.getVideos(); 132 | mRemoteAdapter = new RemoteVideoAdapter(MineActivity.this, 133 | mMediaInfos); 134 | mRemoteVideo.setAdapter(mRemoteAdapter); 135 | message = String.format(message, a.getVideos().size()); 136 | mUploadNum.setText(message); 137 | } else { 138 | ToastUtils.showToast(MineActivity.this, 139 | R.string.me_get_upload_video_null); 140 | message = String.format(message, 0); 141 | } 142 | mUploadNum.setText(message); 143 | } 144 | 145 | @Override 146 | public void onRequestFailed(int error, String message) { 147 | mLoadingDialog.dismiss(); 148 | mLoadingDialog = null; 149 | ToastUtils.showToast(MineActivity.this, R.string.me_get_upload_error); 150 | } 151 | }); 152 | } 153 | 154 | @Override 155 | public boolean onKeyDown(int keyCode, KeyEvent event) { 156 | switch (keyCode) { 157 | case KeyEvent.KEYCODE_BACK: 158 | onBackClick(); //覆盖系统返回键进行个性化处理 159 | return true; 160 | default: 161 | break; 162 | } 163 | return super.onKeyDown(keyCode, event); 164 | } 165 | 166 | private void onBackClick() { 167 | Intent intent = new Intent(); 168 | intent.putExtra(USER_PARAMS, mUser); 169 | setResult(RESULT_OK, intent); 170 | finish(); 171 | } 172 | 173 | private void deleteUploadVideo(final MediaInfo info) { 174 | mLoadingDialog = KSVSDialodManager.showLoadingDialog(MineActivity.this, null); 175 | mKitManager.deleteUploadVideos(mUser.getUid(), info.getId(), 176 | new IKSVSShortVideoData.IKSVSShortVideoRequestListener() { 177 | @Override 178 | public void onRequestDate(Boolean a) { 179 | mMediaInfos.remove(info); 180 | mRemoteAdapter.notifyDataSetChanged(); 181 | String message = getResources().getString(R.string.me_upload_video_size); 182 | message = String.format(message, mMediaInfos.size()); 183 | mUploadNum.setText(message); 184 | mLoadingDialog.dismiss(); 185 | } 186 | 187 | @Override 188 | public void onRequestFailed(int error, String message) { 189 | mLoadingDialog.dismiss(); 190 | ToastUtils.showToast(MineActivity.this, R.string.me_delete_video_error); 191 | } 192 | }); 193 | } 194 | 195 | @Override 196 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 197 | super.onActivityResult(requestCode, resultCode, data); 198 | if (requestCode == REQUESTCODE && resultCode == RESULT_OK) { 199 | mUser = (User) data.getSerializableExtra(USER_PARAMS); 200 | uploadUser(); 201 | } 202 | } 203 | 204 | 205 | private void uploadUser() { 206 | Glide.with(this) 207 | .load(mUser.getHeadUrl()) 208 | .placeholder(R.drawable.user_icon) 209 | .error(R.drawable.user_icon) 210 | .dontAnimate() 211 | .transform(mCircleTransform) 212 | .into(mUserIcon); 213 | mUserName.setText(mUser.getNickname()); 214 | String uid = String.format(getResources().getString(R.string.me_setting_uid), 215 | mUser.getUid()); 216 | mUserID.setText(uid); 217 | 218 | if (mUser != null && mUser.getGender() != null && mUser.getGender()) { 219 | mSexIcon.setImageResource(R.drawable.sex_woman); 220 | } else { 221 | mSexIcon.setImageResource(R.drawable.sex_man); 222 | } 223 | } 224 | 225 | @Override 226 | protected void onDestroy() { 227 | super.onDestroy(); 228 | mKitManager.release(); 229 | mKitManager = null; 230 | } 231 | 232 | class RemoteVideoAdapter extends RecyclerView.Adapter { 233 | private Context mContext; 234 | private List mVideos; 235 | 236 | public RemoteVideoAdapter(Context context, List videos) { 237 | this.mContext = context; 238 | this.mVideos = videos; 239 | } 240 | 241 | @Override 242 | public RemoteHolder onCreateViewHolder(ViewGroup parent, int viewType) { 243 | View view = View.inflate(mContext, R.layout.item_upload_video, null); 244 | return new RemoteHolder(view); 245 | } 246 | 247 | @Override 248 | public void onBindViewHolder(RemoteHolder holder, int position) { 249 | holder.bindData(mVideos.get(position)); 250 | } 251 | 252 | @Override 253 | public int getItemCount() { 254 | return mVideos.size(); 255 | } 256 | 257 | private DefaultOnClick mOnClick = new DefaultOnClick(null, new Consumer() { 258 | @Override 259 | public void accept(final View view) throws Exception { 260 | if (view.getId() == R.id.btn_delete) { 261 | KSVSDialodManager.showMessageDialog(mContext, 262 | R.string.me_delete_video_dialog_message, 263 | R.string.me_delete_video_dialog_confirm, 264 | new Consumer() { 265 | @Override 266 | public void accept(Dialog dialog) throws Exception { 267 | deleteUploadVideo((MediaInfo) view.getTag()); 268 | } 269 | }, 270 | R.string.me_delete_video_dialog_cancel, 271 | null); 272 | 273 | } else { 274 | ColumnInfo info = new ColumnInfo(); 275 | info.setId(1); 276 | KSVSShortVideoKitManager.startPlayer((Activity) mContext, 277 | (MediaInfo) view.getTag(), null, mUser.getUid()); 278 | } 279 | } 280 | }); 281 | 282 | class RemoteHolder extends RecyclerView.ViewHolder { 283 | BlurImageView mVideoCover; 284 | TextView mVideoName; 285 | TextView mCreateTimer; 286 | TextView mDuration; 287 | Button mDelete; 288 | View mMain; 289 | 290 | public RemoteHolder(View itemView) { 291 | super(itemView); 292 | mVideoCover = itemView.findViewById(R.id.imgv_video_cover); 293 | mVideoName = itemView.findViewById(R.id.tv_video_name); 294 | mCreateTimer = itemView.findViewById(R.id.tv_create_timer); 295 | mDuration = itemView.findViewById(R.id.tv_video_duration); 296 | mDelete = itemView.findViewById(R.id.btn_delete); 297 | mDelete.setOnClickListener(mOnClick); 298 | mMain = itemView.findViewById(R.id.main); 299 | mMain.setOnClickListener(mOnClick); 300 | } 301 | 302 | public void bindData(MediaInfo info) { 303 | Glide.with(mContext) 304 | .load(info.getCover()) 305 | .dontAnimate() 306 | .dontTransform() 307 | .placeholder(R.drawable.kts_recommend_back_default_1) 308 | .error(R.drawable.kts_recommend_back_default_1) 309 | .into(mVideoCover); 310 | mVideoName.setText(info.getName()); 311 | mCreateTimer.setText(mDataFormat.format(info.getTime())); 312 | mDuration.setText(ContextUtil.formatTime((int) (info.getDuration() / 1000))); 313 | mDelete.setTag(info); 314 | mMain.setTag(info); 315 | } 316 | } 317 | } 318 | 319 | } 320 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/SettingActivity.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.app.Dialog; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.pm.PackageManager; 9 | import android.graphics.Bitmap; 10 | import android.net.Uri; 11 | import android.os.Build; 12 | import android.os.Bundle; 13 | import android.provider.MediaStore; 14 | import android.support.annotation.NonNull; 15 | import android.support.annotation.Nullable; 16 | import android.support.v4.app.ActivityCompat; 17 | import android.support.v4.content.FileProvider; 18 | import android.text.TextUtils; 19 | import android.view.View; 20 | import android.view.inputmethod.InputMethodManager; 21 | import android.widget.EditText; 22 | import android.widget.FrameLayout; 23 | import android.widget.ImageView; 24 | import android.widget.LinearLayout; 25 | import android.widget.TextView; 26 | 27 | import com.bumptech.glide.Glide; 28 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 29 | import com.ksyun.ts.ShortVideoDemo.model.ErrorModel; 30 | import com.ksyun.ts.ShortVideoDemo.model.User; 31 | import com.ksyun.ts.ShortVideoDemo.request.RequestManager; 32 | import com.ksyun.ts.ShortVideoDemo.request.RequestModel; 33 | import com.ksyun.ts.ShortVideoDemo.ui.DefaultOnClick; 34 | import com.ksyun.ts.ShortVideoDemo.ui.GlideCircleTransform; 35 | import com.ksyun.ts.ShortVideoDemo.ui.SelectWindow; 36 | import com.ksyun.ts.ShortVideoDemo.ui.UserLocalization; 37 | import com.ksyun.ts.ShortVideoDemo.ui.Utils; 38 | import com.ksyun.ts.shortvideo.common.util.KLog; 39 | import com.ksyun.ts.shortvideo.kit.IKSVSShortVideoListener; 40 | import com.ksyun.ts.shortvideo.kit.IKSVSShortVideoUpload; 41 | import com.ksyun.ts.skin.KSVSShortVideoKitManager; 42 | import com.ksyun.ts.skin.common.KSVSDialodManager; 43 | import com.ksyun.ts.skin.common.TitleCommonView; 44 | import com.ksyun.ts.skin.util.ToastUtils; 45 | 46 | import java.io.File; 47 | 48 | import io.reactivex.Observable; 49 | import io.reactivex.functions.Consumer; 50 | import io.reactivex.schedulers.Schedulers; 51 | 52 | /** 53 | * Created by xiaoqiang on 2017/11/21. 54 | */ 55 | 56 | public class SettingActivity extends BaseActivity { 57 | private final static int PERMISSION_REQUEST_CAMERA = 1; 58 | private final static int PERMISSION_REQUEST_WRITE_EXTERNL = 2; 59 | private static final int TAKE_PHOTO = 1; 60 | private static final int CUT_PHOTO = 2; 61 | private static final int CHOOSE_PHOTO = 3; 62 | private TitleCommonView mCommonView; 63 | private EditText mUserName; 64 | private FrameLayout mUserIconSelect; 65 | private ImageView mUserIcon; 66 | private TextView mSexSelect; 67 | private TextView mLogout; 68 | private SelectWindow mSelectWindow; 69 | private User mUser; 70 | private GlideCircleTransform mCircleTransform; 71 | private RequestManager mRequestManager; 72 | private Dialog mLoadingDialog; 73 | private KSVSShortVideoKitManager mKitManager; 74 | private File mSrcFile; 75 | private File mDstFile; 76 | 77 | public static void openSettingActivityForResult(Activity context, User user, int requestCode) { 78 | Intent intent = new Intent(context, SettingActivity.class); 79 | intent.putExtra(USER_PARAMS, user); 80 | context.startActivityForResult(intent, requestCode); 81 | } 82 | 83 | @Override 84 | protected void onCreate(@Nullable Bundle savedInstanceState) { 85 | super.onCreate(savedInstanceState); 86 | setContentView(R.layout.activity_setting); 87 | mUser = (User) getIntent().getSerializableExtra(USER_PARAMS); 88 | initView(); 89 | } 90 | 91 | private void initView() { 92 | mCommonView = findViewById(R.id.title_common_view); 93 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 94 | LinearLayout.LayoutParams.MATCH_PARENT, 95 | LinearLayout.LayoutParams.WRAP_CONTENT); 96 | params.setMargins(0, getBorderTop(), 0, 0); 97 | mCommonView.setLayoutParams(params); 98 | mCommonView.setBackView(R.drawable.kts_black_back); 99 | mCommonView.setTitle(R.string.setting_title, R.color.setting_title_color); 100 | mCommonView.setOtherView(R.string.setting_save, R.color.setting_title_color); 101 | mCommonView.setBackClick(new Consumer() { 102 | @Override 103 | public void accept(View view) throws Exception { 104 | finish(); 105 | } 106 | }); 107 | mCommonView.setOtherClick(mSaveClick); 108 | 109 | // 头像选择 110 | mUserIconSelect = findViewById(R.id.fl_user_icon_select); 111 | mUserName = findViewById(R.id.edit_user_name_change); 112 | mUserIcon = findViewById(R.id.imgv_user_logo); 113 | mSexSelect = findViewById(R.id.tv_sex_select); 114 | mLogout = findViewById(R.id.tv_logout); 115 | mCircleTransform = new GlideCircleTransform(this); 116 | 117 | mUserName.setHint(mUser.getNickname()); 118 | Glide.with(this) 119 | .load(mUser.getHeadUrl()) 120 | .placeholder(R.drawable.user_icon) 121 | .error(R.drawable.user_icon) 122 | .dontAnimate() 123 | .transform(mCircleTransform) 124 | .into(mUserIcon); 125 | 126 | String sex = (mUser.getGender() == null || !mUser.getGender()) ? "男" : "女"; 127 | mSexSelect.setText(sex); 128 | 129 | mUserIconSelect.setOnClickListener(mOnClick); 130 | mSexSelect.setOnClickListener(mOnClick); 131 | mLogout.setOnClickListener(mOnClick); 132 | 133 | mRequestManager = new RequestManager(SettingActivity.this); 134 | 135 | mKitManager = new KSVSShortVideoKitManager(SettingActivity.this); 136 | } 137 | 138 | 139 | private void setUserInfo(String userIcon) { 140 | mRequestManager.setUserInfo(userIcon, mUser.getNickname(), 141 | (mUser.getGender() != null) ? mUser.getGender() : false, 142 | new RequestManager.IKSVSRequestListener() { 143 | @Override 144 | public void onSuccess(User user) { 145 | new UserLocalization(SettingActivity.this). 146 | saveData(mUser); 147 | Intent intent = new Intent(); 148 | intent.putExtra(USER_PARAMS, mUser); 149 | setResult(RESULT_OK, intent); 150 | finish(); 151 | if (mLoadingDialog != null) 152 | mLoadingDialog.dismiss(); 153 | } 154 | 155 | @Override 156 | public void onFailed(ErrorModel errorInfo) { 157 | if ("NotLoggedIn".equalsIgnoreCase(errorInfo.getCode())) { 158 | ToastUtils.showToast(SettingActivity.this, R.string.logout_user); 159 | LoginActivity.logout(SettingActivity.this); 160 | } 161 | if (mLoadingDialog != null) 162 | mLoadingDialog.dismiss(); 163 | } 164 | }); 165 | } 166 | 167 | /** 168 | * 更新用户信息 169 | */ 170 | private Consumer mSaveClick = new Consumer() { 171 | @Override 172 | public void accept(View o) throws Exception { 173 | String name = mUserName.getText().toString().trim(); 174 | if (TextUtils.isEmpty(name)) { 175 | name = mUser.getNickname(); 176 | } 177 | mUser.setNickname(name); 178 | 179 | mLoadingDialog = KSVSDialodManager.showLoadingDialog(SettingActivity.this, null); 180 | 181 | if (mDstFile == null || !mDstFile.exists()) { 182 | setUserInfo(null); 183 | } else { 184 | mKitManager.uploadFile(mUser.getUid(), mDstFile.getPath(), 185 | new IKSVSShortVideoListener() { 186 | @Override 187 | public void onInfo(String type, Bundle data) { 188 | if (type.equals(IKSVSShortVideoListener.KSVS_LISTENER_TYPE_UPLOAD)) { 189 | int status = data.getInt(IKSVSShortVideoListener.KSVS_LISTENER_BUNDLE_STATUS_INT); 190 | if (status == IKSVSShortVideoUpload.UPLOAD_INFO_COMPLETE) { 191 | String path = data.getString(IKSVSShortVideoUpload.UPLOAD_INFO_FILE_PATH); 192 | KLog.d(TAG, "获取到的path是:" + path); 193 | mUser.setHeadUrl(path); 194 | //图片上传成功 195 | setUserInfo(path); 196 | } 197 | } 198 | } 199 | 200 | @Override 201 | public void onError(String type, int error, Bundle data) { 202 | KLog.e(TAG, "个人信息修改失败,error=" + error); 203 | ToastUtils.showToast(SettingActivity.this, R.string.setting_setuser_error); 204 | if (mLoadingDialog != null) 205 | mLoadingDialog.dismiss(); 206 | } 207 | 208 | @Override 209 | public void onProgress(String type, int params, int progress) { 210 | 211 | } 212 | }); 213 | 214 | } 215 | } 216 | }; 217 | 218 | @Override 219 | protected void onDestroy() { 220 | super.onDestroy(); 221 | if (mRequestManager != null) { 222 | mRequestManager.releaseAllReuqest(); 223 | } 224 | mRequestManager = null; 225 | deleteFile(); 226 | } 227 | 228 | /** 229 | * 拍一张效果 230 | * 6.0以上手机需要动态请求权限 231 | */ 232 | private void openCamera() { 233 | int cameraPerm = ActivityCompat.checkSelfPermission(SettingActivity.this, 234 | Manifest.permission.CAMERA); 235 | int writePerm = ActivityCompat.checkSelfPermission(SettingActivity.this, 236 | Manifest.permission.WRITE_EXTERNAL_STORAGE); 237 | 238 | if (cameraPerm != PackageManager.PERMISSION_GRANTED || 239 | writePerm != PackageManager.PERMISSION_GRANTED) { 240 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 241 | ToastUtils.showToast(SettingActivity.this, R.string.setting_no_camera_permissions); 242 | KLog.e(TAG, "No Camera permission,check permission"); 243 | } else { 244 | String[] permissions = {Manifest.permission.CAMERA, 245 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 246 | Manifest.permission.READ_EXTERNAL_STORAGE,}; 247 | ActivityCompat.requestPermissions(SettingActivity.this, permissions, 248 | PERMISSION_REQUEST_CAMERA); 249 | } 250 | } else { 251 | openImageCapture(); 252 | } 253 | } 254 | 255 | /** 256 | * 开启照相功能,文件保存在临时目录下。名称为user_logo 257 | *

258 | * 注意,在7.0及以上的设备,因为权限的问题,所以不能使用URi,必须使用ContentValues包装以下 259 | */ 260 | private void openImageCapture() { 261 | mSrcFile = new File(Utils.getTempLocalVideoPath(SettingActivity.this), 262 | "user_logo.png"); 263 | if (mSrcFile.exists()) { 264 | mSrcFile.delete(); 265 | } 266 | Intent intent = new Intent(); 267 | intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); 268 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { 269 | intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mSrcFile)); 270 | } else { 271 | Uri imgUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", 272 | mSrcFile); 273 | intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri); 274 | } 275 | startActivityForResult(intent, TAKE_PHOTO); 276 | } 277 | 278 | 279 | @Override 280 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 281 | @NonNull int[] grantResults) { 282 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 283 | switch (requestCode) { 284 | case PERMISSION_REQUEST_CAMERA: { 285 | if (grantResults.length >= 2 && 286 | grantResults[0] == PackageManager.PERMISSION_GRANTED && 287 | grantResults[1] == PackageManager.PERMISSION_GRANTED) { 288 | openImageCapture(); 289 | } else { 290 | ToastUtils.showToast(SettingActivity.this, R.string.setting_no_camera_permissions); 291 | } 292 | } 293 | break; 294 | case PERMISSION_REQUEST_WRITE_EXTERNL: 295 | if (grantResults.length > 0 && 296 | grantResults[0] == PackageManager.PERMISSION_GRANTED) { 297 | openImageGallery(); 298 | } else { 299 | ToastUtils.showToast(SettingActivity.this, R.string.setting_no_write_permissions); 300 | } 301 | break; 302 | } 303 | } 304 | 305 | /** 306 | * 打开图库 307 | */ 308 | private void openGallery() { 309 | int writePerm = ActivityCompat.checkSelfPermission(SettingActivity.this, 310 | Manifest.permission.WRITE_EXTERNAL_STORAGE); 311 | 312 | if (writePerm != PackageManager.PERMISSION_GRANTED) { 313 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 314 | ToastUtils.showToast(SettingActivity.this, R.string.setting_no_write_permissions); 315 | KLog.e(TAG, "No write permission,check permission"); 316 | } else { 317 | String[] permissions = { 318 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 319 | Manifest.permission.READ_EXTERNAL_STORAGE,}; 320 | ActivityCompat.requestPermissions(SettingActivity.this, permissions, 321 | PERMISSION_REQUEST_WRITE_EXTERNL); 322 | } 323 | } else { 324 | openImageGallery(); 325 | } 326 | 327 | } 328 | 329 | private void openImageGallery() { 330 | 331 | Intent intent = new Intent(Intent.ACTION_PICK, null); 332 | intent.setDataAndType( 333 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 334 | "image/*"); 335 | startActivityForResult(intent, CHOOSE_PHOTO); 336 | } 337 | 338 | /** 339 | * 图片剪裁 340 | */ 341 | private void cutPhoto(Uri srcUri) { 342 | Intent intent = new Intent("com.android.camera.action.CROP"); 343 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 344 | intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 345 | intent.setDataAndType(srcUri, "image/*"); 346 | intent.putExtra("scale", true); 347 | intent.putExtra("aspectX", 1); 348 | intent.putExtra("aspectY", 1); 349 | intent.putExtra("outputX", 100); 350 | intent.putExtra("outputY", 100); 351 | intent.putExtra("noFaceDetection", true); 352 | intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString()); 353 | intent.putExtra("return-data", false); 354 | mDstFile = new File(Utils.getTempLocalVideoPath(SettingActivity.this), 355 | "user_logo_dst.png"); 356 | if (mDstFile.exists()) { 357 | mDstFile.delete(); 358 | } 359 | intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mDstFile)); 360 | try { 361 | startActivityForResult(intent, CUT_PHOTO);// 启动裁剪程序 362 | } catch (Exception e) { 363 | ToastUtils.showToast(SettingActivity.this, R.string.setting_no_picture); 364 | } 365 | } 366 | 367 | private void deleteFile() { 368 | File[] files = new File[2]; 369 | files[0] = mDstFile != null ? mDstFile : new File(""); 370 | files[1] = mSrcFile != null ? mSrcFile : new File(""); 371 | Observable.fromArray(files) 372 | .observeOn(Schedulers.io()) 373 | .subscribe(new Consumer() { 374 | @Override 375 | public void accept(File file) throws Exception { 376 | if (file != null && file.exists()) { 377 | file.delete(); 378 | } 379 | } 380 | }); 381 | } 382 | 383 | @Override 384 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 385 | super.onActivityResult(requestCode, resultCode, data); 386 | if (resultCode != RESULT_OK) { 387 | return; 388 | } 389 | switch (requestCode) { 390 | case TAKE_PHOTO: 391 | Uri imgUri; 392 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 393 | imgUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", 394 | mSrcFile); 395 | } else { 396 | imgUri = Uri.fromFile(mSrcFile); 397 | } 398 | cutPhoto(imgUri); 399 | break; 400 | case CHOOSE_PHOTO: 401 | cutPhoto(data.getData()); 402 | break; 403 | case CUT_PHOTO: 404 | if (mDstFile != null && mDstFile.exists()) { 405 | Glide.with(SettingActivity.this) 406 | .load(mDstFile) 407 | .placeholder(R.drawable.user_icon) 408 | .error(R.drawable.user_icon) 409 | .skipMemoryCache(true) 410 | .dontAnimate() 411 | .diskCacheStrategy(DiskCacheStrategy.NONE) 412 | .transform(mCircleTransform) 413 | .into(mUserIcon); 414 | } 415 | break; 416 | } 417 | } 418 | 419 | private Consumer mSexSelectClick = new Consumer() { 420 | @Override 421 | public void accept(View o) throws Exception { 422 | int type = (int) o.getTag(); 423 | switch (type) { 424 | case SelectWindow.ITEM1_CLICK: 425 | mSexSelect.setText("男"); 426 | mUser.setGender(false); 427 | break; 428 | case SelectWindow.ITEM2_CLICK: 429 | mSexSelect.setText("女"); 430 | mUser.setGender(true); 431 | break; 432 | } 433 | } 434 | }; 435 | 436 | 437 | private Consumer mUserIconSelectClick = new Consumer() { 438 | @Override 439 | public void accept(View o) throws Exception { 440 | int type = (int) o.getTag(); 441 | switch (type) { 442 | case SelectWindow.ITEM1_CLICK: 443 | openCamera(); 444 | break; 445 | case SelectWindow.ITEM2_CLICK: 446 | openGallery(); 447 | break; 448 | } 449 | } 450 | }; 451 | 452 | 453 | private DefaultOnClick mOnClick = new DefaultOnClick(null, new Consumer() { 454 | @Override 455 | public void accept(View view) throws Exception { 456 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 457 | imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0); 458 | if (view.getId() == R.id.fl_user_icon_select) {// 头像选择 459 | if (mSelectWindow != null) { 460 | mSelectWindow.dismissWindow(); 461 | } 462 | mSelectWindow = new SelectWindow(SettingActivity.this); 463 | mSelectWindow.setViewOnClick(mUserIconSelectClick); 464 | mSelectWindow.showWindow(findViewById(R.id.ll_root_view)); 465 | } else if (view.getId() == R.id.tv_sex_select) { //性别选择 466 | if (mSelectWindow != null) { 467 | mSelectWindow.dismissWindow(); 468 | } 469 | mSelectWindow = new SelectWindow(SettingActivity.this, "男", "女"); 470 | mSelectWindow.setViewOnClick(mSexSelectClick); 471 | mSelectWindow.showWindow(findViewById(R.id.ll_root_view)); 472 | } else if (view.getId() == R.id.tv_logout) { //退出登录 473 | KSVSDialodManager.showMessageDialog( 474 | SettingActivity.this, 475 | R.string.setting_logout_message, 476 | R.string.setting_logout_confirm, 477 | new Consumer

() { 478 | 479 | @Override 480 | public void accept(Dialog dialog) throws Exception { 481 | mRequestManager.logout( 482 | new RequestManager.IKSVSRequestListener() { 483 | @Override 484 | public void onSuccess(RequestModel requestModel) { 485 | KLog.d(TAG, "退出登录接口调用成功"); 486 | } 487 | 488 | @Override 489 | public void onFailed(ErrorModel errorInfo) { 490 | KLog.e(TAG, "退出登录接口调用失败,失败原因是:" + 491 | errorInfo.getMessage()); 492 | } 493 | }); 494 | LoginActivity.logout(SettingActivity.this); 495 | } 496 | }, 497 | R.string.setting_logout_cancel, 498 | null 499 | ); 500 | 501 | } 502 | } 503 | }); 504 | 505 | } 506 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.support.annotation.Nullable; 7 | 8 | import com.ksyun.ts.ShortVideoDemo.model.User; 9 | import com.ksyun.ts.ShortVideoDemo.ui.UserLocalization; 10 | import com.ksyun.ts.shortvideo.common.util.KLog; 11 | import com.ksyun.ts.shortvideo.kit.IKSVSShortVideoAuth; 12 | import com.ksyun.ts.skin.KSVSShortVideoKitManager; 13 | import com.ksyun.ts.skin.util.ToastUtils; 14 | 15 | /** 16 | * Created by xiaoqiang on 2017/11/30. 17 | */ 18 | 19 | public class SplashActivity extends BaseActivity { 20 | public final static String SDK_AUTH_TOKEN = "ff9737a56f0805482b1dff8fb662c784"; 21 | private UserLocalization mUserLocal; 22 | private User mUser; 23 | private long mStartTime; 24 | private Handler mHandler; 25 | 26 | @Override 27 | protected void onCreate(@Nullable Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_splash); 30 | mStartTime = System.currentTimeMillis(); 31 | } 32 | 33 | @Override 34 | protected void onResume() { 35 | super.onResume(); 36 | mUserLocal = new UserLocalization(this); 37 | mUser = mUserLocal.getData(); 38 | mHandler = new Handler(); 39 | if (mUser == null) { 40 | openLoginActivity(); 41 | } else { 42 | sdkAuth(); 43 | } 44 | } 45 | 46 | private void openMainActivity() { 47 | long time = 2 * 1000 - (System.currentTimeMillis() - mStartTime); 48 | if (time > 0) { 49 | mHandler.postDelayed(new Runnable() { 50 | @Override 51 | public void run() { 52 | MainActivity.openMainActivity(SplashActivity.this, mUser); 53 | finish(); 54 | } 55 | }, time); 56 | } else { 57 | MainActivity.openMainActivity(SplashActivity.this, mUser); 58 | finish(); 59 | } 60 | 61 | } 62 | 63 | private void openLoginActivity() { 64 | long time = 2 * 1000 - (System.currentTimeMillis() - mStartTime); 65 | if (time > 0) { 66 | mHandler.postDelayed(new Runnable() { 67 | @Override 68 | public void run() { 69 | Intent intent = new Intent(SplashActivity.this, LoginActivity.class); 70 | startActivity(intent); 71 | finish(); 72 | } 73 | }, time); 74 | } else { 75 | Intent intent = new Intent(SplashActivity.this, LoginActivity.class); 76 | startActivity(intent); 77 | finish(); 78 | } 79 | } 80 | 81 | private void sdkAuth() { 82 | KSVSShortVideoKitManager.addAuthorizeListener(SplashActivity.this, mAuthListener); 83 | KSVSShortVideoKitManager.authorize(SplashActivity.this, 84 | SDK_AUTH_TOKEN, mUser.getToken()); 85 | 86 | } 87 | 88 | private IKSVSShortVideoAuth.IKSVSShortVideoAuthListener mAuthListener = 89 | new IKSVSShortVideoAuth.IKSVSShortVideoAuthListener() { 90 | @Override 91 | public void onSuccess() { 92 | openMainActivity(); 93 | } 94 | 95 | @Override 96 | public void onFailed(int error, String message) { 97 | KLog.e(TAG, "鉴权失败,错误码:" + error + ",错误原因:" + message); 98 | ToastUtils.showToast(SplashActivity.this, R.string.login_auth_error); 99 | // 不管是登录过程中鉴权失败。还是正在使用中出现鉴权失败。 100 | openLoginActivity(); 101 | } 102 | }; 103 | 104 | @Override 105 | protected void onDestroy() { 106 | super.onDestroy(); 107 | KSVSShortVideoKitManager.removeAuthorizeListener(SplashActivity.this, mAuthListener); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/auth/AuthInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.auth; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | 6 | import com.ksyun.ts.ShortVideoDemo.BaseActivity; 7 | import com.ksyun.ts.ShortVideoDemo.ui.Utils; 8 | import com.ksyun.ts.shortvideo.common.util.SystemUtil; 9 | 10 | import java.io.IOException; 11 | import java.util.Set; 12 | import java.util.TreeSet; 13 | 14 | import okhttp3.HttpUrl; 15 | import okhttp3.Interceptor; 16 | import okhttp3.Request; 17 | import okhttp3.Response; 18 | 19 | /** 20 | * Created by xiaoqiang on 2017/11/22. 21 | */ 22 | 23 | public class AuthInterceptor implements Interceptor { 24 | 25 | private Context mContext; 26 | private static final String SIGNSTRING_LINKER = "\n"; 27 | 28 | public AuthInterceptor(Context context) { 29 | this.mContext = context; 30 | } 31 | 32 | public Response intercept(Chain chain) throws IOException { 33 | Request original = chain.request(); 34 | String time = SystemUtil.getTimer(); 35 | Request.Builder builder = original.newBuilder() 36 | .addHeader("X-KMS-OS", "Android") 37 | .addHeader("X-KMS-OSVersion", String.valueOf(Build.VERSION.SDK_INT)) 38 | .addHeader("X-KMS-DevType", SystemUtil.getDevType()) 39 | .addHeader("X-KMS-DeviceId", Utils.getIMEI(mContext)) 40 | .addHeader("X-KMS-Timestamp", time) 41 | .addHeader("X-KMS-PackageName", SystemUtil.getPackageName(mContext)) 42 | .method(original.method(), original.body()); 43 | 44 | 45 | Request request = builder.build(); 46 | 47 | StringBuilder strBuilder = new StringBuilder(); 48 | strBuilder.append(getCredential()); 49 | strBuilder.append(", "); 50 | Set params = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); 51 | params.addAll(request.url().queryParameterNames()); 52 | 53 | strBuilder.append(getSignature(request, time, params)); 54 | String auth = strBuilder.toString(); 55 | 56 | 57 | builder.addHeader("Authorization", auth); 58 | request = builder.build(); 59 | return chain.proceed(request); 60 | } 61 | 62 | private String getCredential() { 63 | return "Credential=" + BaseActivity.getUserToken(); 64 | } 65 | 66 | private String getSignature(Request request, String timer, Set params) { 67 | StringBuilder strBuilder = new StringBuilder(); 68 | strBuilder 69 | .append(request.method().toUpperCase()) 70 | .append(SIGNSTRING_LINKER) 71 | .append(request.url().host().toLowerCase()) 72 | .append(SIGNSTRING_LINKER) 73 | .append(request.url().encodedPath().toLowerCase()) 74 | .append(SIGNSTRING_LINKER); 75 | 76 | 77 | StringBuilder pb = new StringBuilder(); 78 | if (params != null && params.size() > 0) { 79 | HttpUrl url = request.url(); 80 | for (String param : params) { 81 | String value = url.queryParameterValues(param).get(0); 82 | pb 83 | .append(param) 84 | .append("=") 85 | .append(value) 86 | .append("&"); 87 | } 88 | pb.deleteCharAt(pb.length() - 1); 89 | strBuilder 90 | .append(pb.toString()) 91 | .append(SIGNSTRING_LINKER); 92 | } 93 | strBuilder 94 | .append(timer) 95 | .append(BaseActivity.getUserSecret()); 96 | return "Signature=" + Utils.computeMD5(strBuilder.toString()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/model/AuthInfo.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.model; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * Created by xiaoqiang on 2017/11/23. 11 | */ 12 | 13 | public class AuthInfo { 14 | @SerializedName("PackageName") 15 | private String packageName; 16 | @SerializedName("UserRefer") 17 | private String userRefer; 18 | @SerializedName("Interval") 19 | private long interval; 20 | @SerializedName("Timeout") 21 | private long timerOut; 22 | @SerializedName("Modules") 23 | private String modules; 24 | @SerializedName("Message") 25 | private String message; 26 | @SerializedName("LastModified") 27 | private long lastModiafied; 28 | @SerializedName("ModuleList") 29 | private List> moduleList; 30 | @Expose 31 | private long time; // 本地时间。获取到鉴权的时间 32 | 33 | public String getPackageName() { 34 | return packageName; 35 | } 36 | 37 | public void setPackageName(String packageName) { 38 | this.packageName = packageName; 39 | } 40 | 41 | public String getUserRefer() { 42 | return userRefer; 43 | } 44 | 45 | public void setUserRefer(String userRefer) { 46 | this.userRefer = userRefer; 47 | } 48 | 49 | public long getInterval() { 50 | return interval; 51 | } 52 | 53 | public void setInterval(long interval) { 54 | this.interval = interval; 55 | } 56 | 57 | public long getTimerOut() { 58 | return timerOut; 59 | } 60 | 61 | public void setTimerOut(long timerOut) { 62 | this.timerOut = timerOut; 63 | } 64 | 65 | public String getModules() { 66 | return modules; 67 | } 68 | 69 | public void setModules(String modules) { 70 | this.modules = modules; 71 | } 72 | 73 | public String getMessage() { 74 | return message; 75 | } 76 | 77 | public void setMessage(String message) { 78 | this.message = message; 79 | } 80 | 81 | public long getLastModiafied() { 82 | return lastModiafied; 83 | } 84 | 85 | public void setLastModiafied(long lastModiafied) { 86 | this.lastModiafied = lastModiafied; 87 | } 88 | 89 | public List> getModuleList() { 90 | return moduleList; 91 | } 92 | 93 | public void setModuleList(List> moduleList) { 94 | this.moduleList = moduleList; 95 | } 96 | 97 | public long getTime() { 98 | return time; 99 | } 100 | 101 | public void setTime(long time) { 102 | this.time = time; 103 | } 104 | 105 | @Override 106 | public String toString() { 107 | return "AuthInfo{" + 108 | "packageName='" + packageName + '\'' + 109 | ", userRefer='" + userRefer + '\'' + 110 | ", interval=" + interval + 111 | ", timerOut=" + timerOut + 112 | ", modules='" + modules + '\'' + 113 | ", message='" + message + '\'' + 114 | ", lastModiafied=" + lastModiafied + 115 | ", moduleList=" + moduleList + 116 | ", time=" + time + 117 | '}'; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/model/ErrorModel.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by xiaoqiang on 2017/11/20. 7 | */ 8 | 9 | public class ErrorModel { 10 | @SerializedName("Type") 11 | private String type; 12 | @SerializedName("Code") 13 | private String code; 14 | @SerializedName("Message") 15 | private String message; 16 | 17 | public String getType() { 18 | return type; 19 | } 20 | 21 | public void setType(String type) { 22 | this.type = type; 23 | } 24 | 25 | public String getCode() { 26 | return code; 27 | } 28 | 29 | public void setCode(String code) { 30 | this.code = code; 31 | } 32 | 33 | public String getMessage() { 34 | return message; 35 | } 36 | 37 | public void setMessage(String message) { 38 | this.message = message; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/model/User.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by xiaoqiang on 2017/11/20. 7 | */ 8 | 9 | public class User implements Serializable { 10 | private String nickname; 11 | private String uid; 12 | private Boolean gender; 13 | private String token; 14 | private String headUrl; 15 | 16 | public String getNickname() { 17 | return nickname; 18 | } 19 | 20 | public void setNickname(String nickname) { 21 | this.nickname = nickname; 22 | } 23 | 24 | public String getUid() { 25 | return uid; 26 | } 27 | 28 | public void setUid(String uid) { 29 | this.uid = uid; 30 | } 31 | 32 | public Boolean getGender() { 33 | return gender; 34 | } 35 | 36 | public void setGender(Boolean gender) { 37 | this.gender = gender; 38 | } 39 | 40 | public String getToken() { 41 | return token; 42 | } 43 | 44 | public void setToken(String token) { 45 | this.token = token; 46 | } 47 | 48 | public String getHeadUrl() { 49 | return headUrl; 50 | } 51 | 52 | public void setHeadUrl(String headUrl) { 53 | this.headUrl = headUrl; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/request/BodyBuild.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.request; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONException; 5 | import org.json.JSONObject; 6 | 7 | import okhttp3.MediaType; 8 | import okhttp3.RequestBody; 9 | 10 | /** 11 | * Created by xiaoqiang on 2017/11/6. 12 | */ 13 | 14 | public class BodyBuild { 15 | private JSONObject mJson; 16 | 17 | public BodyBuild() { 18 | mJson = new JSONObject(); 19 | } 20 | 21 | public BodyBuild bindDeviceID(String deviceID) { 22 | try { 23 | mJson.put("deviceId", deviceID); 24 | } catch (JSONException e) { 25 | e.printStackTrace(); 26 | } 27 | return this; 28 | } 29 | 30 | public BodyBuild bindMobile(String mobild) { 31 | try { 32 | mJson.put("mobile", mobild); 33 | } catch (JSONException e) { 34 | e.printStackTrace(); 35 | } 36 | return this; 37 | } 38 | 39 | public BodyBuild bindCode(String code) { 40 | try { 41 | mJson.put("code", code); 42 | } catch (JSONException e) { 43 | e.printStackTrace(); 44 | } 45 | return this; 46 | } 47 | 48 | public BodyBuild bindBucket(String bucket) { 49 | try { 50 | mJson.put("bucket", bucket); 51 | } catch (JSONException e) { 52 | e.printStackTrace(); 53 | } 54 | return this; 55 | } 56 | 57 | public BodyBuild bindHeadUrlPath(String headUrlPath) { 58 | try { 59 | mJson.put("headUrlPath", headUrlPath); 60 | } catch (JSONException e) { 61 | e.printStackTrace(); 62 | } 63 | return this; 64 | } 65 | 66 | public BodyBuild bindNickname(String nickname) { 67 | try { 68 | mJson.put("nickname", nickname); 69 | } catch (JSONException e) { 70 | e.printStackTrace(); 71 | } 72 | return this; 73 | } 74 | 75 | public BodyBuild bindGender(boolean gender) { 76 | try { 77 | mJson.put("gender", gender); 78 | } catch (JSONException e) { 79 | e.printStackTrace(); 80 | } 81 | return this; 82 | } 83 | 84 | public RequestBody build() { 85 | if (mJson == null || mJson.length() <= 0) { 86 | return null; 87 | } 88 | return RequestBody.create(MediaType.parse("application/json"), mJson.toString()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/request/RequestManager.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.request; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | 7 | import com.google.gson.Gson; 8 | import com.google.gson.JsonSyntaxException; 9 | import com.ksyun.ts.ShortVideoDemo.auth.AuthInterceptor; 10 | import com.ksyun.ts.ShortVideoDemo.model.ErrorModel; 11 | import com.ksyun.ts.ShortVideoDemo.model.User; 12 | import com.ksyun.ts.ShortVideoDemo.ui.Utils; 13 | import com.ksyun.ts.shortvideo.KSVSError; 14 | 15 | import java.io.IOException; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | import javax.net.ssl.HostnameVerifier; 20 | import javax.net.ssl.SSLSession; 21 | 22 | import io.reactivex.Observer; 23 | import io.reactivex.android.schedulers.AndroidSchedulers; 24 | import io.reactivex.disposables.Disposable; 25 | import io.reactivex.schedulers.Schedulers; 26 | import okhttp3.OkHttpClient; 27 | import okhttp3.RequestBody; 28 | import okhttp3.ResponseBody; 29 | import retrofit2.HttpException; 30 | import retrofit2.Retrofit; 31 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 32 | import retrofit2.converter.gson.GsonConverterFactory; 33 | 34 | /** 35 | * Created by xiaoqiang on 2017/10/27. 36 | */ 37 | 38 | public class RequestManager { 39 | private final static String BASE_URL = "https://kms-svsdk-demo-api.ksyun.com/"; 40 | private Retrofit mRetrofit; 41 | private RequestService mRequestService; 42 | private List mDisposables = new ArrayList(); 43 | private Context mContext; 44 | 45 | public RequestManager(Context context) { 46 | this.mContext = context; 47 | OkHttpClient okHttpClient = new OkHttpClient 48 | .Builder() 49 | .addInterceptor(new AuthInterceptor(context)) 50 | .hostnameVerifier(new HostnameVerifier() { 51 | @Override 52 | public boolean verify(String hostname, SSLSession session) { 53 | return true; 54 | } 55 | }) 56 | .build(); 57 | mRetrofit = new Retrofit.Builder() 58 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 59 | .addConverterFactory(GsonConverterFactory.create()) 60 | .client(okHttpClient) 61 | .baseUrl(BASE_URL) 62 | .build(); 63 | mRequestService = mRetrofit.create(RequestService.class); 64 | } 65 | 66 | 67 | public void getPhoneVerification(String mobile, IKSVSRequestListener listener) { 68 | mRequestService.getPhoneVerification(Utils.getIMEI(mContext), mobile) 69 | .subscribeOn(Schedulers.io()) 70 | .observeOn(AndroidSchedulers.mainThread()) 71 | .subscribe(new RequestObserver(listener)); 72 | } 73 | 74 | public void login(String mobile, String code, IKSVSRequestListener listener) { 75 | RequestBody body = new BodyBuild() 76 | .bindDeviceID(Utils.getIMEI(mContext)) 77 | .bindCode(code) 78 | .bindMobile(mobile) 79 | .build(); 80 | mRequestService.login(body) 81 | .subscribeOn(Schedulers.io()) 82 | .observeOn(AndroidSchedulers.mainThread()) 83 | .subscribe(new RequestObserver(listener)); 84 | } 85 | 86 | public void logout(IKSVSRequestListener listener) { 87 | mRequestService.logout() 88 | .subscribeOn(Schedulers.io()) 89 | .observeOn(AndroidSchedulers.mainThread()) 90 | .subscribe(new RequestObserver(listener)); 91 | } 92 | 93 | public void getUserInfo(IKSVSRequestListener listener) { 94 | mRequestService.getUserInfo() 95 | .subscribeOn(Schedulers.io()) 96 | .observeOn(AndroidSchedulers.mainThread()) 97 | .subscribe(new RequestObserver(listener)); 98 | } 99 | 100 | public void setUserInfo(String headUrlPath, String name, boolean gender, 101 | IKSVSRequestListener listener) { 102 | if (TextUtils.isEmpty(name)) { 103 | name = "大西瓜"; 104 | } 105 | RequestBody body = new BodyBuild() 106 | .bindHeadUrlPath(headUrlPath) 107 | .bindNickname(name) 108 | .bindGender(gender) 109 | .build(); 110 | mRequestService.setUserInfo(body) 111 | .subscribeOn(Schedulers.io()) 112 | .observeOn(AndroidSchedulers.mainThread()) 113 | .subscribe(new RequestObserver(listener)); 114 | } 115 | 116 | 117 | public void releaseAllReuqest() { 118 | for (Disposable disposable : mDisposables) { 119 | disposable.dispose(); 120 | } 121 | mDisposables.clear(); 122 | } 123 | 124 | /*****数据解析*****/ 125 | 126 | /** 127 | * 网络访问成功。需要检查数据是否合法 128 | * 129 | * @param listener 回调 130 | * @param value 网络请求结果 131 | */ 132 | private void onParseData(IKSVSRequestListener listener, RequestModel value) { 133 | 134 | if (value == null) { 135 | ErrorModel info = new ErrorModel(); 136 | info.setCode(String.valueOf(KSVSError.KSVS_REQUEST_ERROR_RESPONSE_NULL)); 137 | info.setMessage("Response null"); 138 | onParseError(listener, info); 139 | } else if (value.getError() != null) {// 这种情况一般不会出现。当有error时,HTTP的状态码都是400以上 140 | onParseError(listener, value.getError()); 141 | } else { 142 | listener.onSuccess(value.getData()); 143 | } 144 | } 145 | 146 | 147 | private void onParseError(IKSVSRequestListener listener, ErrorModel info) { 148 | Log.e(RequestManager.class.getName(), "网络请求失败,错误码:" + 149 | info.getCode() + ",错误原因:" + info.getMessage()); 150 | listener.onFailed(info); 151 | } 152 | 153 | private void onParseError(IKSVSRequestListener listener, ResponseBody errorBody) { 154 | if (listener == null) return; 155 | ErrorModel info = null; 156 | try { 157 | String str = errorBody.string(); 158 | RequestModel data = new Gson().fromJson(str, RequestModel.class); 159 | info = data.getError(); 160 | } catch (IOException e1) { 161 | e1.printStackTrace(); 162 | } catch (JsonSyntaxException e1) { 163 | e1.printStackTrace(); 164 | } catch (Exception e) { 165 | e.printStackTrace(); 166 | } 167 | 168 | if (info == null) { 169 | info = new ErrorModel(); 170 | info.setCode(String.valueOf(KSVSError.KSVS_NETWORK_OTHER_ERROR)); 171 | info.setMessage("error body is null"); 172 | } 173 | onParseError(listener, info); 174 | } 175 | 176 | private void onParseError(IKSVSRequestListener listener, Throwable e) { 177 | if (listener == null) return; 178 | if (e instanceof HttpException) { 179 | HttpException exception = (HttpException) e; 180 | if (exception.response() == null) { 181 | ErrorModel info = new ErrorModel(); 182 | info.setCode(String.valueOf(KSVSError.KSVS_REQUEST_ERROR_RESPONSE_NULL)); 183 | info.setMessage("request error ,Exception response null"); 184 | onParseError(listener, info); 185 | } else { 186 | onParseError(listener, exception.response().errorBody()); 187 | } 188 | } else { 189 | e.printStackTrace(); 190 | ErrorModel info = new ErrorModel(); 191 | info.setCode(String.valueOf(KSVSError.KSVS_REQUEST_ERROR_RESPONSE_NULL)); 192 | info.setMessage("request error ,Exception response null"); 193 | onParseError(listener, info); 194 | } 195 | } 196 | 197 | public interface IKSVSRequestListener { 198 | void onSuccess(T t); 199 | 200 | void onFailed(ErrorModel errorInfo); 201 | } 202 | 203 | /** 204 | * 网络请求默认解析类型 205 | */ 206 | class RequestObserver implements Observer { 207 | protected IKSVSRequestListener mListener; 208 | private Disposable mDisposable; 209 | 210 | RequestObserver(IKSVSRequestListener listener) { 211 | this.mListener = listener; 212 | } 213 | 214 | @Override 215 | public void onSubscribe(Disposable d) { 216 | mDisposable = d; 217 | mDisposables.add(mDisposable); 218 | } 219 | 220 | @Override 221 | public void onNext(RequestModel value) { 222 | onParseData(mListener, value); 223 | } 224 | 225 | @Override 226 | public void onError(Throwable e) { 227 | onParseError(mListener, e); 228 | mDisposable.dispose(); 229 | mDisposables.remove(mDisposable); 230 | } 231 | 232 | @Override 233 | public void onComplete() { 234 | mDisposable.dispose(); 235 | mDisposables.remove(mDisposable); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/request/RequestModel.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.request; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.ksyun.ts.ShortVideoDemo.model.ErrorModel; 5 | 6 | /** 7 | * Created by xiaoqiang on 2017/11/20. 8 | */ 9 | 10 | public class RequestModel { 11 | private String result; 12 | private String message; 13 | @SerializedName("RequestId") 14 | private String requestId; 15 | @SerializedName("Error") 16 | private ErrorModel error; 17 | private T data; 18 | 19 | public String getResult() { 20 | return result; 21 | } 22 | 23 | public void setResult(String result) { 24 | this.result = result; 25 | } 26 | 27 | public String getMessage() { 28 | return message; 29 | } 30 | 31 | public void setMessage(String message) { 32 | this.message = message; 33 | } 34 | 35 | public String getRequestId() { 36 | return requestId; 37 | } 38 | 39 | public void setRequestId(String requestId) { 40 | this.requestId = requestId; 41 | } 42 | 43 | public T getData() { 44 | return data; 45 | } 46 | 47 | public void setData(T data) { 48 | this.data = data; 49 | } 50 | 51 | public ErrorModel getError() { 52 | return error; 53 | } 54 | 55 | public void setError(ErrorModel error) { 56 | this.error = error; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/request/RequestService.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.request; 2 | 3 | import com.ksyun.ts.ShortVideoDemo.model.User; 4 | 5 | import io.reactivex.Observable; 6 | import okhttp3.RequestBody; 7 | import retrofit2.http.Body; 8 | import retrofit2.http.GET; 9 | import retrofit2.http.POST; 10 | import retrofit2.http.Query; 11 | 12 | /** 13 | * Created by xiaoqiang on 2017/10/27. 14 | */ 15 | 16 | interface RequestService { 17 | @GET("api/v1/mobile/code/send") 18 | Observable getPhoneVerification(@Query("deviceId") String deviceId, 19 | @Query("mobile") String mobile); 20 | 21 | @POST("/api/v1/mobile/code/check") 22 | Observable> login(@Body RequestBody body); 23 | 24 | @GET("api/v1/logout") 25 | Observable logout(); 26 | 27 | 28 | @POST("api/v1/user/info/update") 29 | Observable> setUserInfo(@Body RequestBody body); 30 | 31 | 32 | @GET("api/v1/user/info") 33 | Observable> getUserInfo(); 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/ui/CrashHandler.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.ui; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.os.Build; 7 | import android.os.Environment; 8 | import android.os.Looper; 9 | import android.util.Log; 10 | import android.widget.Toast; 11 | 12 | import java.io.File; 13 | import java.io.FileOutputStream; 14 | import java.io.PrintWriter; 15 | import java.io.StringWriter; 16 | import java.io.Writer; 17 | import java.lang.reflect.Field; 18 | import java.text.DateFormat; 19 | import java.text.SimpleDateFormat; 20 | import java.util.Date; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | /** 25 | * Created by xiaoqiang on 2017/3/31. 26 | */ 27 | 28 | public class CrashHandler implements Thread.UncaughtExceptionHandler { 29 | public static final String TAG = "CrashHandler"; 30 | 31 | // 系统默认的UncaughtException处理类 32 | private Thread.UncaughtExceptionHandler mDefaultHandler; 33 | // CrashHandler实例 34 | private static CrashHandler INSTANCE = new CrashHandler(); 35 | // 程序的Context对象 36 | private Context mContext; 37 | // 用来存储设备信息和异常信息 38 | private Map infos = new HashMap(); 39 | 40 | // 用于格式化日期,作为日志文件名的一部分 41 | private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); 42 | 43 | /** 44 | * 保证只有一个CrashHandler实例 45 | */ 46 | private CrashHandler() { 47 | } 48 | 49 | /** 50 | * 获取CrashHandler实例 ,单例模式 51 | */ 52 | public static CrashHandler getInstance() { 53 | return INSTANCE; 54 | } 55 | 56 | /** 57 | * 初始化 58 | * 59 | * @param context 60 | */ 61 | public void init(Context context) { 62 | mContext = context; 63 | // 获取系统默认的UncaughtException处理器 64 | mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); 65 | // 设置该CrashHandler为程序的默认处理器 66 | Thread.setDefaultUncaughtExceptionHandler(this); 67 | } 68 | 69 | /** 70 | * 当UncaughtException发生时会转入该函数来处理 71 | */ 72 | @Override 73 | public void uncaughtException(Thread thread, Throwable ex) { 74 | if (!handleException(ex) && mDefaultHandler != null) { 75 | // 如果用户没有处理则让系统默认的异常处理器来处理 76 | mDefaultHandler.uncaughtException(thread, ex); 77 | } else { 78 | try { 79 | Thread.sleep(3000); 80 | } catch (InterruptedException e) { 81 | Log.e(TAG, "error : ", e); 82 | } 83 | // 退出程序 84 | android.os.Process.killProcess(android.os.Process.myPid()); 85 | System.exit(1); 86 | } 87 | } 88 | 89 | /** 90 | * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 91 | * 92 | * @param ex 93 | * @return true:如果处理了该异常信息;否则返回false. 94 | */ 95 | private boolean handleException(Throwable ex) { 96 | if (ex == null) { 97 | return false; 98 | } 99 | // 使用Toast来显示异常信息 100 | new Thread() { 101 | @Override 102 | public void run() { 103 | Looper.prepare(); 104 | Toast.makeText(mContext, "很抱歉,程序出现异常,正在收集日志,即将退出", Toast.LENGTH_SHORT).show(); 105 | Looper.loop(); 106 | } 107 | }.start(); 108 | // 收集设备参数信息 109 | collectDeviceInfo(mContext); 110 | // 保存日志文件 111 | saveCrashInfo2File(ex); 112 | return true; 113 | } 114 | 115 | /** 116 | * 收集设备参数信息 117 | * 118 | * @param ctx 119 | */ 120 | public void collectDeviceInfo(Context ctx) { 121 | try { 122 | PackageManager pm = ctx.getPackageManager(); 123 | PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), 124 | PackageManager.GET_ACTIVITIES); 125 | if (pi != null) { 126 | String versionName = pi.versionName == null ? "null" 127 | : pi.versionName; 128 | String versionCode = pi.versionCode + ""; 129 | infos.put("versionName", versionName); 130 | infos.put("versionCode", versionCode); 131 | } 132 | } catch (PackageManager.NameNotFoundException e) { 133 | Log.e(TAG, "an error occured when collect package info", e); 134 | } 135 | Field[] fields = Build.class.getDeclaredFields(); 136 | for (Field field : fields) { 137 | try { 138 | field.setAccessible(true); 139 | infos.put(field.getName(), field.get(null).toString()); 140 | Log.d(TAG, field.getName() + " : " + field.get(null)); 141 | } catch (Exception e) { 142 | Log.e(TAG, "an error occured when collect crash info", e); 143 | } 144 | } 145 | } 146 | 147 | /** 148 | * 保存错误信息到文件中 149 | * 150 | * @param ex 151 | * @return 返回文件名称, 便于将文件传送到服务器 152 | */ 153 | private String saveCrashInfo2File(Throwable ex) { 154 | 155 | StringBuffer sb = new StringBuffer(); 156 | for (Map.Entry entry : infos.entrySet()) { 157 | String key = entry.getKey(); 158 | String value = entry.getValue(); 159 | sb.append(key + "=" + value + "\n"); 160 | } 161 | 162 | Writer writer = new StringWriter(); 163 | PrintWriter printWriter = new PrintWriter(writer); 164 | ex.printStackTrace(printWriter); 165 | Throwable cause = ex.getCause(); 166 | while (cause != null) { 167 | cause.printStackTrace(printWriter); 168 | cause = cause.getCause(); 169 | } 170 | printWriter.close(); 171 | String result = writer.toString(); 172 | sb.append(result); 173 | try { 174 | long timestamp = System.currentTimeMillis(); 175 | String time = formatter.format(new Date()); 176 | String fileName = time + "-" + timestamp + ".log"; 177 | // if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 178 | String path = Environment.getExternalStorageDirectory() + "/kts_short_video/crash/"; 179 | File dir = new File(path); 180 | if (!dir.exists()) { 181 | dir.mkdirs(); 182 | } 183 | FileOutputStream fos = new FileOutputStream(path + fileName); 184 | Log.e(TAG, sb.toString()); 185 | fos.write(sb.toString().getBytes()); 186 | fos.close(); 187 | // } 188 | return fileName; 189 | } catch (Exception e) { 190 | Log.e(TAG, "an error occured while writing file...", e); 191 | } 192 | return null; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/ui/DefaultOnClick.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.ui; 2 | 3 | import android.view.View; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import io.reactivex.Observable; 8 | import io.reactivex.ObservableEmitter; 9 | import io.reactivex.ObservableOnSubscribe; 10 | import io.reactivex.android.schedulers.AndroidSchedulers; 11 | import io.reactivex.functions.Consumer; 12 | import io.reactivex.functions.Function; 13 | 14 | /** 15 | * Created by gaoyuanpeng on 2017/2/23. 16 | */ 17 | 18 | public class DefaultOnClick implements View.OnClickListener { 19 | private ObservableEmitter mEmitter; 20 | 21 | public DefaultOnClick(Function function, Consumer consumer2) { 22 | 23 | Observable observable = Observable.create(new ObservableOnSubscribe() { 24 | @Override 25 | public void subscribe(ObservableEmitter emitter) throws Exception { 26 | mEmitter = emitter; 27 | } 28 | }) 29 | .throttleFirst(500, TimeUnit.MILLISECONDS) 30 | .observeOn(AndroidSchedulers.mainThread()); 31 | if (function == null && consumer2 != null) { 32 | observable.subscribe(consumer2); 33 | } else if (function != null && consumer2 != null) { 34 | observable 35 | .map(function) 36 | .subscribe(consumer2); 37 | } else if (function != null) { 38 | observable 39 | .map(function) 40 | .subscribe(new Consumer() { 41 | @Override 42 | public void accept(Object o) throws Exception { 43 | 44 | } 45 | }); 46 | } else { 47 | observable 48 | .subscribe(new Consumer() { 49 | @Override 50 | public void accept(Object o) throws Exception { 51 | 52 | } 53 | }); 54 | } 55 | } 56 | 57 | 58 | @Override 59 | public void onClick(View view) { 60 | mEmitter.onNext(view); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/ui/GlideCircleTransform.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.ui; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapShader; 6 | import android.graphics.Canvas; 7 | import android.graphics.Paint; 8 | 9 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 10 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 11 | 12 | /** 13 | * Created by xiaoqiang on 2017/11/15. 14 | */ 15 | 16 | public class GlideCircleTransform extends BitmapTransformation { 17 | public GlideCircleTransform(Context context) { 18 | super(context); 19 | } 20 | 21 | @Override 22 | protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { 23 | return circleCrop(pool, toTransform); 24 | } 25 | 26 | private static Bitmap circleCrop(BitmapPool pool, Bitmap source) { 27 | if (source == null) return null; 28 | 29 | int size = Math.min(source.getWidth(), source.getHeight()); 30 | int x = (source.getWidth() - size) / 2; 31 | int y = (source.getHeight() - size) / 2; 32 | 33 | // TODO this could be acquired from the pool too 34 | Bitmap squared = Bitmap.createBitmap(source, x, y, size, size); 35 | 36 | Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888); 37 | if (result == null) { 38 | result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); 39 | } 40 | 41 | Canvas canvas = new Canvas(result); 42 | Paint paint = new Paint(); 43 | paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); 44 | paint.setAntiAlias(true); 45 | float r = size / 2f; 46 | canvas.drawCircle(r, r, r, paint); 47 | return result; 48 | } 49 | 50 | @Override public String getId() { 51 | return getClass().getName(); 52 | } 53 | } -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/ui/SelectWindow.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.ui; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.ColorDrawable; 7 | import android.text.TextUtils; 8 | import android.view.Gravity; 9 | import android.view.View; 10 | import android.view.WindowManager; 11 | import android.widget.LinearLayout; 12 | import android.widget.PopupWindow; 13 | import android.widget.TextView; 14 | 15 | import com.ksyun.ts.ShortVideoDemo.R; 16 | 17 | import io.reactivex.functions.Consumer; 18 | import io.reactivex.functions.Function; 19 | 20 | /** 21 | * Created by xiaoqiang on 2017/11/21. 22 | */ 23 | 24 | public class SelectWindow { 25 | 26 | public final static int ITEM1_CLICK = 1; 27 | public final static int ITEM2_CLICK = 2; 28 | public final static int CANCEL_CLICK = 3; 29 | 30 | private PopupWindow mWindow; 31 | private Context mContext; 32 | private String mItem1; 33 | private String mItem2; 34 | private String mCancel; 35 | private View mWindowView; 36 | private TextView mItemView1; 37 | private TextView mItemView2; 38 | private TextView mCancelView; 39 | 40 | 41 | public SelectWindow(Context context) { 42 | this(context, null, null); 43 | } 44 | 45 | public SelectWindow(Context context, String item1, String item2) { 46 | this(context, item1, item2, null); 47 | } 48 | 49 | public SelectWindow(Context context, String item1, String item2, String cancel) { 50 | this.mContext = context; 51 | this.mItem1 = item1; 52 | this.mItem2 = item2; 53 | this.mCancel = cancel; 54 | initView(); 55 | initWindow(mWindowView); 56 | } 57 | 58 | private void initView() { 59 | mWindowView = View.inflate(mContext, R.layout.layout_select_window, null); 60 | mItemView1 = mWindowView.findViewById(R.id.tv_select_item1); 61 | mItemView2 = mWindowView.findViewById(R.id.tv_select_item2); 62 | mCancelView = mWindowView.findViewById(R.id.tv_select_cancel); 63 | 64 | if (!TextUtils.isEmpty(mItem1)) { 65 | mItemView1.setText(mItem1); 66 | } 67 | if (!TextUtils.isEmpty(mItem2)) { 68 | mItemView2.setText(mItem2); 69 | } 70 | if (!TextUtils.isEmpty(mCancel)) { 71 | mCancelView.setText(mCancel); 72 | } 73 | } 74 | 75 | private void initWindow(View view) { 76 | mWindow = new PopupWindow(view, WindowManager.LayoutParams.MATCH_PARENT, 77 | WindowManager.LayoutParams.WRAP_CONTENT); 78 | mWindow.setAnimationStyle(R.style.SelectWindowStyle); 79 | mWindow.setTouchable(true); 80 | mWindow.setFocusable(true); 81 | mWindow.setOutsideTouchable(true); 82 | mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 83 | mWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { 84 | @Override 85 | public void onDismiss() { 86 | WindowManager.LayoutParams lp = ((Activity) mContext).getWindow() 87 | .getAttributes(); 88 | lp.alpha = 1f; 89 | ((Activity) mContext).getWindow().setAttributes(lp); 90 | } 91 | }); 92 | } 93 | 94 | public void setViewOnClick(Consumer onClick) { 95 | DefaultOnClick onclick = new DefaultOnClick(new Function() { 96 | @Override 97 | public View apply(View view) throws Exception { 98 | if (mWindow.isShowing()) { 99 | mWindow.dismiss(); 100 | } 101 | return view; 102 | } 103 | }, onClick); 104 | mItemView1.setOnClickListener(onclick); 105 | mItemView1.setTag(ITEM1_CLICK); 106 | mItemView2.setOnClickListener(onclick); 107 | mItemView2.setTag(ITEM2_CLICK); 108 | mCancelView.setOnClickListener(onclick); 109 | mCancelView.setTag(CANCEL_CLICK); 110 | } 111 | 112 | public void showWindow(View anchor) { 113 | WindowManager.LayoutParams lp = ((Activity) mContext).getWindow() 114 | .getAttributes(); 115 | lp.alpha = 0.5f; 116 | ((Activity) mContext).getWindow().setAttributes(lp); 117 | mWindow.showAtLocation(anchor, Gravity.BOTTOM | Gravity.LEFT, 0, 118 | 0); 119 | } 120 | 121 | public void dismissWindow() { 122 | if (mWindow != null) { 123 | mWindow.dismiss(); 124 | } 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/ui/UserLocalization.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.ui; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.text.TextUtils; 6 | 7 | import com.ksyun.ts.ShortVideoDemo.model.User; 8 | 9 | /** 10 | * Created by xiaoqiang on 2017/11/28. 11 | */ 12 | 13 | public class UserLocalization { 14 | private SharedPreferences mSharedPreferences; 15 | 16 | public UserLocalization(Context context) { 17 | mSharedPreferences = context.getSharedPreferences("user_tab", 18 | Context.MODE_PRIVATE); 19 | } 20 | 21 | public void saveData(User data) { 22 | SharedPreferences.Editor editor = mSharedPreferences.edit(); 23 | editor.putString("nickname", data.getNickname()); 24 | editor.putString("uid", data.getUid()); 25 | editor.putBoolean("gender", data.getGender()); 26 | editor.putString("token", data.getToken()); 27 | editor.putString("headUrl", data.getHeadUrl()); 28 | editor.apply(); 29 | } 30 | 31 | 32 | public User getData() { 33 | User user = new User(); 34 | user.setNickname(mSharedPreferences.getString("nickname", "大西瓜")); 35 | user.setUid(mSharedPreferences.getString("uid", null)); 36 | user.setGender((mSharedPreferences.getBoolean("gender", false))); 37 | user.setToken((mSharedPreferences.getString("token", null))); 38 | user.setHeadUrl((mSharedPreferences.getString("headUrl", ""))); 39 | if (TextUtils.isEmpty(user.getUid()) || TextUtils.isEmpty(user.getToken())) { 40 | return null; 41 | } else { 42 | return user; 43 | } 44 | } 45 | 46 | public void clearAuthData() { 47 | if (mSharedPreferences != null) { 48 | SharedPreferences.Editor editor = mSharedPreferences.edit(); 49 | editor.remove("nickname"); 50 | editor.remove("uid"); 51 | editor.remove("gender"); 52 | editor.remove("token"); 53 | editor.remove("headUrl"); 54 | editor.apply(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /demo/app/src/main/java/com/ksyun/ts/ShortVideoDemo/ui/Utils.java: -------------------------------------------------------------------------------- 1 | package com.ksyun.ts.ShortVideoDemo.ui; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | import android.os.Environment; 6 | import android.provider.Settings; 7 | 8 | import java.io.File; 9 | import java.security.MessageDigest; 10 | import java.security.NoSuchAlgorithmException; 11 | 12 | /** 13 | * Created by xiaoqiang on 2017/11/20. 14 | */ 15 | 16 | public class Utils { 17 | 18 | public static String getIMEI(Context context) { 19 | // TelephonyManager TelephonyMgr = (TelephonyManager) context.getSystemService( 20 | // Context.TELEPHONY_SERVICE); 21 | // return TelephonyMgr.getDeviceId(); 22 | String androidID = Settings.Secure.getString(context.getContentResolver(), 23 | Settings.Secure.ANDROID_ID); 24 | String id = androidID + Build.SERIAL; 25 | return id; 26 | } 27 | 28 | /** 29 | * 视频临时目录,在应用程序退出时需要删除 30 | * 31 | * @param context 32 | * @return 33 | */ 34 | public static String getTempLocalVideoPath(Context context) { 35 | File file = null; 36 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 37 | file = new File(Environment.getExternalStorageDirectory() + "/shortdemo"); 38 | } else { 39 | file = new File(context.getFilesDir() + "/shortdemo"); 40 | } 41 | if (!file.exists()) { 42 | file.mkdirs(); 43 | } 44 | return file.getAbsolutePath(); 45 | } 46 | 47 | public static String computeMD5(String string) { 48 | try { 49 | // ON8XoXxgwQ/WHgxKvKzkdryPs54AcK2NN/B/zzFMLgTawTv823lbjcnvSv/5Z3OC3w== 50 | MessageDigest messageDigest = MessageDigest.getInstance("MD5"); 51 | byte[] digestBytes = messageDigest.digest(string.getBytes()); 52 | return bytesToHexString(digestBytes); 53 | } catch (NoSuchAlgorithmException e) { 54 | throw new IllegalStateException(e); 55 | } 56 | } 57 | 58 | private static String bytesToHexString(byte[] bytes) { 59 | StringBuffer sb = new StringBuffer(); 60 | for (byte b : bytes) { 61 | sb.append(String.format("%02x", b)); 62 | } 63 | return sb.toString(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /demo/app/src/main/res/anim/pop_enter_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /demo/app/src/main/res/anim/pop_exit_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/black_backimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/black_backimage.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/me_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/me_back.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/me_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/me_setting.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/player_user_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/player_user_image.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/record_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/record_icon.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/sex_man.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/sex_man.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/sex_woman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/sex_woman.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/splash_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/splash_back.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/user_camera_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/user_camera_icon.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/user_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/user_icon.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/user_woman_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xhdpi/user_woman_icon.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/KSVSShortVideoKit_Android/65386548cbc3d1e6f16279dcef6affb2623ac14c/demo/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable/login_affirm_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable/login_get_phone_captcha_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable/select_window_text_bottom_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable/select_window_text_cancel_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable/select_window_text_top_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /demo/app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 26 | 27 |