├── .gitignore ├── LICENSE ├── README.md ├── README_en.md ├── app ├── .gitignore ├── build.gradle ├── libs │ └── rtmpx-1.0.3.aar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── rtmpx │ │ └── app │ │ ├── config │ │ └── Const.java │ │ ├── ui │ │ ├── MainActivity.java │ │ └── PublishPreferenceActivity.java │ │ ├── utils │ │ └── Utils.java │ │ └── widget │ │ ├── FocusView.java │ │ └── VerticalSeekBar.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_baseline_brightness_6_24.xml │ ├── ic_baseline_cameraswitch_24.xml │ ├── ic_baseline_lock_24.xml │ ├── ic_baseline_lock_open_24.xml │ ├── ic_baseline_settings_24.xml │ ├── ic_circle_solid.xml │ ├── ic_circle_stroke.xml │ ├── ic_circle_stroke_0_8dp.xml │ ├── ic_divider_0_5dp.xml │ ├── ic_launcher_background.xml │ ├── ic_sun_24dp.xml │ ├── round_corner_grey_5dp.xml │ └── selector_exposure_lock_unlock.xml │ ├── layout │ ├── activity_main.xml │ ├── activity_publish_setting.xml │ ├── vr_view_exposure.xml │ └── vr_view_focus.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values-night │ └── themes.xml │ ├── values-zh-rCN │ └── values.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── rtmpx_library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ ├── flvmuxer │ │ ├── xiecc_rtmp.c │ │ └── xiecc_rtmp.h │ ├── librtmp-jni.c │ ├── librtmp-jni.h │ ├── librtmp │ │ ├── COPYING │ │ ├── Makefile │ │ ├── amf.c │ │ ├── amf.h │ │ ├── bytes.h │ │ ├── dh.h │ │ ├── dhgroups.h │ │ ├── handshake.h │ │ ├── hashswf.c │ │ ├── http.h │ │ ├── librtmp.3 │ │ ├── librtmp.3.html │ │ ├── librtmp.pc.in │ │ ├── log.c │ │ ├── log.h │ │ ├── parseurl.c │ │ ├── rtmp.c │ │ ├── rtmp.h │ │ └── rtmp_sys.h │ ├── rtmpmuxer.c │ ├── yuv │ │ ├── libyuv.h │ │ └── libyuv │ │ │ ├── basic_types.h │ │ │ ├── compare.h │ │ │ ├── compare_row.h │ │ │ ├── convert.h │ │ │ ├── convert_argb.h │ │ │ ├── convert_from.h │ │ │ ├── convert_from_argb.h │ │ │ ├── cpu_id.h │ │ │ ├── macros_msa.h │ │ │ ├── mjpeg_decoder.h │ │ │ ├── planar_functions.h │ │ │ ├── rotate.h │ │ │ ├── rotate_argb.h │ │ │ ├── rotate_row.h │ │ │ ├── row.h │ │ │ ├── scale.h │ │ │ ├── scale_argb.h │ │ │ ├── scale_row.h │ │ │ ├── version.h │ │ │ └── video_common.h │ └── yuv_convert.cpp │ ├── java │ └── com │ │ └── rtmpx │ │ └── library │ │ ├── camera │ │ ├── ICamera.java │ │ ├── ICameraPreviewCallback.java │ │ └── widget │ │ │ └── CameraXImplView.java │ │ ├── config │ │ ├── Config.java │ │ └── Const.java │ │ ├── encode │ │ ├── AudioEncoder.java │ │ ├── Encoder.java │ │ └── VideoEncoder.java │ │ ├── publish │ │ ├── IPublishListener.java │ │ ├── PublisherX.java │ │ └── RTMPPublisher.java │ │ ├── record │ │ ├── audio │ │ │ └── AudioRecorder.java │ │ └── video │ │ │ └── VideoRecorder.java │ │ ├── rtmp │ │ ├── RTMPFrame.java │ │ ├── RTMPMuxer.java │ │ └── RtmpClient.java │ │ ├── utils │ │ └── ConvertUtils.java │ │ └── yuv │ │ ├── YuvFrame.java │ │ └── YuvHelper.java │ └── jniLibs │ ├── arm64-v8a │ └── libyuv.so │ └── armeabi-v7a │ └── libyuv.so └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | /.idea/ 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RTMPX 2 | 3 | [English doc][1] 4 | 5 | [1]: https://github.com/luohaohaha/RTMPX/blob/dev/README_en.md 6 | 7 | RTMPX是一个android的rtmp推流库,采集使用camerax,支持60fps,编码使用mediacodec硬编码,推流使用了librtmp。 8 | 9 | https://user-images.githubusercontent.com/3376376/145600890-4abd9b30-ba70-4f04-b1ca-1409be121838.mp4 10 | 11 | 12 | https://user-images.githubusercontent.com/3376376/145600918-9c88f7b4-5ed5-4bde-9ec9-36c10fa07d81.mp4 13 | 14 | 15 | ### 支持的功能 16 | - [x] 60帧预览、推流(理论上支持更高,只要手机支持,目前pixel2最高设置240fps,但是没效果) 17 | - [x] 边推流边录制(保存到本地) 18 | 19 | ### 已知问题 20 | - [x] 部分机型在竖屏推流60fps的时候性能不够,导致libyuv旋转需要很长时间(例如pixel2,I帧旋转要12ms),导致达不到60fps 21 | - [x] 等你发现 22 | 23 | ### 待优化 24 | - [ ] 添加注释 25 | - [ ] 支持滤镜 26 | - [ ] 竖屏libyuv旋转时长优化 27 | 28 | ### 怎么使用(参考示例app) 29 | #### 1. 依赖 30 | 31 | ##### 1) 下载aar(这种集成aar文件的方式,camerax库是必须依赖) 32 | ``` 33 | def camerax_version = "1.0.0" 34 | // CameraX core library using camera2 implementation 35 | implementation "androidx.camera:camera-camera2:$camerax_version" 36 | // CameraX Lifecycle Library 37 | implementation "androidx.camera:camera-lifecycle:$camerax_version" 38 | // CameraX View class 39 | implementation "androidx.camera:camera-view:1.0.0-alpha24" 40 | ``` 41 | or 42 | 43 | ##### 2) mavenCentral远程依赖 44 | ``` 45 | implementation 'io.github.luohaohaha:rtmpx:latest' 46 | ``` 47 | 48 | 49 | #### 2. 将CameraXImplView 放入布局 50 | 51 | ``` 52 | ... 53 | 57 | ... 58 | ``` 59 | 60 | #### 3. 设置推流配置 61 | ``` 62 | Config config = new Config.ConfigBuilder() 63 | .withBitRate(1000 * 5000) //码率 64 | .withPublishUrl("rtmp://192.168.50.170:18888/test/live") //推流url 65 | .withFrameRate(60)//帧率 66 | .withVideoWidth(1080)//视频宽 67 | .withVideoHeight(1920) //视频高 68 | .withRecordVideo(false)//是否录制 69 | .withRecordVideoPath("sdcard/dump.mp4")//录制文件保存文件 70 | .build(); 71 | ``` 72 | 73 | #### 4. 绑定预览控件 74 | 75 | ``` 76 | CameraXImplView mPreview = findViewById(R.id.preview); 77 | mPreview.setPreviewRange(mConfig.getFrameRate(),mConfig.getFrameRate()); 78 | mPreview.setTargetResolution(mConfig.getVideoWidth(), mConfig.getVideoHeight()); 79 | 80 | PublisherX mPublisher = new PublisherX(mConfig); 81 | mPublisher.bindCamera(mPreview); 82 | mPublisher.setPublishListener(this); 83 | 84 | ``` 85 | 86 | #### 5. 开启预览 & 开始推流 / 停止预览 & 停止推流 87 | 88 | ``` 89 | mPreview.startPreview(); 90 | mPublisher.startPublish(); 91 | ``` 92 | 93 | or 94 | 95 | ``` 96 | mPreview.stopPreview(); 97 | mPublisher.stopPublish(); 98 | ``` 99 | 100 | 101 | 102 | 103 | ### IPublishListener 回调 104 | 105 | ``` 106 | /** 107 | * rtmp连接中 108 | */ 109 | void onConnecting(); 110 | 111 | /** 112 | * rtmp 连接建立成功 113 | */ 114 | void onConnected(); 115 | 116 | /** 117 | * rtmp 连接失败 118 | * @param code error code 失败错误码 119 | */ 120 | void onConnectedFailed(int code); 121 | 122 | /** 123 | * 开始推流 124 | */ 125 | void onStartPublish(); 126 | 127 | /** 128 | * 结束推流 129 | */ 130 | void onStopPublish(); 131 | 132 | /** 133 | * 开始录制 134 | */ 135 | void onStartRecord(); 136 | 137 | /** 138 | * 结束录制 139 | */ 140 | void onStopRecord(); 141 | 142 | /** 143 | * 发送平均帧率统计(带宽不足的情况下会低于设置帧率) 144 | * @param fps avg fps 145 | */ 146 | void onFpsStatistic(int fps); 147 | 148 | /** 149 | * rtmp 断开连接 150 | */ 151 | void onRtmpDisconnect(); 152 | ``` 153 | 154 | 155 | 156 | ## Thanks 157 | [LibRtmp-Client-for-Android][2] 158 | 159 | [2]: https://github.com/ant-media/LibRtmp-Client-for-Android 160 | 161 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | # RTMPX 2 | 3 | 4 | RTMPX is an android rtmp streaming library. It uses camerax for acquisition and supports 60fps. The encoding uses mediacodec hard coding, and the streaming uses librtmp. 5 | 6 | 7 | 8 | https://user-images.githubusercontent.com/3376376/145600890-4abd9b30-ba70-4f04-b1ca-1409be121838.mp4 9 | 10 | 11 | https://user-images.githubusercontent.com/3376376/145600918-9c88f7b4-5ed5-4bde-9ec9-36c10fa07d81.mp4 12 | 13 | 14 | ### Features 15 | - [x] 60-frame preview and streaming (theoretically support higher, as long as the phone supports it, the current pixel2 is set to 240fps, but it has no effect) 16 | - [x] Streaming while recording (save to local) 17 | 18 | ### Known issues 19 | - [x] Some models have insufficient performance when pushing 60fps in the vertical screen, which causes libyuv rotation to take a long time (for example, pixel2, I frame rotation takes 12ms), resulting in failure to reach 60fps 20 | - [x] Waiting for you to find out 21 | 22 | ### To be optimized 23 | - [ ] add notes 24 | - [ ] support filters 25 | - [ ] optimization of vertical screen libyuv rotation 26 | 27 | ### How to use(Refer to sample app) 28 | #### 1. Depend 29 | 30 | ##### 1) Download aar (this way of integrating aar files, the camerax library is a must) 31 | ``` 32 | def camerax_version = "1.0.0" 33 | // CameraX core library using camera2 implementation 34 | implementation "androidx.camera:camera-camera2:$camerax_version" 35 | // CameraX Lifecycle Library 36 | implementation "androidx.camera:camera-lifecycle:$camerax_version" 37 | // CameraX View class 38 | implementation "androidx.camera:camera-view:1.0.0-alpha24" 39 | ``` 40 | or 41 | 42 | ##### 2) mavenCentral() 43 | ``` 44 | implementation 'io.github.luohaohaha:rtmpx:latest' 45 | ``` 46 | 47 | 48 | #### 2. Put CameraXImplView in the xml layout 49 | 50 | ``` 51 | ... 52 | 56 | ... 57 | ``` 58 | 59 | #### 3. set publish config 60 | ``` 61 | Config config = new Config.ConfigBuilder() 62 | .withBitRate(1000 * 5000) //bitrate 63 | .withPublishUrl("rtmp://192.168.50.170:18888/test/live") //publish url 64 | .withFrameRate(60)//fps 65 | .withVideoWidth(1080)//width 66 | .withVideoHeight(1920) //height 67 | .withRecordVideo(false)//record video 68 | .withRecordVideoPath("sdcard/dump.mp4")//path 69 | .build(); 70 | ``` 71 | 72 | #### 4. bind camera preview 73 | 74 | ``` 75 | CameraXImplView mPreview = findViewById(R.id.preview); 76 | mPreview.setPreviewRange(mConfig.getFrameRate(),mConfig.getFrameRate()); 77 | mPreview.setTargetResolution(mConfig.getVideoWidth(), mConfig.getVideoHeight()); 78 | 79 | PublisherX mPublisher = new PublisherX(mConfig); 80 | mPublisher.bindCamera(mPreview); 81 | mPublisher.setPublishListener(this); 82 | 83 | ``` 84 | 85 | #### 5. start preview & start publish 86 | 87 | ``` 88 | mPreview.startPreview(); 89 | mPublisher.startPublish(); 90 | ``` 91 | 92 | or 93 | 94 | ``` 95 | mPreview.stopPreview(); 96 | mPublisher.stopPublish(); 97 | ``` 98 | 99 | 100 | 101 | 102 | ### IPublishListener Callback 103 | 104 | ``` 105 | /** 106 | * rtmp is connecting 107 | */ 108 | void onConnecting(); 109 | 110 | /** 111 | * rtmp connection is successful 112 | */ 113 | void onConnected(); 114 | 115 | /** 116 | * rtmp connection failed 117 | * @param code error code 118 | */ 119 | void onConnectedFailed(int code); 120 | 121 | /** 122 | * Start publishing 123 | */ 124 | void onStartPublish(); 125 | 126 | /** 127 | * Stop publishing 128 | */ 129 | void onStopPublish(); 130 | 131 | /** 132 | * Start recording 133 | */ 134 | void onStartRecord(); 135 | 136 | /** 137 | * Stop recording 138 | */ 139 | void onStopRecord(); 140 | 141 | /** 142 | * fps statistics 143 | * @param fps avg fps 144 | */ 145 | void onFpsStatistic(int fps); 146 | 147 | /** 148 | * rtmp disconnect 149 | */ 150 | void onRtmpDisconnect(); 151 | ``` 152 | 153 | 154 | 155 | ## Thanks 156 | [LibRtmp-Client-for-Android][1] 157 | 158 | [1]: https://github.com/ant-media/LibRtmp-Client-for-Android 159 | 160 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdkVersion 30 7 | buildToolsVersion "30.0.3" 8 | 9 | defaultConfig { 10 | applicationId "com.rtmpx.app" 11 | minSdkVersion 21 12 | targetSdkVersion 29 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | 29 | repositories { 30 | flatDir { 31 | dirs 'libs' 32 | } 33 | } 34 | } 35 | 36 | dependencies { 37 | 38 | implementation 'com.google.android.material:material:1.4.0' 39 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 40 | // implementation project(path: ':rtmpx_library') 41 | implementation 'io.github.luohaohaha:rtmpx:1.0.3' 42 | 43 | 44 | /* implementation(name:"rtmpx-${version}", ext:'aar') 45 | 46 | def camerax_version = "1.0.0" 47 | // CameraX core library using camera2 implementation 48 | implementation "androidx.camera:camera-camera2:$camerax_version" 49 | // CameraX Lifecycle Library 50 | implementation "androidx.camera:camera-lifecycle:$camerax_version" 51 | // CameraX View class 52 | implementation "androidx.camera:camera-view:1.0.0-alpha24"*/ 53 | } -------------------------------------------------------------------------------- /app/libs/rtmpx-1.0.3.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luohaohaha/RTMPX/5efc17ef2a1f69d5c9efa967654b695ee5b6fb98/app/libs/rtmpx-1.0.3.aar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/rtmpx/app/config/Const.java: -------------------------------------------------------------------------------- 1 | package com.rtmpx.app.config; 2 | 3 | /** 4 | * Project: RTMPX
5 | * Package: com.rtmpx.app.config
6 | * ClassName: Const
7 | * Description: TODO
8 | * Date: 2023-01-17 19:21
9 | *

10 | * Author luohao
11 | * Version 1.0
12 | * since JDK 1.6
13 | *

14 | */ 15 | public class Const { 16 | 17 | public static final String PREFERENCE_PUBLISH_CONFIG = "PublishConfig"; 18 | public static final String PREFERENCE_SAVE_CONFIG = "PublishSaveConfig"; 19 | public static final String BITRATE = "bitrate"; 20 | public static final String PUBLISH_URL = "publishUrl"; 21 | public static final String FRAME_RATE = "frameRate"; 22 | public static final String WIDTH = "width"; 23 | public static final String HEIGHT = "height"; 24 | public static final String RECORD = "record"; 25 | public static final String RECORD_PATH = "recordPath"; 26 | public static final String RESULT = "result"; 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/rtmpx/app/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.rtmpx.app.utils; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | import android.view.Display; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | import android.view.WindowManager; 9 | 10 | import java.lang.reflect.Method; 11 | 12 | public class Utils { 13 | 14 | public static int dip2px(Context context, int dp) { 15 | final float density = context.getResources().getDisplayMetrics().density; 16 | return (int) ((float) dp * density + 0.5f); 17 | } 18 | 19 | private static final DisplayMetrics getRealDisplayMetricsForAndroid40(Context context) { 20 | if (context == null) return null; 21 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 22 | Display display = windowManager.getDefaultDisplay(); 23 | DisplayMetrics dm = new DisplayMetrics(); 24 | 25 | Class c; 26 | try { 27 | c = Class.forName("android.view.Display"); 28 | Method method = c.getMethod("getRealMetrics", DisplayMetrics.class); 29 | method.invoke(display, dm); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | } 33 | return dm; 34 | } 35 | 36 | public static int getScreenWidth(Context context) { 37 | if (context == null) return -1; 38 | DisplayMetrics displaymetrics = getRealDisplayMetricsForAndroid40(context); 39 | return displaymetrics.widthPixels; 40 | } 41 | 42 | public static int getScreenHeight(Context context) { 43 | if (context == null) return -1; 44 | DisplayMetrics displaymetrics = getRealDisplayMetricsForAndroid40(context); 45 | return displaymetrics.heightPixels; 46 | } 47 | 48 | public static void autoClickOnce(View view) { 49 | if (null == view) 50 | return; 51 | view.post(() -> autoClickOnce(view, getScreenWidth(view.getContext()) / 2, getScreenHeight(view.getContext()) / 2)); 52 | } 53 | 54 | public static void autoClickOnce(View view, float x, float y) { 55 | if (null == view) 56 | return; 57 | long downTime = System.currentTimeMillis(); 58 | long upTime = downTime + 200; 59 | view.dispatchTouchEvent(obtainEvent(downTime, upTime, x, y, MotionEvent.ACTION_DOWN)); 60 | view.dispatchTouchEvent(obtainEvent(downTime, upTime, x, y, MotionEvent.ACTION_UP)); 61 | } 62 | 63 | private static MotionEvent obtainEvent(long downTime, long eventTime, float x, float y, int action) { 64 | return MotionEvent.obtain(downTime, eventTime, action, x, y, 0); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/rtmpx/app/widget/VerticalSeekBar.java: -------------------------------------------------------------------------------- 1 | package com.rtmpx.app.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | 9 | import androidx.appcompat.widget.AppCompatSeekBar; 10 | 11 | /** 12 | * Project: android_client
13 | * Package: com.sqtech.client.widget
14 | * ClassName: VerticalSeekBar
15 | * Description: TODO
16 | * Date: 2020/12/17 11:24 AM
17 | *

18 | * Author LuoHao
19 | * Version 1.0
20 | * since JDK 1.6
21 | *

22 | */ 23 | public class VerticalSeekBar extends AppCompatSeekBar { 24 | 25 | private int max, min; 26 | 27 | private OnSeekBarChangeListener mSeekBarChangeListener; 28 | 29 | public VerticalSeekBar(Context context) { 30 | super(context); 31 | } 32 | 33 | public VerticalSeekBar(Context context, AttributeSet attrs) { 34 | super(context, attrs); 35 | } 36 | 37 | public VerticalSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { 38 | super(context, attrs, defStyleAttr); 39 | } 40 | 41 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 42 | super.onSizeChanged(h, w, oldh, oldw); 43 | } 44 | 45 | @Override 46 | protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 47 | super.onMeasure(heightMeasureSpec, widthMeasureSpec); 48 | setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); 49 | } 50 | 51 | protected void onDraw(Canvas c) { 52 | c.rotate(-90); 53 | c.translate(-getHeight(), 0); 54 | super.onDraw(c); 55 | } 56 | 57 | @Override 58 | public synchronized void setProgress(int progress) { 59 | super.setProgress(progress); 60 | onSizeChanged(getWidth(), getHeight(), 0, 0); 61 | } 62 | 63 | public synchronized void setSupportMax(int max) { 64 | this.max = max; 65 | try { 66 | super.setMax(max); 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | } 70 | } 71 | 72 | public synchronized void setSupportMin(int min) { 73 | this.min = min; 74 | try { 75 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 76 | super.setMin(min); 77 | } 78 | } catch (Exception e) { 79 | e.printStackTrace(); 80 | } 81 | } 82 | 83 | @Override 84 | public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) { 85 | super.setOnSeekBarChangeListener(l); 86 | this.mSeekBarChangeListener = l; 87 | } 88 | 89 | @Override 90 | public boolean onTouchEvent(MotionEvent event) { 91 | if (!isEnabled()) { 92 | return false; 93 | } 94 | switch (event.getAction()) { 95 | case MotionEvent.ACTION_DOWN: 96 | if(null != mSeekBarChangeListener){ 97 | mSeekBarChangeListener.onStartTrackingTouch(this); 98 | } 99 | case MotionEvent.ACTION_MOVE: 100 | float y = event.getY(); 101 | int height = getHeight(); 102 | int progress = 0; 103 | final int range = max - min; 104 | float scale = y / height; 105 | progress -= scale * range + min; 106 | setProgress(progress); 107 | onSizeChanged(getWidth(), getHeight(), 0, 0); 108 | break; 109 | case MotionEvent.ACTION_UP: 110 | case MotionEvent.ACTION_CANCEL: 111 | if(null != mSeekBarChangeListener){ 112 | mSeekBarChangeListener.onStopTrackingTouch(this); 113 | } 114 | break; 115 | } 116 | return true; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_brightness_6_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_cameraswitch_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_lock_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_lock_open_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_settings_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_circle_solid.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_circle_stroke.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_circle_stroke_0_8dp.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_divider_0_5dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sun_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_corner_grey_5dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_exposure_lock_unlock.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 21 | 22 | 28 | 29 |