(view);
43 | }
44 |
45 | public View getView() {
46 | if (mWeakView == null)
47 | return null;
48 | return mWeakView.get();
49 | }
50 |
51 | public void setVideoSize(int videoWidth, int videoHeight) {
52 | mVideoWidth = videoWidth;
53 | mVideoHeight = videoHeight;
54 | }
55 |
56 | public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
57 | mVideoSarNum = videoSarNum;
58 | mVideoSarDen = videoSarDen;
59 | }
60 |
61 | public void setVideoRotation(int videoRotationDegree) {
62 | mVideoRotationDegree = videoRotationDegree;
63 | }
64 |
65 | /**
66 | * Must be called by View.onMeasure(int, int)
67 | *
68 | * @param widthMeasureSpec
69 | * @param heightMeasureSpec
70 | */
71 | public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
72 | //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
73 | // + MeasureSpec.toString(heightMeasureSpec) + ")");
74 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
75 | int tempSpec = widthMeasureSpec;
76 | widthMeasureSpec = heightMeasureSpec;
77 | heightMeasureSpec = tempSpec;
78 | }
79 |
80 | int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
81 | int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
82 | if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
83 | width = widthMeasureSpec;
84 | height = heightMeasureSpec;
85 | } else if (mVideoWidth > 0 && mVideoHeight > 0) {
86 | int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
87 | int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
88 | int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
89 | int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
90 |
91 | if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
92 | float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
93 | float displayAspectRatio;
94 | switch (mCurrentAspectRatio) {
95 | case IRenderView.AR_16_9_FIT_PARENT:
96 | displayAspectRatio = 16.0f / 9.0f;
97 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
98 | displayAspectRatio = 1.0f / displayAspectRatio;
99 | break;
100 | case IRenderView.AR_4_3_FIT_PARENT:
101 | displayAspectRatio = 4.0f / 3.0f;
102 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
103 | displayAspectRatio = 1.0f / displayAspectRatio;
104 | break;
105 | case IRenderView.AR_ASPECT_FIT_PARENT:
106 | case IRenderView.AR_ASPECT_FILL_PARENT:
107 | case IRenderView.AR_ASPECT_WRAP_CONTENT:
108 | default:
109 | displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
110 | if (mVideoSarNum > 0 && mVideoSarDen > 0)
111 | displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
112 | break;
113 | }
114 | boolean shouldBeWider = displayAspectRatio > specAspectRatio;
115 |
116 | switch (mCurrentAspectRatio) {
117 | case IRenderView.AR_ASPECT_FIT_PARENT:
118 | case IRenderView.AR_16_9_FIT_PARENT:
119 | case IRenderView.AR_4_3_FIT_PARENT:
120 | if (shouldBeWider) {
121 | // too wide, fix width
122 | width = widthSpecSize;
123 | height = (int) (width / displayAspectRatio);
124 | } else {
125 | // too high, fix height
126 | height = heightSpecSize;
127 | width = (int) (height * displayAspectRatio);
128 | }
129 | break;
130 | case IRenderView.AR_ASPECT_FILL_PARENT:
131 | if (shouldBeWider) {
132 | // not high enough, fix height
133 | height = heightSpecSize;
134 | width = (int) (height * displayAspectRatio);
135 | } else {
136 | // not wide enough, fix width
137 | width = widthSpecSize;
138 | height = (int) (width / displayAspectRatio);
139 | }
140 | break;
141 | case IRenderView.AR_ASPECT_WRAP_CONTENT:
142 | default:
143 | if (shouldBeWider) {
144 | // too wide, fix width
145 | width = Math.min(mVideoWidth, widthSpecSize);
146 | height = (int) (width / displayAspectRatio);
147 | } else {
148 | // too high, fix height
149 | height = Math.min(mVideoHeight, heightSpecSize);
150 | width = (int) (height * displayAspectRatio);
151 | }
152 | break;
153 | }
154 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
155 | // the size is fixed
156 | width = widthSpecSize;
157 | height = heightSpecSize;
158 |
159 | // for compatibility, we adjust size based on aspect ratio
160 | if (mVideoWidth * height < width * mVideoHeight) {
161 | //Log.i("@@@", "image too wide, correcting");
162 | width = height * mVideoWidth / mVideoHeight;
163 | } else if (mVideoWidth * height > width * mVideoHeight) {
164 | //Log.i("@@@", "image too tall, correcting");
165 | height = width * mVideoHeight / mVideoWidth;
166 | }
167 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
168 | // only the width is fixed, adjust the height to match aspect ratio if possible
169 | width = widthSpecSize;
170 | height = width * mVideoHeight / mVideoWidth;
171 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
172 | // couldn't match aspect ratio within the constraints
173 | height = heightSpecSize;
174 | }
175 | } else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
176 | // only the height is fixed, adjust the width to match aspect ratio if possible
177 | height = heightSpecSize;
178 | width = height * mVideoWidth / mVideoHeight;
179 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
180 | // couldn't match aspect ratio within the constraints
181 | width = widthSpecSize;
182 | }
183 | } else {
184 | // neither the width nor the height are fixed, try to use actual video size
185 | width = mVideoWidth;
186 | height = mVideoHeight;
187 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
188 | // too tall, decrease both width and height
189 | height = heightSpecSize;
190 | width = height * mVideoWidth / mVideoHeight;
191 | }
192 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
193 | // too wide, decrease both width and height
194 | width = widthSpecSize;
195 | height = width * mVideoHeight / mVideoWidth;
196 | }
197 | }
198 | } else {
199 | // no size yet, just adopt the given spec sizes
200 | }
201 |
202 | mMeasuredWidth = width;
203 | mMeasuredHeight = height;
204 | }
205 |
206 | public int getMeasuredWidth() {
207 | return mMeasuredWidth;
208 | }
209 |
210 | public int getMeasuredHeight() {
211 | return mMeasuredHeight;
212 | }
213 |
214 | public void setAspectRatio(int aspectRatio) {
215 | mCurrentAspectRatio = aspectRatio;
216 | }
217 |
218 | }
219 |
--------------------------------------------------------------------------------
/player/src/main/java/com/leo/player/media/util/NetworkUtils.java:
--------------------------------------------------------------------------------
1 | package com.leo.player.media.util;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.ConnectivityManager;
7 | import android.net.NetworkInfo;
8 | import android.net.wifi.WifiManager;
9 | import android.telephony.TelephonyManager;
10 |
11 | import java.lang.reflect.Method;
12 | import java.net.InetAddress;
13 | import java.net.NetworkInterface;
14 | import java.net.SocketException;
15 | import java.net.UnknownHostException;
16 | import java.util.Enumeration;
17 |
18 | /**
19 | *
20 | * author: Blankj
21 | * blog : http://blankj.com
22 | * time : 2016/08/02
23 | * desc : 网络相关工具类
24 | *
25 | */
26 | public final class NetworkUtils {
27 |
28 | private NetworkUtils() {
29 | throw new UnsupportedOperationException("u can't instantiate me...");
30 | }
31 |
32 | public enum NetworkType {
33 | NETWORK_WIFI,
34 | NETWORK_4G,
35 | NETWORK_3G,
36 | NETWORK_2G,
37 | NETWORK_UNKNOWN,
38 | NETWORK_NO
39 | }
40 |
41 | /**
42 | * 打开网络设置界面
43 | */
44 | public static void openWirelessSettings(Context context) {
45 | context.startActivity(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
46 | }
47 |
48 | /**
49 | * 获取活动网络信息
50 | * 需添加权限 {@code }
51 | *
52 | * @return NetworkInfo
53 | */
54 | private static NetworkInfo getActiveNetworkInfo(Context context) {
55 | return ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
56 | }
57 |
58 | /**
59 | * 判断网络是否连接
60 | * 需添加权限 {@code }
61 | *
62 | * @return {@code true}: 是
{@code false}: 否
63 | */
64 | public static boolean isConnected(Context context) {
65 | NetworkInfo info = getActiveNetworkInfo(context);
66 | return info != null && info.isConnected();
67 | }
68 |
69 |
70 | /**
71 | * 判断移动数据是否打开
72 | *
73 | * @return {@code true}: 是
{@code false}: 否
74 | */
75 | public static boolean getDataEnabled(Context context) {
76 | try {
77 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
78 | Method getMobileDataEnabledMethod = tm.getClass().getDeclaredMethod("getDataEnabled");
79 | if (null != getMobileDataEnabledMethod) {
80 | return (boolean) getMobileDataEnabledMethod.invoke(tm);
81 | }
82 | } catch (Exception e) {
83 | e.printStackTrace();
84 | }
85 | return false;
86 | }
87 |
88 | /**
89 | * 打开或关闭移动数据
90 | * 需系统应用 需添加权限{@code }
91 | *
92 | * @param enabled {@code true}: 打开
{@code false}: 关闭
93 | */
94 | public static void setDataEnabled(Context context, final boolean enabled) {
95 | try {
96 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
97 | Method setMobileDataEnabledMethod = tm.getClass().getDeclaredMethod("setDataEnabled", boolean.class);
98 | if (null != setMobileDataEnabledMethod) {
99 | setMobileDataEnabledMethod.invoke(tm, enabled);
100 | }
101 | } catch (Exception e) {
102 | e.printStackTrace();
103 | }
104 | }
105 |
106 | /**
107 | * 判断网络是否是4G
108 | * 需添加权限 {@code }
109 | *
110 | * @return {@code true}: 是
{@code false}: 否
111 | */
112 | public static boolean is4G(Context context) {
113 | NetworkInfo info = getActiveNetworkInfo(context);
114 | return info != null && info.isAvailable() && info.getSubtype() == TelephonyManager.NETWORK_TYPE_LTE;
115 | }
116 |
117 | /**
118 | * 判断wifi是否打开
119 | * 需添加权限 {@code }
120 | *
121 | * @return {@code true}: 是
{@code false}: 否
122 | */
123 | public static boolean getWifiEnabled(Context context) {
124 | @SuppressLint("WifiManagerLeak")
125 | WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
126 | return wifiManager.isWifiEnabled();
127 | }
128 |
129 | /**
130 | * 打开或关闭wifi
131 | * 需添加权限 {@code }
132 | *
133 | * @param enabled {@code true}: 打开
{@code false}: 关闭
134 | */
135 | public static void setWifiEnabled(Context context, final boolean enabled) {
136 | @SuppressLint("WifiManagerLeak")
137 | WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
138 | if (enabled) {
139 | if (!wifiManager.isWifiEnabled()) {
140 | wifiManager.setWifiEnabled(true);
141 | }
142 | } else {
143 | if (wifiManager.isWifiEnabled()) {
144 | wifiManager.setWifiEnabled(false);
145 | }
146 | }
147 | }
148 |
149 | /**
150 | * 判断wifi是否连接状态
151 | * 需添加权限 {@code }
152 | *
153 | * @return {@code true}: 连接
{@code false}: 未连接
154 | */
155 | public static boolean isWifiConnected(Context context) {
156 | ConnectivityManager cm = (ConnectivityManager) context
157 | .getSystemService(Context.CONNECTIVITY_SERVICE);
158 | return cm != null && cm.getActiveNetworkInfo() != null
159 | && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
160 | }
161 |
162 |
163 | private static final int NETWORK_TYPE_GSM = 16;
164 | private static final int NETWORK_TYPE_TD_SCDMA = 17;
165 | private static final int NETWORK_TYPE_IWLAN = 18;
166 |
167 | /**
168 | * 获取当前网络类型
169 | * 需添加权限 {@code }
170 | *
171 | * @return 网络类型
172 | *
173 | * - {@link NetworkType#NETWORK_WIFI }
174 | * - {@link NetworkType#NETWORK_4G }
175 | * - {@link NetworkType#NETWORK_3G }
176 | * - {@link NetworkType#NETWORK_2G }
177 | * - {@link NetworkType#NETWORK_UNKNOWN}
178 | * - {@link NetworkType#NETWORK_NO }
179 | *
180 | */
181 | public static NetworkType getNetworkType(Context context) {
182 | NetworkType netType = NetworkType.NETWORK_NO;
183 | NetworkInfo info = getActiveNetworkInfo(context);
184 | if (info != null && info.isAvailable()) {
185 |
186 | if (info.getType() == ConnectivityManager.TYPE_WIFI) {
187 | netType = NetworkType.NETWORK_WIFI;
188 | } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
189 | switch (info.getSubtype()) {
190 |
191 | case NETWORK_TYPE_GSM:
192 | case TelephonyManager.NETWORK_TYPE_GPRS:
193 | case TelephonyManager.NETWORK_TYPE_CDMA:
194 | case TelephonyManager.NETWORK_TYPE_EDGE:
195 | case TelephonyManager.NETWORK_TYPE_1xRTT:
196 | case TelephonyManager.NETWORK_TYPE_IDEN:
197 | netType = NetworkType.NETWORK_2G;
198 | break;
199 |
200 | case NETWORK_TYPE_TD_SCDMA:
201 | case TelephonyManager.NETWORK_TYPE_EVDO_A:
202 | case TelephonyManager.NETWORK_TYPE_UMTS:
203 | case TelephonyManager.NETWORK_TYPE_EVDO_0:
204 | case TelephonyManager.NETWORK_TYPE_HSDPA:
205 | case TelephonyManager.NETWORK_TYPE_HSUPA:
206 | case TelephonyManager.NETWORK_TYPE_HSPA:
207 | case TelephonyManager.NETWORK_TYPE_EVDO_B:
208 | case TelephonyManager.NETWORK_TYPE_EHRPD:
209 | case TelephonyManager.NETWORK_TYPE_HSPAP:
210 | netType = NetworkType.NETWORK_3G;
211 | break;
212 |
213 | case NETWORK_TYPE_IWLAN:
214 | case TelephonyManager.NETWORK_TYPE_LTE:
215 | netType = NetworkType.NETWORK_4G;
216 | break;
217 | default:
218 |
219 | String subtypeName = info.getSubtypeName();
220 | if (subtypeName.equalsIgnoreCase("TD-SCDMA")
221 | || subtypeName.equalsIgnoreCase("WCDMA")
222 | || subtypeName.equalsIgnoreCase("CDMA2000")) {
223 | netType = NetworkType.NETWORK_3G;
224 | } else {
225 | netType = NetworkType.NETWORK_UNKNOWN;
226 | }
227 | break;
228 | }
229 | } else {
230 | netType = NetworkType.NETWORK_UNKNOWN;
231 | }
232 | }
233 | return netType;
234 | }
235 |
236 | /**
237 | * 获取IP地址
238 | * 需添加权限 {@code }
239 | *
240 | * @param useIPv4 是否用IPv4
241 | * @return IP地址
242 | */
243 | public static String getIPAddress(final boolean useIPv4) {
244 | try {
245 | for (Enumeration nis = NetworkInterface.getNetworkInterfaces(); nis.hasMoreElements(); ) {
246 | NetworkInterface ni = nis.nextElement();
247 | // 防止小米手机返回10.0.2.15
248 | if (!ni.isUp()) continue;
249 | for (Enumeration addresses = ni.getInetAddresses(); addresses.hasMoreElements(); ) {
250 | InetAddress inetAddress = addresses.nextElement();
251 | if (!inetAddress.isLoopbackAddress()) {
252 | String hostAddress = inetAddress.getHostAddress();
253 | boolean isIPv4 = hostAddress.indexOf(':') < 0;
254 | if (useIPv4) {
255 | if (isIPv4) return hostAddress;
256 | } else {
257 | if (!isIPv4) {
258 | int index = hostAddress.indexOf('%');
259 | return index < 0 ? hostAddress.toUpperCase() : hostAddress.substring(0, index).toUpperCase();
260 | }
261 | }
262 | }
263 | }
264 | }
265 | } catch (SocketException e) {
266 | e.printStackTrace();
267 | }
268 | return null;
269 | }
270 |
271 | /**
272 | * 获取域名ip地址
273 | * 需添加权限 {@code }
274 | *
275 | * @param domain 域名
276 | * @return ip地址
277 | */
278 | public static String getDomainAddress(final String domain) {
279 | InetAddress inetAddress;
280 | try {
281 | inetAddress = InetAddress.getByName(domain);
282 | return inetAddress.getHostAddress();
283 | } catch (UnknownHostException e) {
284 | e.printStackTrace();
285 | return null;
286 | }
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/player/src/main/java/com/leo/player/media/util/OrientationUtils.java:
--------------------------------------------------------------------------------
1 | package com.leo.player.media.util;
2 |
3 | import android.app.Activity;
4 | import android.provider.Settings;
5 | import android.view.OrientationEventListener;
6 |
7 | import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
8 | import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
9 | import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
10 |
11 | /**
12 | * Created on 2017/8/11 下午4:24.
13 | * leo linxiaotao1993@vip.qq.com
14 | */
15 |
16 | public final class OrientationUtils {
17 |
18 |
19 | ///////////////////////////////////////////////////////////////////////////
20 | // 常量区
21 | ///////////////////////////////////////////////////////////////////////////
22 |
23 | private final Activity mContent;
24 | private OrientationEventListener mOrientationEventListener;
25 | /** 当前屏幕模式 */
26 | private int mScreenOrientation = SCREEN_ORIENTATION_PORTRAIT;
27 | /** 是否跟随系统 */
28 | private boolean mRotateWithSystem = true;
29 | /** 是否启用 */
30 | private boolean mEnable = true;
31 | /** 是否为手动切换 */
32 | private boolean mManual;
33 | private Callback mCallback;
34 |
35 | public OrientationUtils(Activity activity) {
36 | mContent = activity;
37 | init();
38 | }
39 |
40 | public OrientationUtils setCallback(Callback callback) {
41 | mCallback = callback;
42 | return this;
43 | }
44 |
45 | public OrientationUtils setEnable(boolean enable) {
46 | mEnable = enable;
47 | if (mEnable) {
48 | mOrientationEventListener.enable();
49 | } else {
50 | mOrientationEventListener.disable();
51 | }
52 | return this;
53 | }
54 |
55 | /** 切换屏幕模式 */
56 | public void toggleScreenOrientation() {
57 | mManual = true;
58 | if (mScreenOrientation == SCREEN_ORIENTATION_PORTRAIT) {
59 | //切换为横屏
60 | setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE);
61 | } else {
62 | //切换为竖屏
63 | setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT);
64 | }
65 | }
66 |
67 | public void release() {
68 | if (mOrientationEventListener != null) {
69 | mOrientationEventListener.disable();
70 | mOrientationEventListener = null;
71 | }
72 | }
73 |
74 | private void init() {
75 |
76 | mScreenOrientation = mContent.getResources().getConfiguration().orientation;
77 |
78 | mOrientationEventListener = new OrientationEventListener(mContent) {
79 | @Override
80 | public void onOrientationChanged(int orientation) {
81 |
82 | boolean autoRotateOn = (Settings.System.getInt(mContent.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
83 | if (!autoRotateOn && mRotateWithSystem) {
84 | return;
85 | }
86 |
87 | if (((orientation >= 0) && (orientation <= 30)) || (orientation >= 330)) {
88 | //设置竖屏
89 | if (mManual && mScreenOrientation == SCREEN_ORIENTATION_PORTRAIT) {
90 | mManual = false;
91 | return;
92 | } else if (mManual) {
93 | return;
94 | }
95 | if (mScreenOrientation != SCREEN_ORIENTATION_PORTRAIT) {
96 | setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT);
97 | }
98 | } else if (((orientation >= 230) && (orientation <= 310))) {
99 | //设置横屏
100 | if (mManual && mScreenOrientation == SCREEN_ORIENTATION_LANDSCAPE) {
101 | mManual = false;
102 | return;
103 | } else if (mManual) {
104 | return;
105 | }
106 | if (mScreenOrientation != SCREEN_ORIENTATION_LANDSCAPE) {
107 | setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE);
108 | }
109 | } else if (((orientation > 30) && (orientation < 95))) {
110 | if (mManual && mScreenOrientation == SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
111 | mManual = false;
112 | return;
113 | } else if (mManual) {
114 | return;
115 | }
116 | if (mScreenOrientation != SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
117 | setScreenOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
118 | }
119 | }
120 |
121 | }
122 | };
123 | if (mEnable) {
124 | mOrientationEventListener.enable();
125 | } else {
126 | mOrientationEventListener.disable();
127 | }
128 | }
129 |
130 | private void setScreenOrientation(int screenOrientation) {
131 | mScreenOrientation = screenOrientation;
132 | switch (screenOrientation) {
133 | case SCREEN_ORIENTATION_LANDSCAPE:
134 | mContent.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
135 | break;
136 | case SCREEN_ORIENTATION_PORTRAIT:
137 | mContent.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
138 | break;
139 | case SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
140 | mContent.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
141 | break;
142 | }
143 | if (mCallback != null) {
144 | mCallback.screenOrientationChangle(mScreenOrientation);
145 | }
146 | }
147 |
148 | public interface Callback {
149 |
150 | void screenOrientationChangle(int screenOrientation);
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/player/src/main/java/com/leo/player/media/util/SimpleTransitionListener.java:
--------------------------------------------------------------------------------
1 | package com.leo.player.media.util;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.transition.Transition;
5 |
6 | /**
7 | * Created on 2017/8/28 下午3:32.
8 | * leo linxiaotao1993@vip.qq.com
9 | */
10 |
11 | public abstract class SimpleTransitionListener implements Transition.TransitionListener {
12 |
13 | @Override
14 | public void onTransitionStart(@NonNull Transition transition) {
15 |
16 | }
17 |
18 | @Override
19 | public void onTransitionEnd(@NonNull Transition transition) {
20 |
21 | }
22 |
23 | @Override
24 | public void onTransitionCancel(@NonNull Transition transition) {
25 |
26 | }
27 |
28 | @Override
29 | public void onTransitionPause(@NonNull Transition transition) {
30 |
31 | }
32 |
33 | @Override
34 | public void onTransitionResume(@NonNull Transition transition) {
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/player/src/main/java/com/leo/player/media/util/StorageUtils.java:
--------------------------------------------------------------------------------
1 | package com.leo.player.media.util;
2 |
3 | import android.Manifest;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.os.Build;
7 | import android.os.Environment;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.content.ContextCompat;
10 |
11 | import com.danikula.videocache.file.Md5FileNameGenerator;
12 |
13 | import java.io.File;
14 |
15 |
16 | public class StorageUtils {
17 |
18 | private static final String INDIVIDUAL_DIR_NAME = "video-cache";
19 |
20 | private static final Md5FileNameGenerator FILE_NAME_GENERATOR = new Md5FileNameGenerator();
21 |
22 | /**
23 | * 获取缓存目录
24 | * 优先使用外部存储
25 | *
26 | * @param context 上下文
27 | * @return 缓存目录
28 | */
29 | @Nullable
30 | public static File getCacheDirectory(Context context) {
31 |
32 | //检查是否有外部存储权限
33 | int permission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
34 | boolean hasPermission = (permission == PackageManager.PERMISSION_GRANTED) ||
35 | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT);
36 |
37 | if (isExternalStorageWritable() && hasPermission) {
38 | //删除可能存在内部存储的缓存
39 | delFile(new File(context.getCacheDir(),INDIVIDUAL_DIR_NAME));
40 | File movies = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES);
41 | return new File(movies, INDIVIDUAL_DIR_NAME);
42 | } else {
43 | return new File(context.getCacheDir(), INDIVIDUAL_DIR_NAME);
44 | }
45 | }
46 |
47 |
48 | /**
49 | * 获取外部存储是否可用
50 | *
51 | * @return 是否可用
52 | */
53 | private static boolean isExternalStorageWritable() {
54 | String state = Environment.getExternalStorageState();
55 | return Environment.MEDIA_MOUNTED.equals(state);
56 | }
57 |
58 | /**
59 | * 删除指定的 url 缓存中文件
60 | *
61 | * @param context
62 | * @param cacheDir
63 | * @param url
64 | */
65 | public static void delSpecifyUrlCache(Context context, File cacheDir, String url) {
66 | String cacheFileName = FILE_NAME_GENERATOR.generate(url) + ".download";
67 | File specifyCacheDir = new File(cacheDir, cacheFileName);
68 | delFile(specifyCacheDir);
69 | }
70 |
71 | public static boolean delFile(File file) {
72 | if (file.exists()) {
73 | if (file.isDirectory()) {
74 | for (File childFile : file.listFiles()) {
75 | delFile(childFile);
76 | }
77 | return file.delete();
78 | } else {
79 | return file.delete();
80 | }
81 | } else {
82 | return false;
83 | }
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/player/src/main/java/com/leo/player/media/videoview/IVideoView.java:
--------------------------------------------------------------------------------
1 | package com.leo.player.media.videoview;
2 |
3 | import android.graphics.Bitmap;
4 | import android.net.Uri;
5 |
6 | import com.leo.player.media.Settings;
7 |
8 | import tv.danmaku.ijk.media.player.IMediaPlayer;
9 | import tv.danmaku.ijk.media.player.IjkTimedText;
10 |
11 | /**
12 | * Created on 2017/8/7 上午10:07.
13 | * leo linxiaotao1993@vip.qq.com
14 | */
15 |
16 | public interface IVideoView {
17 |
18 | Uri getCurrentUri();
19 |
20 | Bitmap getPauseBitmap();
21 |
22 | void setPauseBitmap(Bitmap pauseBitmap);
23 |
24 | void initPauseCover();
25 |
26 | void openVideo();
27 |
28 | void setSettings(Settings settings);
29 |
30 | Settings getSettings();
31 |
32 | void onCreatePlayer(IMediaPlayer mp);
33 |
34 | void onPausePlayer(IMediaPlayer mp);
35 |
36 | void onStartPlayer(IMediaPlayer mp);
37 |
38 | void onReleasePlayer();
39 |
40 | void onPrepared(IMediaPlayer mp);
41 |
42 | void onCompletion(IMediaPlayer mp);
43 |
44 | void onBufferingUpdate(IMediaPlayer mp, int percent);
45 |
46 | void onSeekComplete(IMediaPlayer mp);
47 |
48 | boolean onError(IMediaPlayer mp, int what, int extra);
49 |
50 | boolean onInfo(IMediaPlayer mp, int what, int extra);
51 |
52 | void onVideoSizeChanged(IMediaPlayer mp, int width, int height,
53 | int sar_num, int sar_den);
54 |
55 | void onTimedText(IMediaPlayer mp, IjkTimedText text);
56 | }
57 |
--------------------------------------------------------------------------------
/player/src/main/java/com/leo/player/media/videoview/IjkVideoView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.leo.player.media.videoview;
19 |
20 | import android.annotation.TargetApi;
21 | import android.content.Context;
22 | import android.graphics.Bitmap;
23 | import android.graphics.Canvas;
24 | import android.graphics.Color;
25 | import android.graphics.Rect;
26 | import android.graphics.RectF;
27 | import android.media.AudioManager;
28 | import android.net.Uri;
29 | import android.os.Build;
30 | import android.support.annotation.NonNull;
31 | import android.util.AttributeSet;
32 | import android.util.Log;
33 | import android.view.Gravity;
34 | import android.view.Surface;
35 | import android.view.View;
36 | import android.widget.FrameLayout;
37 |
38 | import com.leo.player.media.IjkVideoManager;
39 | import com.leo.player.media.Settings;
40 | import com.leo.player.media.controller.IMediaController;
41 | import com.leo.player.media.render.IRenderView;
42 | import com.leo.player.media.render.SurfaceRenderView;
43 | import com.leo.player.media.render.TextureRenderView;
44 | import com.leo.player.media.util.LoggerUtils;
45 |
46 | import java.util.ArrayList;
47 | import java.util.List;
48 | import java.util.Locale;
49 |
50 | import tv.danmaku.ijk.media.player.IMediaPlayer;
51 | import tv.danmaku.ijk.media.player.IjkTimedText;
52 |
53 | public class IjkVideoView extends FrameLayout implements IVideoView {
54 |
55 | ///////////////////////////////////////////////////////////////////////////
56 | // 可配置
57 | ///////////////////////////////////////////////////////////////////////////
58 | /** 播放 Uri */
59 | private Uri mUri;
60 | /** 播放器旋转角度 */
61 | private int mVideoRotationDegree;
62 | /** player 配置 */
63 | private Settings mSettings;
64 | /** 兼容列表播放,记录当前播放序号 */
65 | private int mPlayPosition = -1;
66 |
67 | ///////////////////////////////////////////////////////////////////////////
68 | // 常量
69 | ///////////////////////////////////////////////////////////////////////////
70 | private static final String TAG = "IjkVideoView";
71 |
72 | ///////////////////////////////////////////////////////////////////////////
73 | // 内部使用
74 | ///////////////////////////////////////////////////////////////////////////
75 | private Context mAppContext;
76 | private boolean mEnabled;
77 | private boolean mPrepared;
78 | private IMediaController mMediaController;
79 | private int mVideoSarNum;
80 | private int mVideoSarDen;
81 | private int mVideoWidth;
82 | private int mVideoHeight;
83 | private int mSurfaceWidth;
84 | private int mSurfaceHeight;
85 |
86 | public IjkVideoView(Context context) {
87 | super(context);
88 | initVideoView(context);
89 | }
90 |
91 | public IjkVideoView(Context context, AttributeSet attrs) {
92 | super(context, attrs);
93 | initVideoView(context);
94 | }
95 |
96 | public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
97 | super(context, attrs, defStyleAttr);
98 | initVideoView(context);
99 | }
100 |
101 |
102 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
103 | public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
104 | super(context, attrs, defStyleAttr, defStyleRes);
105 | initVideoView(context);
106 | }
107 |
108 | private void initVideoView(Context context) {
109 | setBackgroundColor(Color.BLACK);
110 | mAppContext = context.getApplicationContext();
111 | mVideoWidth = 0;
112 | mVideoHeight = 0;
113 |
114 | }
115 |
116 | public int getPlayPosition() {
117 | return mPlayPosition;
118 | }
119 |
120 | public IjkVideoView setPlayPosition(int playPosition) {
121 | mPlayPosition = playPosition;
122 | return this;
123 | }
124 |
125 | @Override
126 | public void setSettings(Settings settings) {
127 | mSettings = settings;
128 | }
129 |
130 | @Override
131 | public Settings getSettings() {
132 | return mSettings;
133 | }
134 |
135 | ///////////////////////////////////////////////////////////////////////////
136 | // media control
137 | ///////////////////////////////////////////////////////////////////////////
138 |
139 | public void setMediaController(IMediaController mediaController) {
140 | if (mediaController == null) {
141 | throw new IllegalArgumentException("media controller not null");
142 | }
143 | mMediaController = mediaController;
144 | View contentView = mediaController.makeControllerView();
145 | LayoutParams layoutParams = generateDefaultLayoutParams();
146 | addView(contentView, layoutParams);
147 | // TODO: 2017/8/17 这里将 setVideoView 延迟到 setUri 之后,是否合理?
148 | if (mUri != null) {
149 | mediaController.setVideoView(this);
150 | }
151 | mediaController.setEnabled(mPrepared);
152 | }
153 |
154 | ///////////////////////////////////////////////////////////////////////////
155 | // video uri
156 | ///////////////////////////////////////////////////////////////////////////
157 |
158 | /**
159 | * Sets video path.
160 | *
161 | * @param path the path of the video.
162 | */
163 | public void setVideoPath(String path) {
164 | setVideoURI(Uri.parse(path));
165 | }
166 |
167 | /**
168 | * Sets video URI.
169 | *
170 | * @param uri the URI of the video.
171 | */
172 | public void setVideoURI(Uri uri) {
173 | setVideoURI(uri, null);
174 |
175 | }
176 |
177 | private void setVideoURI(Uri uri, Settings settings) {
178 | mUri = uri;
179 | if (settings != null) {
180 | mSettings = settings;
181 | }
182 | mVideoRotationDegree = 0;
183 | if (mMediaController != null) {
184 | mMediaController.setVideoView(this);
185 | }
186 | }
187 |
188 | @Override
189 | public void openVideo() {
190 |
191 | AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
192 | am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
193 |
194 | if (mSettings == null) {
195 | mSettings = new Settings(mAppContext);
196 | }
197 |
198 | //对列表的兼容
199 | IjkVideoManager.getInstance().setVideoView(this);
200 | if (mMediaController != null) {
201 | IjkVideoManager.getInstance().setStateChangeListener(mMediaController);
202 | }
203 |
204 | IjkVideoManager.getInstance().setVideoUri(mUri, mSettings);
205 | }
206 |
207 | ///////////////////////////////////////////////////////////////////////////
208 | // video mananger
209 | ///////////////////////////////////////////////////////////////////////////
210 |
211 |
212 | @Override
213 | public void onCreatePlayer(IMediaPlayer mp) {
214 | mEnabled = true;
215 | IjkVideoManager.getInstance().setPlayPosition(mPlayPosition);
216 | initRenders(mp);
217 |
218 | }
219 |
220 | @Override
221 | public void onPausePlayer(IMediaPlayer mp) {
222 | updatePauseCover();
223 | }
224 |
225 | @Override
226 | public void onStartPlayer(IMediaPlayer mp) {
227 | releasePauseCover();
228 | }
229 |
230 | @Override
231 | public void onReleasePlayer() {
232 | //ijk view 需要释放的资源
233 | mPrepared = false;
234 | mEnabled = false;
235 | releasePauseCover();
236 | AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
237 | am.abandonAudioFocus(null);
238 | if (IjkVideoManager.getInstance().getPlayPosition() == mPlayPosition) {
239 | IjkVideoManager.getInstance().setPlayPosition(-1);
240 | }
241 | if (IjkVideoManager.getInstance().getStateChangeListener() == mMediaController) {
242 | IjkVideoManager.getInstance().setStateChangeListener(null);
243 | }
244 | }
245 |
246 | @Override
247 | public void onPrepared(IMediaPlayer mp) {
248 |
249 | mPrepared = true;
250 |
251 | mVideoWidth = mp.getVideoWidth();
252 | mVideoHeight = mp.getVideoHeight();
253 |
254 | IjkVideoManager.getInstance().handleSeekWhenPrepared();
255 |
256 | if (mVideoWidth != 0 && mVideoHeight != 0) {
257 | if (mRenderView != null) {
258 | mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
259 | mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
260 | if (!mRenderView.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
261 | if (mMediaController != null) {
262 | mMediaController.setEnabled(true);
263 | }
264 | }
265 | }
266 | } else {
267 | if (mMediaController != null) {
268 | mMediaController.setEnabled(true);
269 | }
270 | }
271 | }
272 |
273 | @Override
274 | public void onCompletion(IMediaPlayer mp) {
275 |
276 | }
277 |
278 | @Override
279 | public void onBufferingUpdate(IMediaPlayer mp, int percent) {
280 | }
281 |
282 | @Override
283 | public void onSeekComplete(IMediaPlayer mp) {
284 | }
285 |
286 | @Override
287 | public boolean onError(IMediaPlayer mp, int framework_err, int impl_err) {
288 | error(framework_err + "," + impl_err);
289 | return true;
290 | }
291 |
292 | @Override
293 | public boolean onInfo(IMediaPlayer mp, int what, int extra) {
294 | switch (what) {
295 | case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
296 | debug("MEDIA_INFO_VIDEO_TRACK_LAGGING:");
297 | break;
298 | case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
299 | debug("MEDIA_INFO_VIDEO_RENDERING_START:");
300 | break;
301 | case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
302 | debug("MEDIA_INFO_BUFFERING_START:");
303 | break;
304 | case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
305 | debug("MEDIA_INFO_BUFFERING_END:");
306 | break;
307 | case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:
308 | debug("MEDIA_INFO_NETWORK_BANDWIDTH: " + extra);
309 | break;
310 | case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
311 | debug("MEDIA_INFO_BAD_INTERLEAVING:");
312 | break;
313 | case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
314 | debug("MEDIA_INFO_NOT_SEEKABLE:");
315 | break;
316 | case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE:
317 | debug("MEDIA_INFO_METADATA_UPDATE:");
318 | break;
319 | case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:
320 | debug("MEDIA_INFO_UNSUPPORTED_SUBTITLE:");
321 | break;
322 | case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:
323 | debug("MEDIA_INFO_SUBTITLE_TIMED_OUT:");
324 | break;
325 | case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:
326 | mVideoRotationDegree = extra;
327 | debug("MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + extra);
328 | if (mRenderView != null)
329 | mRenderView.setVideoRotation(extra);
330 | break;
331 | case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
332 | debug("MEDIA_INFO_AUDIO_RENDERING_START:");
333 | break;
334 | }
335 | return true;
336 | }
337 |
338 | @Override
339 | public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sar_num, int sar_den) {
340 | mVideoWidth = mp.getVideoWidth();
341 | mVideoHeight = mp.getVideoHeight();
342 | mVideoSarNum = mp.getVideoSarNum();
343 | mVideoSarDen = mp.getVideoSarDen();
344 | if (mVideoWidth != 0 && mVideoHeight != 0) {
345 | if (mRenderView != null) {
346 | mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
347 | mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
348 | }
349 | requestLayout();
350 | }
351 | }
352 |
353 | @Override
354 | public void onTimedText(IMediaPlayer mp, IjkTimedText text) {
355 |
356 | }
357 |
358 | ///////////////////////////////////////////////////////////////////////////
359 | // Aspect Ratio
360 | ///////////////////////////////////////////////////////////////////////////
361 |
362 | private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;
363 |
364 | public void setAspectRatio(int aspectRatio) {
365 | mCurrentAspectRatio = aspectRatio;
366 | if (mRenderView != null) {
367 | mRenderView.setAspectRatio(mCurrentAspectRatio);
368 | }
369 | }
370 |
371 | ///////////////////////////////////////////////////////////////////////////
372 | // render
373 | ///////////////////////////////////////////////////////////////////////////
374 | // TODO: 2017/8/17 当前都是基于 TextureRenderView
375 |
376 | public static final int RENDER_NONE = 0;
377 | public static final int RENDER_SURFACE_VIEW = 1;
378 | public static final int RENDER_TEXTURE_VIEW = 2;
379 |
380 | private IRenderView mRenderView;
381 | private IRenderView.ISurfaceHolder mSurfaceHolder;
382 | private List mAllRenders = new ArrayList<>();
383 | private int mCurrentRenderIndex = 0;
384 | private int mCurrentRender = RENDER_NONE;
385 |
386 | private void initRenders(IMediaPlayer mediaPlayer) {
387 | mAllRenders.clear();
388 |
389 | if (mSettings.getEnableSurfaceView()) {
390 | mAllRenders.add(RENDER_SURFACE_VIEW);
391 | }
392 | if (mSettings.getEnableTextureView() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
393 | mAllRenders.add(RENDER_TEXTURE_VIEW);
394 | }
395 | if (mSettings.getEnableNoView()) {
396 | mAllRenders.add(RENDER_NONE);
397 | }
398 | if (mAllRenders.isEmpty()) {
399 | mAllRenders.add(RENDER_TEXTURE_VIEW);
400 | }
401 | mCurrentRender = mAllRenders.get(mCurrentRenderIndex);
402 | setRender(mCurrentRender, mediaPlayer);
403 | }
404 |
405 | private void setRender(int render, IMediaPlayer mediaPlayer) {
406 | switch (render) {
407 | case RENDER_NONE:
408 | setRenderView(null);
409 | break;
410 | case RENDER_TEXTURE_VIEW: {
411 | TextureRenderView renderView = new TextureRenderView(getContext());
412 | if (mediaPlayer != null) {
413 | renderView.getSurfaceHolder().bindToMediaPlayer(mediaPlayer);
414 | renderView.setVideoSize(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
415 | renderView.setVideoSampleAspectRatio(mediaPlayer.getVideoSarNum(), mediaPlayer.getVideoSarDen());
416 | renderView.setAspectRatio(mCurrentAspectRatio);
417 | }
418 | setRenderView(renderView);
419 | break;
420 | }
421 | case RENDER_SURFACE_VIEW: {
422 | SurfaceRenderView renderView = new SurfaceRenderView(getContext());
423 | setRenderView(renderView);
424 | break;
425 | }
426 | default:
427 | Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render));
428 | break;
429 | }
430 | }
431 |
432 | private void setRenderView(IRenderView renderView) {
433 | if (mRenderView != null) {
434 | View renderUIView = mRenderView.getView();
435 | mRenderView.removeRenderCallback(mSHCallback);
436 | mRenderView = null;
437 | removeView(renderUIView);
438 | }
439 |
440 | if (renderView == null) {
441 | return;
442 | }
443 |
444 | mRenderView = renderView;
445 | renderView.setAspectRatio(mCurrentAspectRatio);
446 | if (mVideoWidth > 0 && mVideoHeight > 0) {
447 | renderView.setVideoSize(mVideoWidth, mVideoHeight);
448 | }
449 | if (mVideoSarNum > 0 && mVideoSarDen > 0) {
450 | renderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
451 | }
452 |
453 | View renderUIView = mRenderView.getView();
454 | LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER);
455 | renderUIView.setLayoutParams(lp);
456 | addView(renderUIView, 0);
457 |
458 | mRenderView.addRenderCallback(mSHCallback);
459 | mRenderView.setVideoRotation(mVideoRotationDegree);
460 | }
461 |
462 | private IRenderView.IRenderCallback mSHCallback = new IRenderView.IRenderCallback() {
463 | @Override
464 | public void onSurfaceChanged(@NonNull IRenderView.ISurfaceHolder holder, int format, int w, int h) {
465 | if (holder.getRenderView() != mRenderView) {
466 | debug("onSurfaceChanged: unmatched render callback\n");
467 | return;
468 | }
469 | mSurfaceHolder = holder;
470 | mSurfaceWidth = w;
471 | mSurfaceHeight = h;
472 | boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h);
473 | if (hasValidSize) {
474 | debug("onSurfaceChanged");
475 | }
476 | }
477 |
478 | @Override
479 | public void onSurfaceCreated(@NonNull IRenderView.ISurfaceHolder holder, int width, int height) {
480 | if (holder.getRenderView() != mRenderView) {
481 | debug("onSurfaceCreated: unmatched render callback\n");
482 | return;
483 | }
484 | mSurfaceHolder = holder;
485 | IjkVideoManager.getInstance().bindSurfaceHolder(IjkVideoView.this, mSurfaceHolder);
486 | showPauseCover();
487 | }
488 |
489 | @Override
490 | public void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) {
491 | if (holder.getRenderView() != mRenderView) {
492 | debug("onSurfaceDestroyed: unmatched render callback\n");
493 | return;
494 | }
495 | mSurfaceHolder = null;
496 | }
497 | };
498 |
499 |
500 | private Bitmap mPauseBitmap;
501 |
502 | @Override
503 | public Uri getCurrentUri() {
504 | return mUri;
505 | }
506 |
507 | @Override
508 | public Bitmap getPauseBitmap() {
509 | return mPauseBitmap;
510 | }
511 |
512 | @Override
513 | public void setPauseBitmap(Bitmap pauseBitmap) {
514 | releasePauseCover();
515 | mPauseBitmap = pauseBitmap;
516 | }
517 |
518 | /** 初始化暂停时的封面 */
519 | @Override
520 | public void initPauseCover() {
521 | if (mRenderView == null) {
522 | return;
523 | }
524 | if (mPauseBitmap != null && !mPauseBitmap.isRecycled()) {
525 | mPauseBitmap.recycle();
526 | }
527 | mPauseBitmap = null;
528 | mPauseBitmap = mRenderView.getBitmap();
529 | }
530 |
531 | /** 更新暂停时的封面 */
532 | private void updatePauseCover() {
533 | if (mPauseBitmap == null || mPauseBitmap.isRecycled()) {
534 | initPauseCover();
535 | }
536 | }
537 |
538 | /** 显示暂停切换显示的bitmap */
539 | private void showPauseCover() {
540 | // TODO: 2017/8/11 暂时只兼容 TextureRenderView
541 | if (mPauseBitmap != null && !mPauseBitmap.isRecycled() && mSurfaceHolder != null
542 | && mRenderView != null) {
543 | Surface surface = mSurfaceHolder.openSurface();
544 | if (surface != null && surface.isValid()) {
545 | int width = mRenderView.getView().getWidth();
546 | int height = mRenderView.getView().getHeight();
547 | RectF rectF = new RectF(0, 0, width, height);
548 | Canvas canvas = surface.lockCanvas(new Rect(0, 0, width, height));
549 | if (canvas != null) {
550 | canvas.drawBitmap(mPauseBitmap, null, rectF, null);
551 | surface.unlockCanvasAndPost(canvas);
552 | }
553 | }
554 | }
555 | }
556 |
557 | /** 释放暂停时的封面 */
558 | private void releasePauseCover() {
559 | if (mPauseBitmap != null && !mPauseBitmap.isRecycled()) {
560 | mPauseBitmap.recycle();
561 | mPauseBitmap = null;
562 | }
563 | }
564 |
565 |
566 | ///////////////////////////////////////////////////////////////////////////
567 | // util
568 | ///////////////////////////////////////////////////////////////////////////
569 |
570 |
571 | private void debug(@NonNull String info) {
572 | LoggerUtils.debugLog(TAG, info);
573 | }
574 |
575 | private void error(@NonNull String info) {
576 | LoggerUtils.errorLog(TAG, info);
577 | }
578 | }
579 |
--------------------------------------------------------------------------------
/player/src/main/java/com/leo/player/media/weiget/ENDownloadView.java:
--------------------------------------------------------------------------------
1 | package com.leo.player.media.weiget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.content.res.TypedArray;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.Paint;
11 | import android.graphics.Path;
12 | import android.graphics.RectF;
13 | import android.util.AttributeSet;
14 | import android.view.View;
15 | import android.view.animation.LinearInterpolator;
16 | import android.view.animation.OvershootInterpolator;
17 |
18 | import com.leo.player.R;
19 |
20 |
21 | /**
22 | * Created by codeest on 2016/11/9.
23 | */
24 |
25 | public class ENDownloadView extends View {
26 |
27 | public static final int STATE_PRE = 0;
28 |
29 | public static final int STATE_DOWNLOADING = 1;
30 |
31 | public static final int STATE_END = 2;
32 |
33 | public static final int STATE_RESET = 3;
34 |
35 | private static final int DEFAULT_LINE_COLOR = Color.WHITE;
36 |
37 | private static final int DEFAULT_BG_LINE_COLOR = 0xff3a3f45;
38 |
39 | private static final int DEFAULT_TEXT_COLOR = Color.WHITE;
40 |
41 | private static final int DEFAULT_LINE_WIDTH = 9;
42 |
43 | private static final int DEFAULT_BG_LINE_WIDTH = 9;
44 |
45 | private static final int DEFAULT_TEXT_SIZE = 14;
46 |
47 | private static final int DEFAULT_STATE = STATE_PRE;
48 |
49 | private static final int DEFAULT_RIPPLE_SPEED = 2;
50 |
51 | private static final int DEFAULT_DOWNLOAD_TIME = 2000;
52 |
53 | private static final DownloadUnit DEFAULT_DOWNLOAD_UNIT = DownloadUnit.B;
54 |
55 | private OnDownloadStateListener onDownloadStateListener;
56 |
57 | private int mCurrentState;
58 |
59 | private float mCurrentRippleX;
60 |
61 | private double mCurrentSize, mTotalSize;
62 |
63 | private int mTextSize;
64 |
65 | private int mDownloadTime;
66 |
67 | private DownloadUnit mUnit;
68 |
69 | private Paint mPaint, mBgPaint, mTextPaint;
70 |
71 | private Path mPath;
72 |
73 | private RectF mRectF, mClipRectF;
74 |
75 | ValueAnimator mValueAnimator;
76 |
77 | private float mFraction;
78 |
79 | private float mWidth, mHeight;
80 |
81 | private float mCenterX, mCenterY;
82 |
83 | private float mBaseLength, mCircleRadius, mBaseRippleLength;
84 |
85 | public static enum DownloadUnit {
86 | GB,
87 | MB,
88 | KB,
89 | B,
90 | NONE;
91 |
92 | private DownloadUnit() {
93 | }
94 | }
95 |
96 | public ENDownloadView(Context context) {
97 | super(context);
98 | }
99 |
100 | public ENDownloadView(Context context, AttributeSet attrs) {
101 | super(context, attrs);
102 |
103 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ENDownloadView);
104 | int lineColor = ta.getColor(R.styleable.ENDownloadView_line_color, DEFAULT_LINE_COLOR);
105 | int bgLineColor = ta.getColor(R.styleable.ENDownloadView_bg_line_color, DEFAULT_BG_LINE_COLOR);
106 | int textColor = ta.getColor(R.styleable.ENDownloadView_text_color, DEFAULT_TEXT_COLOR);
107 | int lineWidth = ta.getInteger(R.styleable.ENDownloadView_line_color, DEFAULT_LINE_WIDTH);
108 | int bgLineWidth = ta.getInteger(R.styleable.ENDownloadView_bg_line_width, DEFAULT_BG_LINE_WIDTH);
109 | int textSize = ta.getInteger(R.styleable.ENDownloadView_text_size, DEFAULT_TEXT_SIZE);
110 | ta.recycle();
111 |
112 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
113 | mPaint.setStyle(Paint.Style.STROKE);
114 | mPaint.setStrokeCap(Paint.Cap.ROUND);
115 | mPaint.setStrokeWidth(lineWidth);
116 | mPaint.setColor(lineColor);
117 |
118 | mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
119 | mBgPaint.setStyle(Paint.Style.STROKE);
120 | mBgPaint.setStrokeCap(Paint.Cap.ROUND);
121 | mBgPaint.setStrokeWidth(bgLineWidth);
122 | mBgPaint.setColor(bgLineColor);
123 |
124 | mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
125 | mTextPaint.setColor(textColor);
126 | mTextPaint.setTextSize(textSize);
127 | mTextPaint.setTextAlign(Paint.Align.CENTER);
128 |
129 | mPath = new Path();
130 |
131 | mTextSize = textSize;
132 | mCurrentState = DEFAULT_STATE;
133 | mUnit = DEFAULT_DOWNLOAD_UNIT;
134 | mDownloadTime = DEFAULT_DOWNLOAD_TIME;
135 | }
136 |
137 | @Override
138 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
139 | super.onSizeChanged(w, h, oldw, oldh);
140 | mWidth = w;
141 | mHeight = h;
142 | mCenterX = mWidth / 2;
143 | mCenterY = mHeight / 2;
144 | mCircleRadius = mWidth * 5 / 12;
145 | mBaseLength = mCircleRadius / 3;
146 | mBaseRippleLength = 4.4f * mBaseLength / 12;
147 | mCurrentRippleX = mCenterX - mBaseRippleLength * 10;
148 | mRectF = new RectF(mCenterX - mCircleRadius, mCenterY - mCircleRadius, mCenterX + mCircleRadius, mCenterY + mCircleRadius);
149 | mClipRectF = new RectF(mCenterX - 6 * mBaseRippleLength, 0, mCenterX + 6 * mBaseRippleLength, mHeight);
150 | }
151 |
152 | @Override
153 | protected void onDraw(Canvas canvas) {
154 | super.onDraw(canvas);
155 | switch (mCurrentState) {
156 | case STATE_PRE:
157 | if (mFraction <= 0.4) {
158 | canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
159 | canvas.drawLine(mCenterX - mBaseLength, mCenterY, mCenterX, mCenterY + mBaseLength, mPaint);
160 | canvas.drawLine(mCenterX, mCenterY + mBaseLength, mCenterX + mBaseLength, mCenterY, mPaint);
161 | canvas.drawLine(mCenterX, mCenterY + mBaseLength - 1.3f * mBaseLength / 0.4f * mFraction,
162 | mCenterX, mCenterY - 1.6f * mBaseLength + 1.3f * mBaseLength / 0.4f * mFraction, mPaint);
163 | } else if (mFraction <= 0.6) {
164 | canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
165 | canvas.drawCircle(mCenterX, mCenterY - 0.3f * mBaseLength, 2, mPaint);
166 | canvas.drawLine(mCenterX - mBaseLength - mBaseLength * 1.2f / 0.2f * (mFraction - 0.4f), mCenterY, mCenterX, mCenterY + mBaseLength - mBaseLength / 0.2f * (mFraction - 0.4f), mPaint);
167 | canvas.drawLine(mCenterX, mCenterY + mBaseLength - mBaseLength / 0.2f * (mFraction - 0.4f), mCenterX + mBaseLength + mBaseLength * 1.2f / 0.2f * (mFraction - 0.4f), mCenterY, mPaint);
168 | } else if (mFraction <= 1) {
169 | canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
170 | canvas.drawCircle(mCenterX, mCenterY - 0.3f * mBaseLength - (mCircleRadius - 0.3f * mBaseLength) / 0.4f * (mFraction - 0.6f), 2, mPaint);
171 | canvas.drawLine(mCenterX - mBaseLength * 2.2f, mCenterY, mCenterX + mBaseLength * 2.2f, mCenterY, mPaint);
172 | } else {
173 | canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
174 | canvas.drawCircle(mCenterX, mCenterY - mCircleRadius - mBaseLength * 3 * (mFraction - 1), 3, mPaint);
175 | canvas.drawLine(mCenterX - mBaseLength * 2.2f, mCenterY, mCenterX + mBaseLength * 2.2f, mCenterY, mPaint);
176 | }
177 | break;
178 | case STATE_DOWNLOADING:
179 | if (mFraction <= 0.2) {
180 | mTextPaint.setTextSize(mTextSize / 0.2f * mFraction);
181 | }
182 | canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
183 | canvas.drawArc(mRectF, -90, 359.99f * mFraction, false, mPaint);
184 | mPath.reset();
185 | mCurrentRippleX += DEFAULT_RIPPLE_SPEED;
186 | if (mCurrentRippleX > mCenterX - mBaseRippleLength * 6)
187 | mCurrentRippleX = mCenterX - mBaseRippleLength * 10;
188 | mPath.moveTo(mCurrentRippleX, mCenterY);
189 | for (int i = 0; i < 4; i++) {
190 | mPath.rQuadTo(mBaseRippleLength, -(1 - mFraction) * mBaseRippleLength, mBaseRippleLength * 2, 0);
191 | mPath.rQuadTo(mBaseRippleLength, (1 - mFraction) * mBaseRippleLength, mBaseRippleLength * 2, 0);
192 | }
193 | canvas.save();
194 | canvas.clipRect(mClipRectF);
195 | canvas.drawPath(mPath, mPaint);
196 | canvas.restore();
197 | if (mUnit != DownloadUnit.NONE && mCurrentSize > 0) {
198 | //canvas.drawText(String.format("%.2f", mCurrentSize) + getUnitStr(mUnit), mCenterX , mCenterY + 1.4f * mBaseLength , mTextPaint);
199 | }
200 | break;
201 | case STATE_END:
202 | canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mPaint);
203 | if (mFraction <= 0.5) {
204 | mTextPaint.setTextSize(mTextSize - mTextSize / 0.2f * mFraction);
205 | } else {
206 | mTextPaint.setTextSize(0);
207 | }
208 | if (mUnit != DownloadUnit.NONE && mCurrentSize > 0) {
209 | canvas.drawText(String.format("%.2f", mCurrentSize) + getUnitStr(mUnit), mCenterX, mCenterY + 1.4f * mBaseLength, mTextPaint);
210 | }
211 | canvas.drawLine(mCenterX - mBaseLength * 2.2f + mBaseLength * 1.2f * mFraction, mCenterY,
212 | mCenterX - mBaseLength * 0.5f, mCenterY + mBaseLength * 0.5f * mFraction * 1.3f, mPaint);
213 | canvas.drawLine(mCenterX - mBaseLength * 0.5f, mCenterY + mBaseLength * 0.5f * mFraction * 1.3f,
214 | mCenterX + mBaseLength * 2.2f - mBaseLength * mFraction, mCenterY - mBaseLength * mFraction * 1.3f, mPaint);
215 | break;
216 | case STATE_RESET:
217 | canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
218 | canvas.drawLine(mCenterX - mBaseLength, mCenterY,
219 | mCenterX - mBaseLength * 0.5f + mBaseLength * 0.5f * mFraction, mCenterY + mBaseLength * 0.65f + mBaseLength * 0.35f * mFraction, mPaint);
220 | canvas.drawLine(mCenterX - mBaseLength * 0.5f + mBaseLength * 0.5f * mFraction, mCenterY + mBaseLength * 0.65f + mBaseLength * 0.35f * mFraction,
221 | mCenterX + mBaseLength * 1.2f - mBaseLength * 0.2f * mFraction, mCenterY - 1.3f * mBaseLength + 1.3f * mBaseLength * mFraction, mPaint);
222 | canvas.drawLine(mCenterX - mBaseLength * 0.5f + mBaseLength * 0.5f * mFraction, mCenterY + mBaseLength * 0.65f + mBaseLength * 0.35f * mFraction,
223 | mCenterX - mBaseLength * 0.5f + mBaseLength * 0.5f * mFraction,
224 | mCenterY + mBaseLength * 0.65f - mBaseLength * 2.25f * mFraction, mPaint);
225 | break;
226 | }
227 | }
228 |
229 | public void release() {
230 | if (mValueAnimator != null) {
231 | mValueAnimator.removeAllListeners();
232 | mValueAnimator.removeAllUpdateListeners();
233 | if (mValueAnimator.isRunning())
234 | mValueAnimator.cancel();
235 | mValueAnimator = null;
236 | }
237 | }
238 |
239 | public void start() {
240 | if (mValueAnimator != null) {
241 | mValueAnimator.removeAllListeners();
242 | mValueAnimator.removeAllUpdateListeners();
243 | if (mValueAnimator.isRunning())
244 | mValueAnimator.cancel();
245 | mValueAnimator = null;
246 | }
247 | mCurrentState = STATE_DOWNLOADING;
248 | mValueAnimator = ValueAnimator.ofFloat(1.f, 100.f);
249 | mValueAnimator.setDuration(1500);
250 | mValueAnimator.setInterpolator(new OvershootInterpolator());
251 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
252 | @Override
253 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
254 | mFraction = valueAnimator.getAnimatedFraction();
255 | invalidate();
256 | }
257 | });
258 | mValueAnimator.addListener(new AnimatorListenerAdapter() {
259 | @Override
260 | public void onAnimationEnd(Animator animation) {
261 | mCurrentState = STATE_DOWNLOADING;
262 | downloadAnim();
263 | }
264 | });
265 | mValueAnimator.start();
266 | }
267 |
268 | private void downloadAnim() {
269 | if (mValueAnimator != null) {
270 | mValueAnimator.removeAllListeners();
271 | mValueAnimator.removeAllUpdateListeners();
272 | if (mValueAnimator.isRunning())
273 | mValueAnimator.cancel();
274 | mValueAnimator = null;
275 | }
276 | if (mCurrentState != STATE_DOWNLOADING) {
277 | return;
278 | }
279 | mValueAnimator = ValueAnimator.ofFloat(1.f, 100.f);
280 | mValueAnimator.setDuration(mDownloadTime);
281 | mValueAnimator.setInterpolator(new LinearInterpolator());
282 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
283 | @Override
284 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
285 | mFraction = valueAnimator.getAnimatedFraction();
286 | if (mUnit != DownloadUnit.NONE && mTotalSize > 0)
287 | mCurrentSize = mFraction * mTotalSize;
288 | invalidate();
289 | }
290 | });
291 | mValueAnimator.addListener(new AnimatorListenerAdapter() {
292 | @Override
293 | public void onAnimationEnd(Animator animation) {
294 | mCurrentState = STATE_DOWNLOADING;
295 | downloadAnim();
296 | }
297 | });
298 | mValueAnimator.start();
299 | }
300 |
301 | private void endAnim() {
302 | if (mValueAnimator != null) {
303 | mValueAnimator.removeAllListeners();
304 | mValueAnimator.removeAllUpdateListeners();
305 | if (mValueAnimator.isRunning())
306 | mValueAnimator.cancel();
307 | mValueAnimator = null;
308 | }
309 | mValueAnimator = ValueAnimator.ofFloat(1.f, 100.f);
310 | mValueAnimator.setDuration(700);
311 | mValueAnimator.setInterpolator(new OvershootInterpolator());
312 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
313 | @Override
314 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
315 | mFraction = valueAnimator.getAnimatedFraction();
316 | invalidate();
317 | }
318 | });
319 | mValueAnimator.addListener(new AnimatorListenerAdapter() {
320 | @Override
321 | public void onAnimationEnd(Animator animation) {
322 | mFraction = 0;
323 | mCurrentState = STATE_RESET;
324 | if (onDownloadStateListener != null) {
325 | onDownloadStateListener.onDownloadFinish();
326 | }
327 | }
328 | });
329 | mValueAnimator.start();
330 | }
331 |
332 | public void reset() {
333 | mFraction = 0;
334 | mCurrentState = STATE_PRE;
335 | if (mValueAnimator != null) {
336 | mValueAnimator.removeAllListeners();
337 | mValueAnimator.removeAllUpdateListeners();
338 | if (mValueAnimator.isRunning())
339 | mValueAnimator.cancel();
340 | mValueAnimator = null;
341 | }
342 | }
343 |
344 | private String getUnitStr(DownloadUnit unit) {
345 | switch (unit) {
346 | case GB:
347 | return " gb";
348 | case MB:
349 | return " mb";
350 | case KB:
351 | return " kb";
352 | case B:
353 | return " b";
354 | }
355 | return " b";
356 | }
357 |
358 | public void setDownloadConfig(int downloadTime, double downloadFileSize, DownloadUnit unit) {
359 | mDownloadTime = downloadTime;
360 | mTotalSize = downloadFileSize;
361 | mUnit = unit;
362 | }
363 |
364 | public int getCurrentState() {
365 | return mCurrentState;
366 | }
367 |
368 | interface OnDownloadStateListener {
369 |
370 | void onDownloadFinish();
371 |
372 | void onResetFinish();
373 | }
374 |
375 | public void setOnDownloadStateListener(OnDownloadStateListener onDownloadStateListener) {
376 | this.onDownloadStateListener = onDownloadStateListener;
377 | }
378 | }
379 |
--------------------------------------------------------------------------------
/player/src/main/java/com/leo/player/media/weiget/ENPlayView.java:
--------------------------------------------------------------------------------
1 | package com.leo.player.media.weiget;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.CornerPathEffect;
9 | import android.graphics.Paint;
10 | import android.graphics.Path;
11 | import android.graphics.PathMeasure;
12 | import android.graphics.RectF;
13 | import android.util.AttributeSet;
14 | import android.util.TypedValue;
15 | import android.view.View;
16 | import android.view.animation.AnticipateInterpolator;
17 |
18 | import com.leo.player.R;
19 |
20 |
21 | /**
22 | * Created by codeest on 16/11/7.
23 | */
24 |
25 | public class ENPlayView extends View {
26 |
27 | public final static int STATE_PLAY = 0;
28 |
29 | public final static int STATE_PAUSE = 1;
30 |
31 | public final static int DEFAULT_LINE_COLOR = Color.WHITE;
32 |
33 | public final static int DEFAULT_BG_LINE_COLOR = 0xfffafafa;
34 |
35 | public final static int DEFAULT_LINE_WIDTH = 4;
36 |
37 | public final static int DEFAULT_BG_LINE_WIDTH = 4;
38 |
39 | public final static int DEFAULT_DURATION = 600;
40 |
41 | private int mCurrentState = STATE_PAUSE;
42 |
43 | private Paint mPaint, mBgPaint;
44 |
45 | private int mWidth, mHeight;
46 |
47 | private int mCenterX, mCenterY;
48 |
49 | private int mCircleRadius;
50 |
51 | private RectF mRectF, mBgRectF;
52 |
53 | private float mFraction = 1;
54 |
55 | private Path mPath, mDstPath;
56 |
57 | private PathMeasure mPathMeasure;
58 |
59 | private float mPathLength;
60 |
61 | private int mDuration;
62 |
63 |
64 | public ENPlayView(Context context) {
65 | super(context);
66 | }
67 |
68 | public ENPlayView(Context context, AttributeSet attrs) {
69 | super(context, attrs);
70 |
71 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ENPlayView);
72 | int lineColor = ta.getColor(R.styleable.ENPlayView_play_line_color, DEFAULT_LINE_COLOR);
73 | int bgLineColor = ta.getColor(R.styleable.ENPlayView_play_bg_line_color, DEFAULT_BG_LINE_COLOR);
74 | int lineWidth = ta.getInteger(R.styleable.ENPlayView_play_line_width, dp2px(DEFAULT_LINE_WIDTH));
75 | int bgLineWidth = ta.getInteger(R.styleable.ENPlayView_play_bg_line_width, dp2px(DEFAULT_BG_LINE_WIDTH));
76 | ta.recycle();
77 |
78 | setLayerType(View.LAYER_TYPE_SOFTWARE, null);
79 |
80 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
81 | mPaint.setStyle(Paint.Style.STROKE);
82 | mPaint.setStrokeCap(Paint.Cap.ROUND);
83 | mPaint.setColor(lineColor);
84 | mPaint.setStrokeWidth(lineWidth);
85 | mPaint.setPathEffect(new CornerPathEffect(1));
86 |
87 | mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
88 | mBgPaint.setStyle(Paint.Style.STROKE);
89 | mBgPaint.setStrokeCap(Paint.Cap.ROUND);
90 | mBgPaint.setColor(bgLineColor);
91 | mBgPaint.setStrokeWidth(bgLineWidth);
92 |
93 | mPath = new Path();
94 | mDstPath = new Path();
95 | mPathMeasure = new PathMeasure();
96 |
97 | mDuration = DEFAULT_DURATION;
98 | }
99 |
100 | @Override
101 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
102 | super.onSizeChanged(w, h, oldw, oldh);
103 | mWidth = w * 9 / 10;
104 | mHeight = h * 9 / 10;
105 | mCircleRadius = mWidth / dp2px(4);
106 | mCenterX = w / 2;
107 | mCenterY = h / 2;
108 | mRectF = new RectF(mCenterX - mCircleRadius, mCenterY + 0.6f * mCircleRadius,
109 | mCenterX + mCircleRadius, mCenterY + 2.6f * mCircleRadius);
110 | mBgRectF = new RectF(mCenterX - mWidth / 2, mCenterY - mHeight / 2, mCenterX + mWidth / 2, mCenterY + mHeight / 2);
111 | mPath.moveTo(mCenterX - mCircleRadius, mCenterY + 1.8f * mCircleRadius);
112 | mPath.lineTo(mCenterX - mCircleRadius, mCenterY - 1.8f * mCircleRadius);
113 | mPath.lineTo(mCenterX + mCircleRadius, mCenterY);
114 | mPath.close();
115 | mPathMeasure.setPath(mPath, false);
116 | mPathLength = mPathMeasure.getLength();
117 | }
118 |
119 | @Override
120 | protected void onDraw(Canvas canvas) {
121 | super.onDraw(canvas);
122 | canvas.drawCircle(mCenterX, mCenterY, mWidth / 2, mBgPaint);
123 | if (mFraction < 0) { //嗷~~ 弹性部分
124 | canvas.drawLine(mCenterX + mCircleRadius, mCenterY - 1.6f * mCircleRadius + 10 * mCircleRadius * mFraction,
125 | mCenterX + mCircleRadius, mCenterY + 1.6f * mCircleRadius + 10 * mCircleRadius * mFraction, mPaint);
126 |
127 | canvas.drawLine(mCenterX - mCircleRadius, mCenterY - 1.6f * mCircleRadius,
128 | mCenterX - mCircleRadius, mCenterY + 1.6f * mCircleRadius, mPaint);
129 |
130 | canvas.drawArc(mBgRectF, -105, 360, false, mPaint);
131 | } else if (mFraction <= 0.3) { //嗷~~ 右侧直线和下方曲线
132 | canvas.drawLine(mCenterX + mCircleRadius, mCenterY - 1.6f * mCircleRadius + mCircleRadius * 3.2f / 0.3f * mFraction,
133 | mCenterX + mCircleRadius, mCenterY + 1.6f * mCircleRadius, mPaint);
134 |
135 | canvas.drawLine(mCenterX - mCircleRadius, mCenterY - 1.6f * mCircleRadius,
136 | mCenterX - mCircleRadius, mCenterY + 1.6f * mCircleRadius, mPaint);
137 |
138 | if (mFraction != 0)
139 | canvas.drawArc(mRectF, 0f, 180f / 0.3f * mFraction, false, mPaint);
140 |
141 | canvas.drawArc(mBgRectF, -105 + 360 * mFraction, 360 * (1 - mFraction), false, mPaint);
142 | } else if (mFraction <= 0.6) { //嗷~~ 下方曲线和三角形
143 | canvas.drawArc(mRectF, 180f / 0.3f * (mFraction - 0.3f), 180 - 180f / 0.3f * (mFraction - 0.3f), false, mPaint);
144 |
145 | mDstPath.reset();
146 | //mDstPath.lineTo(0 ,0);
147 | mPathMeasure.getSegment(0.02f * mPathLength, 0.38f * mPathLength + 0.42f * mPathLength / 0.3f * (mFraction - 0.3f),
148 | mDstPath, true);
149 | canvas.drawPath(mDstPath, mPaint);
150 |
151 | canvas.drawArc(mBgRectF, -105 + 360 * mFraction, 360 * (1 - mFraction), false, mPaint);
152 | } else if (mFraction <= 0.8) { //嗷~~ 三角形
153 | mDstPath.reset();
154 | //mDstPath.lineTo(0 ,0);
155 | mPathMeasure.getSegment(0.02f * mPathLength + 0.2f * mPathLength / 0.2f * (mFraction - 0.6f)
156 | , 0.8f * mPathLength + 0.2f * mPathLength / 0.2f * (mFraction - 0.6f),
157 | mDstPath, true);
158 | canvas.drawPath(mDstPath, mPaint);
159 |
160 | canvas.drawArc(mBgRectF, -105 + 360 * mFraction, 360 * (1 - mFraction), false, mPaint);
161 | } else { //嗷~~ 弹性部分
162 | mDstPath.reset();
163 | //mDstPath.lineTo(0 ,0);
164 | mPathMeasure.getSegment(10 * mCircleRadius * (mFraction - 1)
165 | , mPathLength,
166 | mDstPath, true);
167 | canvas.drawPath(mDstPath, mPaint);
168 | }
169 | }
170 |
171 | public void setCurrentState(int state) {
172 | if (mCurrentState == state) {
173 | return;
174 | }
175 | mCurrentState = state;
176 | if (mCurrentState == STATE_PLAY) {
177 | mFraction = 0;
178 | postInvalidate();
179 | }
180 | if (mCurrentState == STATE_PAUSE) {
181 | mFraction = 1;
182 | postInvalidate();
183 | }
184 | }
185 |
186 | public void play() {
187 | if (mCurrentState == STATE_PLAY) {
188 | return;
189 | }
190 | mCurrentState = STATE_PLAY;
191 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.f, 100.f);
192 | valueAnimator.setDuration(mDuration);
193 | valueAnimator.setInterpolator(new AnticipateInterpolator());
194 |
195 |
196 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
197 | @Override
198 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
199 | mFraction = 1 - valueAnimator.getAnimatedFraction();
200 | postInvalidate();
201 | }
202 | });
203 | if (!valueAnimator.isRunning()) {
204 | valueAnimator.start();
205 | }
206 | }
207 |
208 | public void pause() {
209 | if (mCurrentState == STATE_PAUSE) {
210 | return;
211 | }
212 | mCurrentState = STATE_PAUSE;
213 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.f, 100.f);
214 | valueAnimator.setDuration(mDuration);
215 | valueAnimator.setInterpolator(new AnticipateInterpolator());
216 |
217 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
218 | @Override
219 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
220 | mFraction = valueAnimator.getAnimatedFraction();
221 | postInvalidate();
222 | }
223 | });
224 | if (!valueAnimator.isRunning()) {
225 | valueAnimator.start();
226 | }
227 | }
228 |
229 | public int getCurrentState() {
230 | return mCurrentState;
231 | }
232 |
233 | public void setDuration(int duration) {
234 | mDuration = duration;
235 | }
236 |
237 | private int dp2px(int dp) {
238 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
239 | getContext().getResources().getDisplayMetrics());
240 | }
241 |
242 |
243 |
244 |
245 | }
246 |
--------------------------------------------------------------------------------
/player/src/main/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/player/src/main/res/anim/fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/player/src/main/res/drawable-xhdpi/brightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinXiaoTao/Oatmeal/a8d998c2b0ad5771f6e839edb71cca3741ae90c5/player/src/main/res/drawable-xhdpi/brightness.png
--------------------------------------------------------------------------------
/player/src/main/res/drawable-xhdpi/fast_forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinXiaoTao/Oatmeal/a8d998c2b0ad5771f6e839edb71cca3741ae90c5/player/src/main/res/drawable-xhdpi/fast_forward.png
--------------------------------------------------------------------------------
/player/src/main/res/drawable-xhdpi/video_enlarge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinXiaoTao/Oatmeal/a8d998c2b0ad5771f6e839edb71cca3741ae90c5/player/src/main/res/drawable-xhdpi/video_enlarge.png
--------------------------------------------------------------------------------
/player/src/main/res/drawable-xhdpi/video_shrink.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinXiaoTao/Oatmeal/a8d998c2b0ad5771f6e839edb71cca3741ae90c5/player/src/main/res/drawable-xhdpi/video_shrink.png
--------------------------------------------------------------------------------
/player/src/main/res/drawable-xhdpi/volume_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinXiaoTao/Oatmeal/a8d998c2b0ad5771f6e839edb71cca3741ae90c5/player/src/main/res/drawable-xhdpi/volume_1.png
--------------------------------------------------------------------------------
/player/src/main/res/drawable-xhdpi/volume_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinXiaoTao/Oatmeal/a8d998c2b0ad5771f6e839edb71cca3741ae90c5/player/src/main/res/drawable-xhdpi/volume_2.png
--------------------------------------------------------------------------------
/player/src/main/res/drawable-xhdpi/volume_x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LinXiaoTao/Oatmeal/a8d998c2b0ad5771f6e839edb71cca3741ae90c5/player/src/main/res/drawable-xhdpi/volume_x.png
--------------------------------------------------------------------------------
/player/src/main/res/drawable/video_dialog_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/player/src/main/res/drawable/video_position_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/player/src/main/res/drawable/video_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | -
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | -
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/player/src/main/res/drawable/video_seek_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | -
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/player/src/main/res/drawable/video_seek_thumb.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/player/src/main/res/drawable/video_seek_thumb_normal.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/player/src/main/res/drawable/video_seek_thumb_pressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/player/src/main/res/layout/dialog_brightness.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
19 |
20 |
28 |
29 |
--------------------------------------------------------------------------------
/player/src/main/res/layout/dialog_video_position.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
18 |
19 |
24 |
25 |
31 |
32 |
39 |
40 |
41 |
42 |
51 |
52 |
--------------------------------------------------------------------------------
/player/src/main/res/layout/dialog_volume.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
20 |
21 |
22 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/player/src/main/res/layout/media_controller.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
19 |
20 |
27 |
28 |
34 |
35 |
46 |
47 |
54 |
55 |
69 |
70 |
77 |
78 |
85 |
86 |
87 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/player/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/player/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':player'
2 |
--------------------------------------------------------------------------------