Commit ba42588e9145d0e8efdcb7538a12713734546021

Authored by 韩亚云
2 parents 4021f74f 80ac6704

Merge branch 'master' of bj.gitlab.cnlive.com:cnlive-team/newModuleStrike

Showing 96 changed files with 8960 additions and 2939 deletions
app/strike/build.gradle
... ... @@ -179,7 +179,7 @@ dependencies {
179 179 implementation project(path: ':core:imageload')
180 180 implementation project(path: ':module:xiaojiasoundbox')
181 181 implementation project(path: ':module:pedometer')
182   -// implementation project(path: ':module:shanghaishop')
  182 + implementation project(path: ':module:shanghaishop')
183 183 implementation project(path: ':library_baidumap')
184 184 implementation 'com.jakewharton.rxbinding3:rxbinding:3.1.0'
185 185 // implementation project(path: ':core:util')
... ...
app/strike/src/main/AndroidManifest.xml
... ... @@ -9,7 +9,7 @@
9 9 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
10 10  
11 11 <application
12   - android:name="com.cnlive.strike.MyApp"
  12 + android:name="com.cnlive.strike.demo.exoplayer.demo.DemoApplication"
13 13 android:allowBackup="true"
14 14 android:icon="@mipmap/ic_launcher"
15 15 android:label="@string/app_name"
... ... @@ -17,6 +17,24 @@
17 17 android:roundIcon="@mipmap/ic_launcher_round"
18 18 android:supportsRtl="true"
19 19 android:theme="@style/AppTheme">
  20 + <provider
  21 + android:name="androidx.core.content.FileProvider"
  22 + android:authorities="${applicationId}.fileprovider"
  23 + android:exported="false"
  24 + android:grantUriPermissions="true"
  25 + tools:replace="android:authorities">
  26 + <meta-data
  27 + android:name="android.support.FILE_PROVIDER_PATHS"
  28 + android:resource="@xml/app_files"
  29 + tools:replace="android:resource" />
  30 + </provider>
  31 +
  32 + <activity android:name="com.cnlive.strike.demo.exoplayer.demo.PlayerActivity" />
  33 + <activity android:name="com.cnlive.strike.demo.exoplayer.demo.SampleChooserActivity" />
  34 + <activity
  35 + android:name="com.cnlive.strike.demo.activity.ExoPlayerActivity"
  36 + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
  37 + android:screenOrientation="portrait" />
20 38 <activity android:name="com.cnlive.strike.demo.activity.CNplayerActivity" />
21 39 <activity android:name="com.cnlive.strike.demo.activity.DemoAnimActivity" />
22 40 <activity
... ... @@ -37,7 +55,8 @@
37 55  
38 56 <meta-data
39 57 android:name="com.baidu.lbsapi.API_KEY"
40   - android:value="${BAIDU_MAP_APP_KEY}" />
  58 + android:value="${BAIDU_MAP_APP_KEY}"
  59 + tools:replace="android:value" />
41 60 <meta-data
42 61 android:name="com.baidu.speech.APP_ID"
43 62 android:value="${BAIDU_APP_ID}" />
... ...
app/strike/src/main/java/com/cnlive/strike/MyApp.java
... ... @@ -29,6 +29,9 @@ public class MyApp extends BaseApplication {
29 29 //自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
30 30 //包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
31 31 SDKInitializer.setCoordType(CoordType.GCJ02);
  32 + //初始化上海电商
  33 + cn.cnlive.b2c.AppApplication.initSDK(this);
  34 +
32 35 }
33 36  
34 37  
... ...
app/strike/src/main/java/com/cnlive/strike/demo/activity/CNplayerActivity.java
1 1 package com.cnlive.strike.demo.activity;
2 2  
3 3 import android.content.Intent;
  4 +import android.os.Handler;
  5 +import android.widget.Toast;
4 6  
  7 +import androidx.annotation.Nullable;
5 8 import androidx.databinding.ViewDataBinding;
6 9  
  10 +import com.alibaba.android.arouter.facade.annotation.Route;
7 11 import com.cnlive.cloud.BuildConfig;
8 12 import com.cnlive.cloud.R;
9 13 import com.cnlive.cloud.databinding.ActivityCnvideoBinding;
... ... @@ -17,11 +21,21 @@ import com.libs.video.util.ProbeUtil;
17 21 import com.libs.video.video.VideoView;
18 22 import com.libs.video.video.base.IMediaPlayer;
19 23  
  24 +import org.greenrobot.eventbus.Subscribe;
  25 +import org.greenrobot.eventbus.ThreadMode;
  26 +
  27 +@Route(path = CNplayerActivity.PATH)
20 28 public class CNplayerActivity extends BaseActivity implements IMediaPlayer.OnPreparedListener,
21 29 IMediaPlayer.OnCompletionListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnInfoListener,
22 30 IMediaPlayer.OnPlayStateListener {
23 31 private ActivityCnvideoBinding binding;
24 32 private String type = "0";
  33 + public static final String PATH = "/test/CNplayerActivity";
  34 +
  35 + @Override
  36 + protected void onDestroy() {
  37 + super.onDestroy();
  38 + }
25 39  
26 40 @Override
27 41 protected int getBindLayoutId() {
... ... @@ -51,23 +65,17 @@ public class CNplayerActivity extends BaseActivity implements IMediaPlayer.OnPre
51 65 binding.videoview.setOnErrorListener(this);
52 66 binding.videoview.setOnInfoListener(this);
53 67 if ("0".equals(type)) {
54   - QMUITipDialogUtil.loadingDialog(me,"点播");
  68 + QMUITipDialogUtil.loadingDialog(me, "点播");
55 69 binding.videoview.setDataSource(new DataSource("223_88c98ab1395f4762b703fbdd9d59d2e5", Config.TYPE_VOD, Config.TYPE_PLAY_RATE_2));
56 70 } else {
57   - QMUITipDialogUtil.loadingDialog(me,"直播");
  71 +// QMUITipDialogUtil.loadingDialog(me, "直播");
58 72 // binding.videoview.setDataSource(new DataSource("604_233216be9552476b93d2c0cac7819222", Config.TYPE_LIVE, Config.TYPE_PLAY_RATE_2));
59 73 // binding.videoview.setDataSource(new DataSource("223_b66053d9eaf147efa58eb1ae4b47661e", Config.TYPE_VOD, Config.TYPE_PLAY_RATE_2));
60   - binding.videoview.setDataSource(new DataSource("604_75ca27cce3a043b1876f694dd1125234", Config.TYPE_LIVE, Config.TYPE_PLAY_RATE_2));
  74 + binding.videoview.setDataSource(new DataSource("604_maishicaijing", Config.TYPE_LIVE, Config.TYPE_PLAY_RATE_2));
61 75  
62 76 }
63 77 binding.videoview.start();
64 78  
65   -
66   - }
67   -
68   - @Override
69   - protected void onDestroy() {
70   - super.onDestroy();
71 79 }
72 80  
73 81 @Override
... ... @@ -83,27 +91,29 @@ public class CNplayerActivity extends BaseActivity implements IMediaPlayer.OnPre
83 91  
84 92 @Override
85 93 public boolean onError(IMediaPlayer mediaplayer, int what, int extra) {
86   - AlertUtil.showDeftToast(me, "onError");
  94 +// AlertUtil.showDeftToast(me, "onError");
87 95  
88 96 return false;
89 97 }
90 98  
91 99 @Override
92 100 public void onCompletion(IMediaPlayer mediaplayer) {
93   - AlertUtil.showDeftToast(me, "onCompletion");
  101 +// AlertUtil.showDeftToast(me, "onCompletion");
94 102 mediaplayer.start();
95 103 }
96 104  
97 105 @Override
98 106 public void onPrepared(IMediaPlayer mediaplayer) {
99   - AlertUtil.showDeftToast(me, "onPrepared");
100   - QMUITipDialogUtil.dismessDialog();
  107 +// AlertUtil.showDeftToast(me, "onPrepared");
  108 +// QMUITipDialogUtil.dismessDialog();
101 109 mediaplayer.start();
102 110 }
103 111  
104 112 @Override
105 113 public void onPlayState(boolean isPlay) {
106   - AlertUtil.showDeftToast(me, "onPlayState:" + isPlay);
  114 +// AlertUtil.showDeftToast(me, "onPlayState:" + isPlay);
107 115  
108 116 }
  117 +
  118 +
109 119 }
... ...
app/strike/src/main/java/com/cnlive/strike/demo/activity/DemoAnimActivity.java
... ... @@ -82,7 +82,7 @@ public class DemoAnimActivity extends BaseActivity {
82 82 @Override
83 83 public void onWindowFocusChanged(boolean hasFocus) {
84 84 if (hasFocus) {
85   - rope = YoYo.with(Techniques.FadeIn).duration(1000).playOn(mTarget);// after start,just click mTarget view, rope is not init
  85 + rope = YoYo.with(Techniques.FadeIn).duration(1000).playOn(mTarget);// after play,just click mTarget view, rope is not init
86 86 }
87 87 }
88 88  
... ...
app/strike/src/main/java/com/cnlive/strike/demo/activity/DemoMvpActivity.java
... ... @@ -5,14 +5,13 @@ import android.content.Intent;
5 5 import android.os.Handler;
6 6 import android.os.Message;
7 7 import android.text.TextUtils;
8   -import android.view.LayoutInflater;
9 8 import android.view.View;
10   -import android.widget.LinearLayout;
11   -import android.widget.TextView;
12 9  
13 10 import androidx.annotation.NonNull;
  11 +import androidx.annotation.Nullable;
14 12 import androidx.databinding.ViewDataBinding;
15 13  
  14 +import com.alibaba.android.arouter.launcher.ARouter;
16 15 import com.cnlive.cloud.R;
17 16 import com.cnlive.cloud.databinding.ActivityDemoMvpMainBinding;
18 17 import com.cnlive.cloud.user.auth.UserService;
... ... @@ -23,8 +22,11 @@ import com.cnlive.cloud.user.ui.activity.PersonalDataActivity;
23 22 import com.cnlive.cloud.wjjpay.pay.WjjPay;
24 23 import com.cnlive.cloud.xiaojiaspeaker.commonInfo.XiaoJIaCommInfo;
25 24 import com.cnlive.cloud.xiaojiaspeaker.ui.activity.AllDeviceActivity;
  25 +import com.cnlive.core.libs.base.arouter.user.CNLiveUserService;
26 26 import com.cnlive.core.libs.base.frame.activity.BaseActivity;
27 27 import com.cnlive.core.libs.base.interfaces.OnPermissionResponseListener;
  28 +import com.cnlive.core.libs.base.util.AlertUtil;
  29 +import com.cnlive.core.libs.base.util.OpenServiceHelpUtil;
28 30 import com.cnlive.core.libs.base.util.QMUITipDialogUtil;
29 31 import com.cnlive.core.libs.base.util.StatusBarConfig;
30 32 import com.cnlive.core.network.HttpConn;
... ... @@ -33,9 +35,10 @@ import com.cnlive.core.network.model.BaseResult;
33 35 import com.cnlive.core.network.request.BaseRequest;
34 36 import com.cnlive.moudle.pedometer.commonInfo.CommonInfo;
35 37 import com.cnlive.moudle.pedometer.ui.activity.RunLineActivity;
36   -import com.cnlive.strike.MyApp;
37 38 import com.cnlive.strike.demo.activity.presenter.DemoActivityPresenter;
38 39 import com.cnlive.strike.demo.activity.view.DemoActivityView;
  40 +import com.cnlive.strike.demo.exoplayer.demo.PlayerActivity;
  41 +import com.cnlive.strike.demo.exoplayer.demo.SampleChooserActivity;
39 42 import com.cnlive.strike.network.api.ApiService;
40 43 import com.cnlive.strike.network.api.ApiTimeService;
41 44 import com.cnlive.strike.network.api.TimeServiceBean;
... ... @@ -399,9 +402,47 @@ public class DemoMvpActivity extends BaseActivity&lt;DemoActivityView, DemoActivity
399 402 binding.btnLivePlayer.setOnClickListener(new View.OnClickListener() {
400 403 @Override
401 404 public void onClick(View v) {
402   - Intent intent=new Intent(me,CNplayerActivity.class);
403   - intent.putExtra("type","1");
404   - startActivity(intent); }
  405 +
  406 +// Intent intent=new Intent(me,CNplayerActivity.class);
  407 +// intent.putExtra("type","1");
  408 +// startActivityForResult(intent,100);
  409 +
  410 + ARouter.getInstance().build(CNplayerActivity.PATH).navigation(me,300);
  411 +
  412 + }
  413 + });
  414 + binding.btnExo.setOnClickListener(new View.OnClickListener() {
  415 + @Override
  416 + public void onClick(View v) {
  417 + Intent intent=new Intent(me,ExoPlayerActivity.class);
  418 + startActivity(intent);
  419 + }
  420 + });
  421 + //exo官方
  422 + binding.btnExoHome.setOnClickListener(new View.OnClickListener() {
  423 + @Override
  424 + public void onClick(View v) {
  425 + Intent intent=new Intent(me, SampleChooserActivity.class);
  426 + startActivity(intent);
  427 + }
  428 + });
  429 + //获取uid
  430 + binding.btnUid.setOnClickListener(new View.OnClickListener() {
  431 + @Override
  432 + public void onClick(View v) {
  433 + CNLiveUserService userService= OpenServiceHelpUtil.getUserService(me);
  434 + if (null!=userService){
  435 + QMUITipDialogUtil.textDialog(me,userService.getUid(me));
  436 + }
  437 + }
  438 + });
  439 + //上海电商
  440 + binding.btnShanghai.setOnClickListener(new View.OnClickListener() {
  441 + @Override
  442 + public void onClick(View v) {
  443 + Intent intent = new Intent(me, cn.cnlive.b2c.MainActivity.class);
  444 + startActivity(intent);
  445 + }
405 446 });
406 447  
407 448 }
... ... @@ -493,4 +534,5 @@ public class DemoMvpActivity extends BaseActivity&lt;DemoActivityView, DemoActivity
493 534 // /*添加View到当前显示的Activity*/
494 535 // MyApp.getInstance().showBottomView(view);
495 536 }
  537 +
496 538 }
... ...
app/strike/src/main/java/com/cnlive/strike/demo/activity/ExoPlayerActivity.java 0 → 100644
  1 +package com.cnlive.strike.demo.activity;
  2 +
  3 +import android.net.Uri;
  4 +import android.view.Surface;
  5 +import android.view.SurfaceControl;
  6 +import android.view.SurfaceView;
  7 +import android.view.View;
  8 +
  9 +import androidx.annotation.Nullable;
  10 +import androidx.core.content.ContextCompat;
  11 +import androidx.databinding.ViewDataBinding;
  12 +
  13 +import com.cnlive.cloud.R;
  14 +import com.cnlive.cloud.databinding.ActivityExplayerBinding;
  15 +import com.cnlive.core.libs.base.frame.activity.BaseActivity;
  16 +import com.google.android.exoplayer2.C;
  17 +import com.google.android.exoplayer2.ExoPlaybackException;
  18 +import com.google.android.exoplayer2.PlaybackParameters;
  19 +import com.google.android.exoplayer2.Player;
  20 +import com.google.android.exoplayer2.SimpleExoPlayer;
  21 +import com.google.android.exoplayer2.Timeline;
  22 +import com.google.android.exoplayer2.source.TrackGroupArray;
  23 +import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
  24 +import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
  25 +import com.google.android.exoplayer2.video.VideoListener;
  26 +import com.libs.video.video.exoplayer.CNLiveBaseVideoControlView;
  27 +import com.libs.video.video.exoplayer.MyTestControlView;
  28 +import com.libs.video.video.exoplayer.VideoPlayer;
  29 +
  30 +public class ExoPlayerActivity extends BaseActivity implements VideoListener, Player.EventListener {
  31 + ActivityExplayerBinding binding;
  32 + //step2. 创建播放器
  33 +// public SimpleExoPlayer player;
  34 + SurfaceControl surfaceControl;
  35 + Surface videoSurface;
  36 + private VideoPlayer videoPlayer;
  37 +
  38 + @Override
  39 + protected int getBindLayoutId() {
  40 + return R.layout.activity_explayer;
  41 + }
  42 +
  43 + @Override
  44 + protected void initBindingLayoutData(ViewDataBinding baseBinding) {
  45 + binding = (ActivityExplayerBinding) baseBinding;
  46 + }
  47 +
  48 + @Override
  49 + protected void initView() {
  50 + initVideoPlayer();
  51 + // Produces DataSource instances through which media data is loaded.
  52 +// DataSource dataSource =new DefaultDataSource(context,Util.getUserAgent(context, "yourApplicationName"),true);
  53 +// Create a data source factory.
  54 +// DataSource.Factory dataSourceFactory =
  55 +// new DefaultHttpDataSourceFactory(Util.getUserAgent(context, "app-name"));
  56 +//
  57 + Uri mp4VideoUri = Uri.parse("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8");
  58 +//// HlsMediaSource hlsMediaSource =
  59 +//// new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mp4VideoUri);
  60 +// Uri dashUri = Uri.parse("rtmp://tyzg.rtmps2.cnlive.com/live-tyzg-577/577_999");
  61 +//
  62 +// RtmpDataSourceFactory rtmpDataSourceFactory=new RtmpDataSourceFactory();
  63 +//
  64 +// MediaSource mediaSource = new ProgressiveMediaSource.Factory(rtmpDataSourceFactory)
  65 +// .createMediaSource(dashUri);
  66 +// HlsMediaSource hlsMediaSource =
  67 +// new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mp4VideoUri);
  68 +// SsMediaSource ssMediaSource = new SsMediaSource.Factory(dataSourceFactory).createMediaSource(dashUri);
  69 +//
  70 +// //step2. 创建播放器
  71 +// player = new SimpleExoPlayer.Builder(context).build();
  72 +// player.addVideoListener(this);
  73 +// player.addListener(this);
  74 +// // Prepare the player with the source.
  75 +// player.prepare(hlsMediaSource);
  76 +// //绑定视图
  77 +// binding.playerView.setPlayer(player);
  78 +
  79 + //全屏
  80 +// player.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
  81 + //按比例放大
  82 +// player.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
  83 +// player.setForegroundMode(true);
  84 +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
  85 +// surfaceControl =
  86 +// new SurfaceControl.Builder()
  87 +// .setName("测试")
  88 +// .setBufferSize(/* width= */ 0, /* height= */ 0)
  89 +// .build();
  90 +// videoSurface = new Surface(surfaceControl);
  91 +// player.setVideoSurface(videoSurface);
  92 +// }
  93 +// CacheUtil cacheUtil=new CacheUtil(this);
  94 +// try {
  95 +// cacheUtil.getVideoUri("123456");
  96 +// cacheUtil.add("123456","test");
  97 +// cacheUtil.getVideoUri("123456");
  98 +// } catch (IllegalBlockSizeException e) {
  99 +// e.printStackTrace();
  100 +// } catch (NoSuchPaddingException e) {
  101 +// e.printStackTrace();
  102 +// } catch (BadPaddingException e) {
  103 +// e.printStackTrace();
  104 +// } catch (InvalidKeySpecException e) {
  105 +// e.printStackTrace();
  106 +// } catch (InvalidKeyException e) {
  107 +// e.printStackTrace();
  108 +// }
  109 +//
  110 +// binding.playerView.setOnClickListener(new View.OnClickListener() {
  111 +// @Override
  112 +// public void onClick(View v) {
  113 +//// player.seekTo(player.getCurrentWindowIndex(),5000);
  114 +// }
  115 +// });
  116 + }
  117 +
  118 + private void initVideoPlayer() {
  119 +// Uri dashUri = Uri.parse("rtmp://tyzg.rtmps2.cnlive.com/live-tyzg-577/577_999");
  120 +
  121 +// Uri mp4VideoUri = Uri.parse("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8");
  122 +// videoPlayer = new VideoPlayer(this);
  123 +// videoPlayer.setDataSource(mp4VideoUri);
  124 +// videoPlayer.setPlayView(binding.playerView);
  125 +// videoPlayer.prepare(true);
  126 +// MyTestControlView myTestControlView = new MyTestControlView(context);
  127 +// myTestControlView.setPlayer(videoPlayer.player);
  128 + binding.playerView.setCustomControllView(new MyTestControlView(context));
  129 +// binding.playerView.setOnClickListener(new View.OnClickListener() {
  130 +// @Override
  131 +// public void onClick(View v) {
  132 +// videoPlayer.pause();
  133 +// }
  134 +// });
  135 + }
  136 +
  137 + @Override
  138 + public void onTimelineChanged(Timeline timeline, int reason) {
  139 +
  140 + }
  141 +
  142 + @Override
  143 + public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) {
  144 +
  145 + }
  146 +
  147 + @Override
  148 + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
  149 +
  150 + }
  151 +
  152 + @Override
  153 + public void onLoadingChanged(boolean isLoading) {
  154 +
  155 + }
  156 +
  157 + @Override
  158 + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
  159 + logError(playbackState + "");
  160 + }
  161 +
  162 + @Override
  163 + public void onPlaybackSuppressionReasonChanged(int playbackSuppressionReason) {
  164 +
  165 + }
  166 +
  167 + @Override
  168 + public void onIsPlayingChanged(boolean isPlaying) {
  169 +
  170 + }
  171 +
  172 + @Override
  173 + public void onRepeatModeChanged(int repeatMode) {
  174 +
  175 + }
  176 +
  177 + @Override
  178 + public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
  179 +
  180 + }
  181 +
  182 + @Override
  183 + public void onPlayerError(ExoPlaybackException error) {
  184 +
  185 + }
  186 +
  187 + @Override
  188 + public void onPositionDiscontinuity(int reason) {
  189 +
  190 + }
  191 +
  192 + @Override
  193 + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
  194 +
  195 + }
  196 +
  197 + @Override
  198 + public void onSeekProcessed() {
  199 +
  200 + }
  201 +}
... ...
app/strike/src/main/java/com/cnlive/strike/demo/activity/IVideoPlayer.java 0 → 100644
  1 +package com.cnlive.strike.demo.activity;
  2 +
  3 +import com.google.android.exoplayer2.Timeline;
  4 +
  5 +public interface IVideoPlayer {
  6 + void onTimelineChanged(Timeline timeline, int reason);
  7 +}
... ...
app/strike/src/main/java/com/cnlive/strike/demo/exoplayer/demo/DemoApplication.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2016 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.cnlive.strike.demo.exoplayer.demo;
  17 +
  18 +import android.app.Application;
  19 +
  20 +import com.alibaba.android.arouter.launcher.ARouter;
  21 +import com.baidu.mapapi.CoordType;
  22 +import com.baidu.mapapi.SDKInitializer;
  23 +import com.cnlive.core.libs.base.application.AppConfig;
  24 +import com.cnlive.core.libs.base.application.BaseApplication;
  25 +import com.google.android.exoplayer2.BuildConfig;
  26 +import com.google.android.exoplayer2.DefaultRenderersFactory;
  27 +import com.google.android.exoplayer2.RenderersFactory;
  28 +import com.google.android.exoplayer2.database.DatabaseProvider;
  29 +import com.google.android.exoplayer2.database.ExoDatabaseProvider;
  30 +import com.google.android.exoplayer2.offline.ActionFileUpgradeUtil;
  31 +import com.google.android.exoplayer2.offline.DefaultDownloadIndex;
  32 +import com.google.android.exoplayer2.offline.DefaultDownloaderFactory;
  33 +import com.google.android.exoplayer2.offline.DownloadManager;
  34 +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
  35 +import com.google.android.exoplayer2.upstream.DataSource;
  36 +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
  37 +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
  38 +import com.google.android.exoplayer2.upstream.FileDataSource;
  39 +import com.google.android.exoplayer2.upstream.HttpDataSource;
  40 +import com.google.android.exoplayer2.upstream.cache.Cache;
  41 +import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
  42 +import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
  43 +import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
  44 +import com.google.android.exoplayer2.upstream.cache.SimpleCache;
  45 +import com.google.android.exoplayer2.util.Log;
  46 +import com.google.android.exoplayer2.util.Util;
  47 +import java.io.File;
  48 +import java.io.IOException;
  49 +
  50 +/**
  51 + * Placeholder application to facilitate overriding Application methods for debugging and testing.
  52 + */
  53 +public class DemoApplication extends BaseApplication {
  54 +
  55 + private static final String TAG = "DemoApplication";
  56 + private static final String DOWNLOAD_ACTION_FILE = "actions";
  57 + private static final String DOWNLOAD_TRACKER_ACTION_FILE = "tracked_actions";
  58 + private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
  59 +
  60 + protected String userAgent;
  61 +
  62 + private DatabaseProvider databaseProvider;
  63 + private File downloadDirectory;
  64 + private Cache downloadCache;
  65 + private DownloadManager downloadManager;
  66 + private DownloadTracker downloadTracker;
  67 +
  68 + @Override
  69 + public void onCreate() {
  70 + super.onCreate();
  71 + init();
  72 + userAgent = Util.getUserAgent(this, "ExoPlayerDemo");
  73 + }
  74 +
  75 + private void init() {
  76 + if (com.cnlive.cloud.BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
  77 + ARouter.openLog(); // 打印日志
  78 + ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
  79 + ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈
  80 + }
  81 + ARouter.init(this); // 尽可能早,推荐在Application中初始化
  82 + AppConfig.init(this, com.cnlive.cloud.BuildConfig.APP_ID, com.cnlive.cloud.BuildConfig.APP_KEY,
  83 + com.cnlive.cloud.BuildConfig.APP_SCERET, com.cnlive.cloud.BuildConfig.SPECIAL_GROUP_MEMBER_LIMIT,
  84 + com.cnlive.cloud.BuildConfig.VERSION_AS_DEBUG, com.cnlive.cloud.BuildConfig.BAIDU_MAP_APP_KEY, "",
  85 + "10514333", "",
  86 + "", "", "",
  87 + "", "", "",
  88 + com.cnlive.cloud.BuildConfig.BAIDU_MAP_APP_KEY, "");
  89 +// if (AppConfig.isDebug()) {
  90 +// Constant.SERVICE_ID = 213511;
  91 +// } else {
  92 +// Constant.SERVICE_ID = 214690;
  93 +// }
  94 + //在使用SDK各组件之前初始化context信息,传入ApplicationContext
  95 + SDKInitializer.initialize(getApplicationContext());
  96 + //自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
  97 + //包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
  98 + SDKInitializer.setCoordType(CoordType.GCJ02);
  99 + //初始化上海电商
  100 + cn.cnlive.b2c.AppApplication.initSDK(this);
  101 + }
  102 +
  103 + /** Returns a {@link DataSource.Factory}. */
  104 + public DataSource.Factory buildDataSourceFactory() {
  105 + DefaultDataSourceFactory upstreamFactory =
  106 + new DefaultDataSourceFactory(this, buildHttpDataSourceFactory());
  107 + return buildReadOnlyCacheDataSource(upstreamFactory, getDownloadCache());
  108 + }
  109 +
  110 + /** Returns a {@link HttpDataSource.Factory}. */
  111 + public HttpDataSource.Factory buildHttpDataSourceFactory() {
  112 + return new DefaultHttpDataSourceFactory(userAgent);
  113 + }
  114 +
  115 + /** Returns whether extension renderers should be used. */
  116 + public boolean useExtensionRenderers() {
  117 + return "withExtensions".equals(BuildConfig.FLAVOR);
  118 + }
  119 +
  120 + public RenderersFactory buildRenderersFactory(boolean preferExtensionRenderer) {
  121 + @DefaultRenderersFactory.ExtensionRendererMode
  122 + int extensionRendererMode =
  123 + useExtensionRenderers()
  124 + ? (preferExtensionRenderer
  125 + ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
  126 + : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
  127 + : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF;
  128 + return new DefaultRenderersFactory(/* context= */ this)
  129 + .setExtensionRendererMode(extensionRendererMode);
  130 + }
  131 +
  132 + public DownloadManager getDownloadManager() {
  133 + initDownloadManager();
  134 + return downloadManager;
  135 + }
  136 +
  137 + public DownloadTracker getDownloadTracker() {
  138 + initDownloadManager();
  139 + return downloadTracker;
  140 + }
  141 +
  142 + protected synchronized Cache getDownloadCache() {
  143 + if (downloadCache == null) {
  144 + File downloadContentDirectory = new File(getDownloadDirectory(), DOWNLOAD_CONTENT_DIRECTORY);
  145 + downloadCache =
  146 + new SimpleCache(downloadContentDirectory, new NoOpCacheEvictor(), getDatabaseProvider());
  147 + }
  148 + return downloadCache;
  149 + }
  150 +
  151 + private synchronized void initDownloadManager() {
  152 + if (downloadManager == null) {
  153 + DefaultDownloadIndex downloadIndex = new DefaultDownloadIndex(getDatabaseProvider());
  154 + upgradeActionFile(
  155 + DOWNLOAD_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ false);
  156 + upgradeActionFile(
  157 + DOWNLOAD_TRACKER_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ true);
  158 + DownloaderConstructorHelper downloaderConstructorHelper =
  159 + new DownloaderConstructorHelper(getDownloadCache(), buildHttpDataSourceFactory());
  160 + downloadManager =
  161 + new DownloadManager(
  162 + this, downloadIndex, new DefaultDownloaderFactory(downloaderConstructorHelper));
  163 + downloadTracker =
  164 + new DownloadTracker(/* context= */ this, buildDataSourceFactory(), downloadManager);
  165 + }
  166 + }
  167 +
  168 + private void upgradeActionFile(
  169 + String fileName, DefaultDownloadIndex downloadIndex, boolean addNewDownloadsAsCompleted) {
  170 + try {
  171 + ActionFileUpgradeUtil.upgradeAndDelete(
  172 + new File(getDownloadDirectory(), fileName),
  173 + /* downloadIdProvider= */ null,
  174 + downloadIndex,
  175 + /* deleteOnFailure= */ true,
  176 + addNewDownloadsAsCompleted);
  177 + } catch (IOException e) {
  178 + Log.e(TAG, "Failed to upgrade action file: " + fileName, e);
  179 + }
  180 + }
  181 +
  182 + private DatabaseProvider getDatabaseProvider() {
  183 + if (databaseProvider == null) {
  184 + databaseProvider = new ExoDatabaseProvider(this);
  185 + }
  186 + return databaseProvider;
  187 + }
  188 +
  189 + private File getDownloadDirectory() {
  190 + if (downloadDirectory == null) {
  191 + downloadDirectory = getExternalFilesDir(null);
  192 + if (downloadDirectory == null) {
  193 + downloadDirectory = getFilesDir();
  194 + }
  195 + }
  196 + return downloadDirectory;
  197 + }
  198 +
  199 + protected static CacheDataSourceFactory buildReadOnlyCacheDataSource(
  200 + DataSource.Factory upstreamFactory, Cache cache) {
  201 + return new CacheDataSourceFactory(
  202 + cache,
  203 + upstreamFactory,
  204 + new FileDataSource.Factory(),
  205 + /* cacheWriteDataSinkFactory= */ null,
  206 + CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR,
  207 + /* eventListener= */ null);
  208 + }
  209 +}
... ...
app/strike/src/main/java/com/cnlive/strike/demo/exoplayer/demo/DemoDownloadService.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.cnlive.strike.demo.exoplayer.demo;
  17 +
  18 +import android.app.Notification;
  19 +
  20 +import com.cnlive.cloud.R;
  21 +import com.google.android.exoplayer2.offline.Download;
  22 +import com.google.android.exoplayer2.offline.DownloadManager;
  23 +import com.google.android.exoplayer2.offline.DownloadService;
  24 +import com.google.android.exoplayer2.scheduler.PlatformScheduler;
  25 +import com.google.android.exoplayer2.ui.DownloadNotificationHelper;
  26 +import com.google.android.exoplayer2.util.NotificationUtil;
  27 +import com.google.android.exoplayer2.util.Util;
  28 +import java.util.List;
  29 +
  30 +/** A service for downloading media. */
  31 +public class DemoDownloadService extends DownloadService {
  32 +
  33 + private static final String CHANNEL_ID = "download_channel";
  34 + private static final int JOB_ID = 1;
  35 + private static final int FOREGROUND_NOTIFICATION_ID = 1;
  36 +
  37 + private static int nextNotificationId = FOREGROUND_NOTIFICATION_ID + 1;
  38 +
  39 + private DownloadNotificationHelper notificationHelper;
  40 +
  41 + public DemoDownloadService() {
  42 + super(
  43 + FOREGROUND_NOTIFICATION_ID,
  44 + DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL,
  45 + CHANNEL_ID,
  46 + R.string.exo_download_notification_channel_name,
  47 + /* channelDescriptionResourceId= */ 0);
  48 + nextNotificationId = FOREGROUND_NOTIFICATION_ID + 1;
  49 + }
  50 +
  51 + @Override
  52 + public void onCreate() {
  53 + super.onCreate();
  54 + notificationHelper = new DownloadNotificationHelper(this, CHANNEL_ID);
  55 + }
  56 +
  57 + @Override
  58 + protected DownloadManager getDownloadManager() {
  59 + return ((DemoApplication) getApplication()).getDownloadManager();
  60 + }
  61 +
  62 + @Override
  63 + protected PlatformScheduler getScheduler() {
  64 + return Util.SDK_INT >= 21 ? new PlatformScheduler(this, JOB_ID) : null;
  65 + }
  66 +
  67 + @Override
  68 + protected Notification getForegroundNotification(List<Download> downloads) {
  69 + return notificationHelper.buildProgressNotification(
  70 + R.drawable.ic_download, /* contentIntent= */ null, /* message= */ null, downloads);
  71 + }
  72 +
  73 + @Override
  74 + protected void onDownloadChanged(Download download) {
  75 + Notification notification;
  76 + if (download.state == Download.STATE_COMPLETED) {
  77 + notification =
  78 + notificationHelper.buildDownloadCompletedNotification(
  79 + R.drawable.ic_download_done,
  80 + /* contentIntent= */ null,
  81 + Util.fromUtf8Bytes(download.request.data));
  82 + } else if (download.state == Download.STATE_FAILED) {
  83 + notification =
  84 + notificationHelper.buildDownloadFailedNotification(
  85 + R.drawable.ic_download_done,
  86 + /* contentIntent= */ null,
  87 + Util.fromUtf8Bytes(download.request.data));
  88 + } else {
  89 + return;
  90 + }
  91 + NotificationUtil.setNotification(this, nextNotificationId++, notification);
  92 + }
  93 +}
... ...
app/strike/src/main/java/com/cnlive/strike/demo/exoplayer/demo/DownloadTracker.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2017 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.cnlive.strike.demo.exoplayer.demo;
  17 +
  18 +import android.content.Context;
  19 +import android.content.DialogInterface;
  20 +import android.net.Uri;
  21 +import android.widget.Toast;
  22 +import androidx.annotation.Nullable;
  23 +import androidx.fragment.app.FragmentManager;
  24 +
  25 +import com.cnlive.cloud.R;
  26 +import com.google.android.exoplayer2.C;
  27 +import com.google.android.exoplayer2.RenderersFactory;
  28 +import com.google.android.exoplayer2.offline.Download;
  29 +import com.google.android.exoplayer2.offline.DownloadCursor;
  30 +import com.google.android.exoplayer2.offline.DownloadHelper;
  31 +import com.google.android.exoplayer2.offline.DownloadIndex;
  32 +import com.google.android.exoplayer2.offline.DownloadManager;
  33 +import com.google.android.exoplayer2.offline.DownloadRequest;
  34 +import com.google.android.exoplayer2.offline.DownloadService;
  35 +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
  36 +import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
  37 +import com.google.android.exoplayer2.upstream.DataSource;
  38 +import com.google.android.exoplayer2.util.Log;
  39 +import com.google.android.exoplayer2.util.Util;
  40 +import java.io.IOException;
  41 +import java.util.HashMap;
  42 +import java.util.concurrent.CopyOnWriteArraySet;
  43 +
  44 +/** Tracks media that has been downloaded. */
  45 +public class DownloadTracker {
  46 +
  47 + /** Listens for changes in the tracked downloads. */
  48 + public interface Listener {
  49 +
  50 + /** Called when the tracked downloads changed. */
  51 + void onDownloadsChanged();
  52 + }
  53 +
  54 + private static final String TAG = "DownloadTracker";
  55 +
  56 + private final Context context;
  57 + private final DataSource.Factory dataSourceFactory;
  58 + private final CopyOnWriteArraySet<Listener> listeners;
  59 + private final HashMap<Uri, Download> downloads;
  60 + private final DownloadIndex downloadIndex;
  61 + private final DefaultTrackSelector.Parameters trackSelectorParameters;
  62 +
  63 + @Nullable private StartDownloadDialogHelper startDownloadDialogHelper;
  64 +
  65 + public DownloadTracker(
  66 + Context context, DataSource.Factory dataSourceFactory, DownloadManager downloadManager) {
  67 + this.context = context.getApplicationContext();
  68 + this.dataSourceFactory = dataSourceFactory;
  69 + listeners = new CopyOnWriteArraySet<>();
  70 + downloads = new HashMap<>();
  71 + downloadIndex = downloadManager.getDownloadIndex();
  72 + trackSelectorParameters = DownloadHelper.getDefaultTrackSelectorParameters(context);
  73 + downloadManager.addListener(new DownloadManagerListener());
  74 + loadDownloads();
  75 + }
  76 +
  77 + public void addListener(Listener listener) {
  78 + listeners.add(listener);
  79 + }
  80 +
  81 + public void removeListener(Listener listener) {
  82 + listeners.remove(listener);
  83 + }
  84 +
  85 + public boolean isDownloaded(Uri uri) {
  86 + Download download = downloads.get(uri);
  87 + return download != null && download.state != Download.STATE_FAILED;
  88 + }
  89 +
  90 + public DownloadRequest getDownloadRequest(Uri uri) {
  91 + Download download = downloads.get(uri);
  92 + return download != null && download.state != Download.STATE_FAILED ? download.request : null;
  93 + }
  94 +
  95 + public void toggleDownload(
  96 + FragmentManager fragmentManager,
  97 + String name,
  98 + Uri uri,
  99 + String extension,
  100 + RenderersFactory renderersFactory) {
  101 + Download download = downloads.get(uri);
  102 + if (download != null) {
  103 + DownloadService.sendRemoveDownload(
  104 + context, DemoDownloadService.class, download.request.id, /* foreground= */ false);
  105 + } else {
  106 + if (startDownloadDialogHelper != null) {
  107 + startDownloadDialogHelper.release();
  108 + }
  109 + startDownloadDialogHelper =
  110 + new StartDownloadDialogHelper(
  111 + fragmentManager, getDownloadHelper(uri, extension, renderersFactory), name);
  112 + }
  113 + }
  114 +
  115 + private void loadDownloads() {
  116 + try (DownloadCursor loadedDownloads = downloadIndex.getDownloads()) {
  117 + while (loadedDownloads.moveToNext()) {
  118 + Download download = loadedDownloads.getDownload();
  119 + downloads.put(download.request.uri, download);
  120 + }
  121 + } catch (IOException e) {
  122 + Log.w(TAG, "Failed to query downloads", e);
  123 + }
  124 + }
  125 +
  126 + private DownloadHelper getDownloadHelper(
  127 + Uri uri, String extension, RenderersFactory renderersFactory) {
  128 + int type = Util.inferContentType(uri, extension);
  129 + switch (type) {
  130 + case C.TYPE_DASH:
  131 + return DownloadHelper.forDash(context, uri, dataSourceFactory, renderersFactory);
  132 + case C.TYPE_SS:
  133 + return DownloadHelper.forSmoothStreaming(context, uri, dataSourceFactory, renderersFactory);
  134 + case C.TYPE_HLS:
  135 + return DownloadHelper.forHls(context, uri, dataSourceFactory, renderersFactory);
  136 + case C.TYPE_OTHER:
  137 + return DownloadHelper.forProgressive(context, uri);
  138 + default:
  139 + throw new IllegalStateException("Unsupported type: " + type);
  140 + }
  141 + }
  142 +
  143 + private class DownloadManagerListener implements DownloadManager.Listener {
  144 +
  145 + @Override
  146 + public void onDownloadChanged(DownloadManager downloadManager, Download download) {
  147 + downloads.put(download.request.uri, download);
  148 + for (Listener listener : listeners) {
  149 + listener.onDownloadsChanged();
  150 + }
  151 + }
  152 +
  153 + @Override
  154 + public void onDownloadRemoved(DownloadManager downloadManager, Download download) {
  155 + downloads.remove(download.request.uri);
  156 + for (Listener listener : listeners) {
  157 + listener.onDownloadsChanged();
  158 + }
  159 + }
  160 + }
  161 +
  162 + private final class StartDownloadDialogHelper
  163 + implements DownloadHelper.Callback,
  164 + DialogInterface.OnClickListener,
  165 + DialogInterface.OnDismissListener {
  166 +
  167 + private final FragmentManager fragmentManager;
  168 + private final DownloadHelper downloadHelper;
  169 + private final String name;
  170 +
  171 + private TrackSelectionDialog trackSelectionDialog;
  172 + private MappedTrackInfo mappedTrackInfo;
  173 +
  174 + public StartDownloadDialogHelper(
  175 + FragmentManager fragmentManager, DownloadHelper downloadHelper, String name) {
  176 + this.fragmentManager = fragmentManager;
  177 + this.downloadHelper = downloadHelper;
  178 + this.name = name;
  179 + downloadHelper.prepare(this);
  180 + }
  181 +
  182 + public void release() {
  183 + downloadHelper.release();
  184 + if (trackSelectionDialog != null) {
  185 + trackSelectionDialog.dismiss();
  186 + }
  187 + }
  188 +
  189 + // DownloadHelper.Callback implementation.
  190 +
  191 + @Override
  192 + public void onPrepared(DownloadHelper helper) {
  193 + if (helper.getPeriodCount() == 0) {
  194 + Log.d(TAG, "No periods found. Downloading entire stream.");
  195 + startDownload();
  196 + downloadHelper.release();
  197 + return;
  198 + }
  199 + mappedTrackInfo = downloadHelper.getMappedTrackInfo(/* periodIndex= */ 0);
  200 + if (!TrackSelectionDialog.willHaveContent(mappedTrackInfo)) {
  201 + Log.d(TAG, "No dialog content. Downloading entire stream.");
  202 + startDownload();
  203 + downloadHelper.release();
  204 + return;
  205 + }
  206 + trackSelectionDialog =
  207 + TrackSelectionDialog.createForMappedTrackInfoAndParameters(
  208 + /* titleId= */ R.string.exo_download_description,
  209 + mappedTrackInfo,
  210 + trackSelectorParameters,
  211 + /* allowAdaptiveSelections =*/ false,
  212 + /* allowMultipleOverrides= */ true,
  213 + /* onClickListener= */ this,
  214 + /* onDismissListener= */ this);
  215 + trackSelectionDialog.show(fragmentManager, /* tag= */ null);
  216 + }
  217 +
  218 + @Override
  219 + public void onPrepareError(DownloadHelper helper, IOException e) {
  220 + Toast.makeText(context, R.string.download_start_error, Toast.LENGTH_LONG).show();
  221 + Log.e(
  222 + TAG,
  223 + e instanceof DownloadHelper.LiveContentUnsupportedException
  224 + ? "Downloading live content unsupported"
  225 + : "Failed to start download",
  226 + e);
  227 + }
  228 +
  229 + // DialogInterface.OnClickListener implementation.
  230 +
  231 + @Override
  232 + public void onClick(DialogInterface dialog, int which) {
  233 + for (int periodIndex = 0; periodIndex < downloadHelper.getPeriodCount(); periodIndex++) {
  234 + downloadHelper.clearTrackSelections(periodIndex);
  235 + for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
  236 + if (!trackSelectionDialog.getIsDisabled(/* rendererIndex= */ i)) {
  237 + downloadHelper.addTrackSelectionForSingleRenderer(
  238 + periodIndex,
  239 + /* rendererIndex= */ i,
  240 + trackSelectorParameters,
  241 + trackSelectionDialog.getOverrides(/* rendererIndex= */ i));
  242 + }
  243 + }
  244 + }
  245 + DownloadRequest downloadRequest = buildDownloadRequest();
  246 + if (downloadRequest.streamKeys.isEmpty()) {
  247 + // All tracks were deselected in the dialog. Don't start the download.
  248 + return;
  249 + }
  250 + startDownload(downloadRequest);
  251 + }
  252 +
  253 + // DialogInterface.OnDismissListener implementation.
  254 +
  255 + @Override
  256 + public void onDismiss(DialogInterface dialogInterface) {
  257 + trackSelectionDialog = null;
  258 + downloadHelper.release();
  259 + }
  260 +
  261 + // Internal methods.
  262 +
  263 + private void startDownload() {
  264 + startDownload(buildDownloadRequest());
  265 + }
  266 +
  267 + private void startDownload(DownloadRequest downloadRequest) {
  268 + DownloadService.sendAddDownload(
  269 + context, DemoDownloadService.class, downloadRequest, /* foreground= */ false);
  270 + }
  271 +
  272 + private DownloadRequest buildDownloadRequest() {
  273 + return downloadHelper.getDownloadRequest(Util.getUtf8Bytes(name));
  274 + }
  275 + }
  276 +}
... ...
app/strike/src/main/java/com/cnlive/strike/demo/exoplayer/demo/PlayerActivity.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2016 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.cnlive.strike.demo.exoplayer.demo;
  17 +
  18 +import android.content.Intent;
  19 +import android.content.pm.PackageManager;
  20 +import android.media.MediaDrm;
  21 +import android.net.Uri;
  22 +import android.os.Bundle;
  23 +import android.util.Pair;
  24 +import android.view.KeyEvent;
  25 +import android.view.View;
  26 +import android.view.View.OnClickListener;
  27 +import android.widget.Button;
  28 +import android.widget.LinearLayout;
  29 +import android.widget.TextView;
  30 +import android.widget.Toast;
  31 +import androidx.annotation.NonNull;
  32 +import androidx.annotation.Nullable;
  33 +import androidx.appcompat.app.AppCompatActivity;
  34 +
  35 +import com.cnlive.cloud.R;
  36 +import com.google.android.exoplayer2.C;
  37 +import com.google.android.exoplayer2.C.ContentType;
  38 +import com.google.android.exoplayer2.ExoPlaybackException;
  39 +import com.google.android.exoplayer2.Format;
  40 +import com.google.android.exoplayer2.PlaybackPreparer;
  41 +import com.google.android.exoplayer2.Player;
  42 +import com.google.android.exoplayer2.RenderersFactory;
  43 +import com.google.android.exoplayer2.SimpleExoPlayer;
  44 +import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
  45 +import com.google.android.exoplayer2.drm.DrmSessionManager;
  46 +import com.google.android.exoplayer2.drm.ExoMediaCrypto;
  47 +import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
  48 +import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
  49 +import com.google.android.exoplayer2.drm.MediaDrmCallback;
  50 +import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.DecoderInitializationException;
  51 +import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
  52 +import com.google.android.exoplayer2.offline.DownloadHelper;
  53 +import com.google.android.exoplayer2.offline.DownloadRequest;
  54 +import com.google.android.exoplayer2.source.BehindLiveWindowException;
  55 +import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
  56 +import com.google.android.exoplayer2.source.MediaSource;
  57 +import com.google.android.exoplayer2.source.MediaSourceFactory;
  58 +import com.google.android.exoplayer2.source.MergingMediaSource;
  59 +import com.google.android.exoplayer2.source.ProgressiveMediaSource;
  60 +import com.google.android.exoplayer2.source.SingleSampleMediaSource;
  61 +import com.google.android.exoplayer2.source.TrackGroupArray;
  62 +import com.google.android.exoplayer2.source.ads.AdsLoader;
  63 +import com.google.android.exoplayer2.source.ads.AdsMediaSource;
  64 +import com.google.android.exoplayer2.source.dash.DashMediaSource;
  65 +import com.google.android.exoplayer2.source.hls.HlsMediaSource;
  66 +import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
  67 +import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
  68 +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
  69 +import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
  70 +import com.google.android.exoplayer2.trackselection.RandomTrackSelection;
  71 +import com.google.android.exoplayer2.trackselection.TrackSelection;
  72 +import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
  73 +import com.google.android.exoplayer2.ui.DebugTextViewHelper;
  74 +import com.google.android.exoplayer2.ui.PlayerControlView;
  75 +import com.google.android.exoplayer2.ui.PlayerView;
  76 +import com.google.android.exoplayer2.ui.spherical.SphericalGLSurfaceView;
  77 +import com.google.android.exoplayer2.upstream.DataSource;
  78 +import com.google.android.exoplayer2.upstream.HttpDataSource;
  79 +import com.google.android.exoplayer2.util.ErrorMessageProvider;
  80 +import com.google.android.exoplayer2.util.EventLogger;
  81 +import com.google.android.exoplayer2.util.Util;
  82 +import java.lang.reflect.Constructor;
  83 +import java.net.CookieHandler;
  84 +import java.net.CookieManager;
  85 +import java.net.CookiePolicy;
  86 +
  87 +/** An activity that plays media using {@link SimpleExoPlayer}. */
  88 +public class PlayerActivity extends AppCompatActivity
  89 + implements OnClickListener, PlaybackPreparer, PlayerControlView.VisibilityListener {
  90 +
  91 + // Activity extras.
  92 +
  93 + public static final String SPHERICAL_STEREO_MODE_EXTRA = "spherical_stereo_mode";
  94 + public static final String SPHERICAL_STEREO_MODE_MONO = "mono";
  95 + public static final String SPHERICAL_STEREO_MODE_TOP_BOTTOM = "top_bottom";
  96 + public static final String SPHERICAL_STEREO_MODE_LEFT_RIGHT = "left_right";
  97 +
  98 + // Actions.
  99 +
  100 + public static final String ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW";
  101 + public static final String ACTION_VIEW_LIST =
  102 + "com.google.android.exoplayer.demo.action.VIEW_LIST";
  103 +
  104 + // Player configuration extras.
  105 +
  106 + public static final String ABR_ALGORITHM_EXTRA = "abr_algorithm";
  107 + public static final String ABR_ALGORITHM_DEFAULT = "default";
  108 + public static final String ABR_ALGORITHM_RANDOM = "random";
  109 +
  110 + // Media item configuration extras.
  111 +
  112 + public static final String URI_EXTRA = "uri";
  113 + public static final String EXTENSION_EXTRA = "extension";
  114 + public static final String IS_LIVE_EXTRA = "is_live";
  115 +
  116 + public static final String DRM_SCHEME_EXTRA = "drm_scheme";
  117 + public static final String DRM_LICENSE_URL_EXTRA = "drm_license_url";
  118 + public static final String DRM_KEY_REQUEST_PROPERTIES_EXTRA = "drm_key_request_properties";
  119 + public static final String DRM_MULTI_SESSION_EXTRA = "drm_multi_session";
  120 + public static final String PREFER_EXTENSION_DECODERS_EXTRA = "prefer_extension_decoders";
  121 + public static final String TUNNELING_EXTRA = "tunneling";
  122 + public static final String AD_TAG_URI_EXTRA = "ad_tag_uri";
  123 + public static final String SUBTITLE_URI_EXTRA = "subtitle_uri";
  124 + public static final String SUBTITLE_MIME_TYPE_EXTRA = "subtitle_mime_type";
  125 + public static final String SUBTITLE_LANGUAGE_EXTRA = "subtitle_language";
  126 + // For backwards compatibility only.
  127 + public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid";
  128 +
  129 + // Saved instance state keys.
  130 +
  131 + private static final String KEY_TRACK_SELECTOR_PARAMETERS = "track_selector_parameters";
  132 + private static final String KEY_WINDOW = "window";
  133 + private static final String KEY_POSITION = "position";
  134 + private static final String KEY_AUTO_PLAY = "auto_play";
  135 +
  136 + private static final CookieManager DEFAULT_COOKIE_MANAGER;
  137 + static {
  138 + DEFAULT_COOKIE_MANAGER = new CookieManager();
  139 + DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
  140 + }
  141 +
  142 + private PlayerView playerView;
  143 + private LinearLayout debugRootView;
  144 + private Button selectTracksButton;
  145 + private TextView debugTextView;
  146 + private boolean isShowingTrackSelectionDialog;
  147 +
  148 + private DataSource.Factory dataSourceFactory;
  149 + private SimpleExoPlayer player;
  150 + private MediaSource mediaSource;
  151 + private DefaultTrackSelector trackSelector;
  152 + private DefaultTrackSelector.Parameters trackSelectorParameters;
  153 + private DebugTextViewHelper debugViewHelper;
  154 + private TrackGroupArray lastSeenTrackGroupArray;
  155 +
  156 + private boolean startAutoPlay;
  157 + private int startWindow;
  158 + private long startPosition;
  159 +
  160 + // Fields used only for ad playback. The ads loader is loaded via reflection.
  161 +
  162 + private AdsLoader adsLoader;
  163 + private Uri loadedAdTagUri;
  164 +
  165 + // Activity lifecycle
  166 +
  167 + @Override
  168 + public void onCreate(Bundle savedInstanceState) {
  169 + Intent intent = getIntent();
  170 + String sphericalStereoMode = intent.getStringExtra(SPHERICAL_STEREO_MODE_EXTRA);
  171 + if (sphericalStereoMode != null) {
  172 + setTheme(R.style.PlayerTheme_Spherical);
  173 + }
  174 + super.onCreate(savedInstanceState);
  175 + dataSourceFactory = buildDataSourceFactory();
  176 + if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
  177 + CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
  178 + }
  179 +
  180 + setContentView(R.layout.player_activity);
  181 + debugRootView = findViewById(R.id.controls_root);
  182 + debugTextView = findViewById(R.id.debug_text_view);
  183 + selectTracksButton = findViewById(R.id.select_tracks_button);
  184 + selectTracksButton.setOnClickListener(this);
  185 +
  186 + playerView = findViewById(R.id.player_view);
  187 + playerView.setControllerVisibilityListener(this);
  188 + playerView.setErrorMessageProvider(new PlayerErrorMessageProvider());
  189 + playerView.requestFocus();
  190 + if (sphericalStereoMode != null) {
  191 + int stereoMode;
  192 + if (SPHERICAL_STEREO_MODE_MONO.equals(sphericalStereoMode)) {
  193 + stereoMode = C.STEREO_MODE_MONO;
  194 + } else if (SPHERICAL_STEREO_MODE_TOP_BOTTOM.equals(sphericalStereoMode)) {
  195 + stereoMode = C.STEREO_MODE_TOP_BOTTOM;
  196 + } else if (SPHERICAL_STEREO_MODE_LEFT_RIGHT.equals(sphericalStereoMode)) {
  197 + stereoMode = C.STEREO_MODE_LEFT_RIGHT;
  198 + } else {
  199 + showToast(R.string.error_unrecognized_stereo_mode);
  200 + finish();
  201 + return;
  202 + }
  203 + ((SphericalGLSurfaceView) playerView.getVideoSurfaceView()).setDefaultStereoMode(stereoMode);
  204 + }
  205 +
  206 + if (savedInstanceState != null) {
  207 + trackSelectorParameters = savedInstanceState.getParcelable(KEY_TRACK_SELECTOR_PARAMETERS);
  208 + startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY);
  209 + startWindow = savedInstanceState.getInt(KEY_WINDOW);
  210 + startPosition = savedInstanceState.getLong(KEY_POSITION);
  211 + } else {
  212 + DefaultTrackSelector.ParametersBuilder builder =
  213 + new DefaultTrackSelector.ParametersBuilder(/* context= */ this);
  214 + boolean tunneling = intent.getBooleanExtra(TUNNELING_EXTRA, false);
  215 + if (Util.SDK_INT >= 21 && tunneling) {
  216 + builder.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(/* context= */ this));
  217 + }
  218 + trackSelectorParameters = builder.build();
  219 + clearStartPosition();
  220 + }
  221 + }
  222 +
  223 + @Override
  224 + public void onNewIntent(Intent intent) {
  225 + super.onNewIntent(intent);
  226 + releasePlayer();
  227 + releaseAdsLoader();
  228 + clearStartPosition();
  229 + setIntent(intent);
  230 + }
  231 +
  232 + @Override
  233 + public void onStart() {
  234 + super.onStart();
  235 + if (Util.SDK_INT > 23) {
  236 + initializePlayer();
  237 + if (playerView != null) {
  238 + playerView.onResume();
  239 + }
  240 + }
  241 + }
  242 +
  243 + @Override
  244 + public void onResume() {
  245 + super.onResume();
  246 + if (Util.SDK_INT <= 23 || player == null) {
  247 + initializePlayer();
  248 + if (playerView != null) {
  249 + playerView.onResume();
  250 + }
  251 + }
  252 + }
  253 +
  254 + @Override
  255 + public void onPause() {
  256 + super.onPause();
  257 + if (Util.SDK_INT <= 23) {
  258 + if (playerView != null) {
  259 + playerView.onPause();
  260 + }
  261 + releasePlayer();
  262 + }
  263 + }
  264 +
  265 + @Override
  266 + public void onStop() {
  267 + super.onStop();
  268 + if (Util.SDK_INT > 23) {
  269 + if (playerView != null) {
  270 + playerView.onPause();
  271 + }
  272 + releasePlayer();
  273 + }
  274 + }
  275 +
  276 + @Override
  277 + public void onDestroy() {
  278 + super.onDestroy();
  279 + releaseAdsLoader();
  280 + }
  281 +
  282 + @Override
  283 + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
  284 + @NonNull int[] grantResults) {
  285 + if (grantResults.length == 0) {
  286 + // Empty results are triggered if a permission is requested while another request was already
  287 + // pending and can be safely ignored in this case.
  288 + return;
  289 + }
  290 + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  291 + initializePlayer();
  292 + } else {
  293 + showToast(R.string.storage_permission_denied);
  294 + finish();
  295 + }
  296 + }
  297 +
  298 + @Override
  299 + public void onSaveInstanceState(Bundle outState) {
  300 + super.onSaveInstanceState(outState);
  301 + updateTrackSelectorParameters();
  302 + updateStartPosition();
  303 + outState.putParcelable(KEY_TRACK_SELECTOR_PARAMETERS, trackSelectorParameters);
  304 + outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay);
  305 + outState.putInt(KEY_WINDOW, startWindow);
  306 + outState.putLong(KEY_POSITION, startPosition);
  307 + }
  308 +
  309 + // Activity input
  310 +
  311 + @Override
  312 + public boolean dispatchKeyEvent(KeyEvent event) {
  313 + // See whether the player view wants to handle media or DPAD keys events.
  314 + return playerView.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
  315 + }
  316 +
  317 + // OnClickListener methods
  318 +
  319 + @Override
  320 + public void onClick(View view) {
  321 + if (view == selectTracksButton
  322 + && !isShowingTrackSelectionDialog
  323 + && TrackSelectionDialog.willHaveContent(trackSelector)) {
  324 + isShowingTrackSelectionDialog = true;
  325 + TrackSelectionDialog trackSelectionDialog =
  326 + TrackSelectionDialog.createForTrackSelector(
  327 + trackSelector,
  328 + /* onDismissListener= */ dismissedDialog -> isShowingTrackSelectionDialog = false);
  329 + trackSelectionDialog.show(getSupportFragmentManager(), /* tag= */ null);
  330 + }
  331 + }
  332 +
  333 + // PlaybackControlView.PlaybackPreparer implementation
  334 +
  335 + @Override
  336 + public void preparePlayback() {
  337 + player.retry();
  338 + }
  339 +
  340 + // PlaybackControlView.VisibilityListener implementation
  341 +
  342 + @Override
  343 + public void onVisibilityChange(int visibility) {
  344 + debugRootView.setVisibility(visibility);
  345 + }
  346 +
  347 + // Internal methods
  348 +
  349 + private void initializePlayer() {
  350 + if (player == null) {
  351 + Intent intent = getIntent();
  352 +
  353 + mediaSource = createTopLevelMediaSource(intent);
  354 + if (mediaSource == null) {
  355 + return;
  356 + }
  357 +
  358 + TrackSelection.Factory trackSelectionFactory;
  359 + String abrAlgorithm = intent.getStringExtra(ABR_ALGORITHM_EXTRA);
  360 + if (abrAlgorithm == null || ABR_ALGORITHM_DEFAULT.equals(abrAlgorithm)) {
  361 + trackSelectionFactory = new AdaptiveTrackSelection.Factory();
  362 + } else if (ABR_ALGORITHM_RANDOM.equals(abrAlgorithm)) {
  363 + trackSelectionFactory = new RandomTrackSelection.Factory();
  364 + } else {
  365 + showToast(R.string.error_unrecognized_abr_algorithm);
  366 + finish();
  367 + return;
  368 + }
  369 +
  370 + boolean preferExtensionDecoders =
  371 + intent.getBooleanExtra(PREFER_EXTENSION_DECODERS_EXTRA, false);
  372 + RenderersFactory renderersFactory =
  373 + ((DemoApplication) getApplication()).buildRenderersFactory(preferExtensionDecoders);
  374 +
  375 + trackSelector = new DefaultTrackSelector(/* context= */ this, trackSelectionFactory);
  376 + trackSelector.setParameters(trackSelectorParameters);
  377 + lastSeenTrackGroupArray = null;
  378 +
  379 + player =
  380 + new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
  381 + .setTrackSelector(trackSelector)
  382 + .build();
  383 + player.addListener(new PlayerEventListener());
  384 + player.setPlayWhenReady(startAutoPlay);
  385 + player.addAnalyticsListener(new EventLogger(trackSelector));
  386 + playerView.setPlayer(player);
  387 + playerView.setPlaybackPreparer(this);
  388 + debugViewHelper = new DebugTextViewHelper(player, debugTextView);
  389 + debugViewHelper.start();
  390 + if (adsLoader != null) {
  391 + adsLoader.setPlayer(player);
  392 + }
  393 + }
  394 + boolean haveStartPosition = startWindow != C.INDEX_UNSET;
  395 + if (haveStartPosition) {
  396 + player.seekTo(startWindow, startPosition);
  397 + }
  398 + player.prepare(mediaSource, !haveStartPosition, false);
  399 + updateButtonVisibility();
  400 + }
  401 +
  402 + @Nullable
  403 + private MediaSource createTopLevelMediaSource(Intent intent) {
  404 + String action = intent.getAction();
  405 + boolean actionIsListView = ACTION_VIEW_LIST.equals(action);
  406 + if (!actionIsListView && !ACTION_VIEW.equals(action)) {
  407 + showToast(getString(R.string.unexpected_intent_action, action));
  408 + finish();
  409 + return null;
  410 + }
  411 +
  412 + Sample intentAsSample = Sample.createFromIntent(intent);
  413 + Sample.UriSample[] samples =
  414 + intentAsSample instanceof Sample.PlaylistSample
  415 + ? ((Sample.PlaylistSample) intentAsSample).children
  416 + : new Sample.UriSample[] {(Sample.UriSample) intentAsSample};
  417 +
  418 + boolean seenAdsTagUri = false;
  419 + for (Sample.UriSample sample : samples) {
  420 + seenAdsTagUri |= sample.adTagUri != null;
  421 + if (!Util.checkCleartextTrafficPermitted(sample.uri)) {
  422 + showToast(R.string.error_cleartext_not_permitted);
  423 + return null;
  424 + }
  425 + if (Util.maybeRequestReadExternalStoragePermission(/* activity= */ this, sample.uri)) {
  426 + // The player will be reinitialized if the permission is granted.
  427 + return null;
  428 + }
  429 + }
  430 +
  431 + MediaSource[] mediaSources = new MediaSource[samples.length];
  432 + for (int i = 0; i < samples.length; i++) {
  433 + mediaSources[i] = createLeafMediaSource(samples[i]);
  434 + Sample.SubtitleInfo subtitleInfo = samples[i].subtitleInfo;
  435 + if (subtitleInfo != null) {
  436 + Format subtitleFormat =
  437 + Format.createTextSampleFormat(
  438 + /* id= */ null,
  439 + subtitleInfo.mimeType,
  440 + C.SELECTION_FLAG_DEFAULT,
  441 + subtitleInfo.language);
  442 + MediaSource subtitleMediaSource =
  443 + new SingleSampleMediaSource.Factory(dataSourceFactory)
  444 + .createMediaSource(subtitleInfo.uri, subtitleFormat, C.TIME_UNSET);
  445 + mediaSources[i] = new MergingMediaSource(mediaSources[i], subtitleMediaSource);
  446 + }
  447 + }
  448 + MediaSource mediaSource =
  449 + mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources);
  450 +
  451 + if (seenAdsTagUri) {
  452 + Uri adTagUri = samples[0].adTagUri;
  453 + if (actionIsListView) {
  454 + showToast(R.string.unsupported_ads_in_concatenation);
  455 + } else {
  456 + if (!adTagUri.equals(loadedAdTagUri)) {
  457 + releaseAdsLoader();
  458 + loadedAdTagUri = adTagUri;
  459 + }
  460 + MediaSource adsMediaSource = createAdsMediaSource(mediaSource, adTagUri);
  461 + if (adsMediaSource != null) {
  462 + mediaSource = adsMediaSource;
  463 + } else {
  464 + showToast(R.string.ima_not_loaded);
  465 + }
  466 + }
  467 + } else {
  468 + releaseAdsLoader();
  469 + }
  470 +
  471 + return mediaSource;
  472 + }
  473 +
  474 + private MediaSource createLeafMediaSource(Sample.UriSample parameters) {
  475 + Sample.DrmInfo drmInfo = parameters.drmInfo;
  476 + int errorStringId = R.string.error_drm_unknown;
  477 + DrmSessionManager<ExoMediaCrypto> drmSessionManager = null;
  478 + if (drmInfo == null) {
  479 + drmSessionManager = DrmSessionManager.getDummyDrmSessionManager();
  480 + } else if (Util.SDK_INT < 18) {
  481 + errorStringId = R.string.error_drm_unsupported_before_api_18;
  482 + } else if (!MediaDrm.isCryptoSchemeSupported(drmInfo.drmScheme)) {
  483 + errorStringId = R.string.error_drm_unsupported_scheme;
  484 + } else {
  485 + MediaDrmCallback mediaDrmCallback =
  486 + createMediaDrmCallback(drmInfo.drmLicenseUrl, drmInfo.drmKeyRequestProperties);
  487 + drmSessionManager =
  488 + new DefaultDrmSessionManager.Builder()
  489 + .setUuidAndExoMediaDrmProvider(drmInfo.drmScheme, FrameworkMediaDrm.DEFAULT_PROVIDER)
  490 + .setMultiSession(drmInfo.drmMultiSession)
  491 + .build(mediaDrmCallback);
  492 + }
  493 +
  494 + if (drmSessionManager == null) {
  495 + showToast(errorStringId);
  496 + finish();
  497 + return null;
  498 + }
  499 +
  500 + DownloadRequest downloadRequest =
  501 + ((DemoApplication) getApplication())
  502 + .getDownloadTracker()
  503 + .getDownloadRequest(parameters.uri);
  504 + if (downloadRequest != null) {
  505 + return DownloadHelper.createMediaSource(downloadRequest, dataSourceFactory);
  506 + }
  507 + return createLeafMediaSource(parameters.uri, parameters.extension, drmSessionManager);
  508 + }
  509 +
  510 + private MediaSource createLeafMediaSource(
  511 + Uri uri, String extension, DrmSessionManager<ExoMediaCrypto> drmSessionManager) {
  512 + @ContentType int type = Util.inferContentType(uri, extension);
  513 + switch (type) {
  514 + case C.TYPE_DASH:
  515 + return new DashMediaSource.Factory(dataSourceFactory)
  516 + .setDrmSessionManager(drmSessionManager)
  517 + .createMediaSource(uri);
  518 + case C.TYPE_SS:
  519 + return new SsMediaSource.Factory(dataSourceFactory)
  520 + .setDrmSessionManager(drmSessionManager)
  521 + .createMediaSource(uri);
  522 + case C.TYPE_HLS:
  523 + return new HlsMediaSource.Factory(dataSourceFactory)
  524 + .setDrmSessionManager(drmSessionManager)
  525 + .createMediaSource(uri);
  526 + case C.TYPE_OTHER:
  527 + return new ProgressiveMediaSource.Factory(dataSourceFactory)
  528 + .setDrmSessionManager(drmSessionManager)
  529 + .createMediaSource(uri);
  530 + default:
  531 + throw new IllegalStateException("Unsupported type: " + type);
  532 + }
  533 + }
  534 +
  535 + private HttpMediaDrmCallback createMediaDrmCallback(
  536 + String licenseUrl, String[] keyRequestPropertiesArray) {
  537 + HttpDataSource.Factory licenseDataSourceFactory =
  538 + ((DemoApplication) getApplication()).buildHttpDataSourceFactory();
  539 + HttpMediaDrmCallback drmCallback =
  540 + new HttpMediaDrmCallback(licenseUrl, licenseDataSourceFactory);
  541 + if (keyRequestPropertiesArray != null) {
  542 + for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) {
  543 + drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i],
  544 + keyRequestPropertiesArray[i + 1]);
  545 + }
  546 + }
  547 + return drmCallback;
  548 + }
  549 +
  550 + private void releasePlayer() {
  551 + if (player != null) {
  552 + updateTrackSelectorParameters();
  553 + updateStartPosition();
  554 + debugViewHelper.stop();
  555 + debugViewHelper = null;
  556 + player.release();
  557 + player = null;
  558 + mediaSource = null;
  559 + trackSelector = null;
  560 + }
  561 + if (adsLoader != null) {
  562 + adsLoader.setPlayer(null);
  563 + }
  564 + }
  565 +
  566 + private void releaseAdsLoader() {
  567 + if (adsLoader != null) {
  568 + adsLoader.release();
  569 + adsLoader = null;
  570 + loadedAdTagUri = null;
  571 + playerView.getOverlayFrameLayout().removeAllViews();
  572 + }
  573 + }
  574 +
  575 + private void updateTrackSelectorParameters() {
  576 + if (trackSelector != null) {
  577 + trackSelectorParameters = trackSelector.getParameters();
  578 + }
  579 + }
  580 +
  581 + private void updateStartPosition() {
  582 + if (player != null) {
  583 + startAutoPlay = player.getPlayWhenReady();
  584 + startWindow = player.getCurrentWindowIndex();
  585 + startPosition = Math.max(0, player.getContentPosition());
  586 + }
  587 + }
  588 +
  589 + private void clearStartPosition() {
  590 + startAutoPlay = true;
  591 + startWindow = C.INDEX_UNSET;
  592 + startPosition = C.TIME_UNSET;
  593 + }
  594 +
  595 + /** Returns a new DataSource factory. */
  596 + private DataSource.Factory buildDataSourceFactory() {
  597 + return ((DemoApplication) getApplication()).buildDataSourceFactory();
  598 + }
  599 +
  600 + /** Returns an ads media source, reusing the ads loader if one exists. */
  601 + @Nullable
  602 + private MediaSource createAdsMediaSource(MediaSource mediaSource, Uri adTagUri) {
  603 + // Load the extension source using reflection so the demo app doesn't have to depend on it.
  604 + // The ads loader is reused for multiple playbacks, so that ad playback can resume.
  605 + try {
  606 + Class<?> loaderClass = Class.forName("com.google.android.exoplayer2.ext.ima.ImaAdsLoader");
  607 + if (adsLoader == null) {
  608 + // Full class names used so the LINT.IfChange rule triggers should any of the classes move.
  609 + // LINT.IfChange
  610 + Constructor<? extends AdsLoader> loaderConstructor =
  611 + loaderClass
  612 + .asSubclass(AdsLoader.class)
  613 + .getConstructor(android.content.Context.class, Uri.class);
  614 + // LINT.ThenChange(../../../../../../../../proguard-rules.txt)
  615 + adsLoader = loaderConstructor.newInstance(this, adTagUri);
  616 + }
  617 + MediaSourceFactory adMediaSourceFactory =
  618 + new MediaSourceFactory() {
  619 + @Override
  620 + public MediaSource createMediaSource(Uri uri) {
  621 + return PlayerActivity.this.createLeafMediaSource(
  622 + uri, /* extension=*/ null, DrmSessionManager.getDummyDrmSessionManager());
  623 + }
  624 +
  625 + @Override
  626 + public int[] getSupportedTypes() {
  627 + return new int[] {C.TYPE_DASH, C.TYPE_SS, C.TYPE_HLS, C.TYPE_OTHER};
  628 + }
  629 + };
  630 + return new AdsMediaSource(mediaSource, adMediaSourceFactory, adsLoader, playerView);
  631 + } catch (ClassNotFoundException e) {
  632 + // IMA extension not loaded.
  633 + return null;
  634 + } catch (Exception e) {
  635 + throw new RuntimeException(e);
  636 + }
  637 + }
  638 +
  639 + // User controls
  640 +
  641 + private void updateButtonVisibility() {
  642 + selectTracksButton.setEnabled(
  643 + player != null && TrackSelectionDialog.willHaveContent(trackSelector));
  644 + }
  645 +
  646 + private void showControls() {
  647 + debugRootView.setVisibility(View.VISIBLE);
  648 + }
  649 +
  650 + private void showToast(int messageId) {
  651 + showToast(getString(messageId));
  652 + }
  653 +
  654 + private void showToast(String message) {
  655 + Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
  656 + }
  657 +
  658 + private static boolean isBehindLiveWindow(ExoPlaybackException e) {
  659 + if (e.type != ExoPlaybackException.TYPE_SOURCE) {
  660 + return false;
  661 + }
  662 + Throwable cause = e.getSourceException();
  663 + while (cause != null) {
  664 + if (cause instanceof BehindLiveWindowException) {
  665 + return true;
  666 + }
  667 + cause = cause.getCause();
  668 + }
  669 + return false;
  670 + }
  671 +
  672 + private class PlayerEventListener implements Player.EventListener {
  673 +
  674 + @Override
  675 + public void onPlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) {
  676 + if (playbackState == Player.STATE_ENDED) {
  677 + showControls();
  678 + }
  679 + updateButtonVisibility();
  680 + }
  681 +
  682 + @Override
  683 + public void onPlayerError(ExoPlaybackException e) {
  684 + if (isBehindLiveWindow(e)) {
  685 + clearStartPosition();
  686 + initializePlayer();
  687 + } else {
  688 + updateButtonVisibility();
  689 + showControls();
  690 + }
  691 + }
  692 +
  693 + @Override
  694 + @SuppressWarnings("ReferenceEquality")
  695 + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
  696 + updateButtonVisibility();
  697 + if (trackGroups != lastSeenTrackGroupArray) {
  698 + MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
  699 + if (mappedTrackInfo != null) {
  700 + if (mappedTrackInfo.getTypeSupport(C.TRACK_TYPE_VIDEO)
  701 + == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
  702 + showToast(R.string.error_unsupported_video);
  703 + }
  704 + if (mappedTrackInfo.getTypeSupport(C.TRACK_TYPE_AUDIO)
  705 + == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
  706 + showToast(R.string.error_unsupported_audio);
  707 + }
  708 + }
  709 + lastSeenTrackGroupArray = trackGroups;
  710 + }
  711 + }
  712 + }
  713 +
  714 + private class PlayerErrorMessageProvider implements ErrorMessageProvider<ExoPlaybackException> {
  715 +
  716 + @Override
  717 + public Pair<Integer, String> getErrorMessage(ExoPlaybackException e) {
  718 + String errorString = getString(R.string.error_generic);
  719 + if (e.type == ExoPlaybackException.TYPE_RENDERER) {
  720 + Exception cause = e.getRendererException();
  721 + if (cause instanceof DecoderInitializationException) {
  722 + // Special case for decoder initialization failures.
  723 + DecoderInitializationException decoderInitializationException =
  724 + (DecoderInitializationException) cause;
  725 + if (decoderInitializationException.codecInfo == null) {
  726 + if (decoderInitializationException.getCause() instanceof DecoderQueryException) {
  727 + errorString = getString(R.string.error_querying_decoders);
  728 + } else if (decoderInitializationException.secureDecoderRequired) {
  729 + errorString =
  730 + getString(
  731 + R.string.error_no_secure_decoder, decoderInitializationException.mimeType);
  732 + } else {
  733 + errorString =
  734 + getString(R.string.error_no_decoder, decoderInitializationException.mimeType);
  735 + }
  736 + } else {
  737 + errorString =
  738 + getString(
  739 + R.string.error_instantiating_decoder,
  740 + decoderInitializationException.codecInfo.name);
  741 + }
  742 + }
  743 + }
  744 + return Pair.create(0, errorString);
  745 + }
  746 + }
  747 +}
... ...
app/strike/src/main/java/com/cnlive/strike/demo/exoplayer/demo/Sample.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2019 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.cnlive.strike.demo.exoplayer.demo;
  17 +
  18 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.ACTION_VIEW_LIST;
  19 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.AD_TAG_URI_EXTRA;
  20 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.DRM_KEY_REQUEST_PROPERTIES_EXTRA;
  21 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.DRM_LICENSE_URL_EXTRA;
  22 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.DRM_MULTI_SESSION_EXTRA;
  23 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.DRM_SCHEME_EXTRA;
  24 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.DRM_SCHEME_UUID_EXTRA;
  25 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.EXTENSION_EXTRA;
  26 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.IS_LIVE_EXTRA;
  27 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.SUBTITLE_LANGUAGE_EXTRA;
  28 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.SUBTITLE_MIME_TYPE_EXTRA;
  29 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.SUBTITLE_URI_EXTRA;
  30 +import static com.cnlive.strike.demo.exoplayer.demo.PlayerActivity.URI_EXTRA;
  31 +
  32 +import android.content.Intent;
  33 +import android.net.Uri;
  34 +import androidx.annotation.Nullable;
  35 +import com.google.android.exoplayer2.util.Assertions;
  36 +import com.google.android.exoplayer2.util.Util;
  37 +import java.util.ArrayList;
  38 +import java.util.UUID;
  39 +
  40 +/* package */public abstract class Sample {
  41 +
  42 + public static final class UriSample extends Sample {
  43 +
  44 + public static UriSample createFromIntent(Uri uri, Intent intent, String extrasKeySuffix) {
  45 + String extension = intent.getStringExtra(EXTENSION_EXTRA + extrasKeySuffix);
  46 + String adsTagUriString = intent.getStringExtra(AD_TAG_URI_EXTRA + extrasKeySuffix);
  47 + boolean isLive =
  48 + intent.getBooleanExtra(IS_LIVE_EXTRA + extrasKeySuffix, /* defaultValue= */ false);
  49 + Uri adTagUri = adsTagUriString != null ? Uri.parse(adsTagUriString) : null;
  50 + return new UriSample(
  51 + /* name= */ null,
  52 + uri,
  53 + extension,
  54 + isLive,
  55 + DrmInfo.createFromIntent(intent, extrasKeySuffix),
  56 + adTagUri,
  57 + /* sphericalStereoMode= */ null,
  58 + SubtitleInfo.createFromIntent(intent, extrasKeySuffix));
  59 + }
  60 +
  61 + public final Uri uri;
  62 + public final String extension;
  63 + public final boolean isLive;
  64 + public final DrmInfo drmInfo;
  65 + public final Uri adTagUri;
  66 + @Nullable public final String sphericalStereoMode;
  67 + @Nullable SubtitleInfo subtitleInfo;
  68 +
  69 + public UriSample(
  70 + String name,
  71 + Uri uri,
  72 + String extension,
  73 + boolean isLive,
  74 + DrmInfo drmInfo,
  75 + Uri adTagUri,
  76 + @Nullable String sphericalStereoMode,
  77 + @Nullable SubtitleInfo subtitleInfo) {
  78 + super(name);
  79 + this.uri = uri;
  80 + this.extension = extension;
  81 + this.isLive = isLive;
  82 + this.drmInfo = drmInfo;
  83 + this.adTagUri = adTagUri;
  84 + this.sphericalStereoMode = sphericalStereoMode;
  85 + this.subtitleInfo = subtitleInfo;
  86 + }
  87 +
  88 + @Override
  89 + public void addToIntent(Intent intent) {
  90 + intent.setAction(PlayerActivity.ACTION_VIEW).setData(uri);
  91 + intent.putExtra(IS_LIVE_EXTRA, isLive);
  92 + intent.putExtra(PlayerActivity.SPHERICAL_STEREO_MODE_EXTRA, sphericalStereoMode);
  93 + addPlayerConfigToIntent(intent, /* extrasKeySuffix= */ "");
  94 + }
  95 +
  96 + public void addToPlaylistIntent(Intent intent, String extrasKeySuffix) {
  97 + intent.putExtra(URI_EXTRA + extrasKeySuffix, uri.toString());
  98 + intent.putExtra(IS_LIVE_EXTRA + extrasKeySuffix, isLive);
  99 + addPlayerConfigToIntent(intent, extrasKeySuffix);
  100 + }
  101 +
  102 + private void addPlayerConfigToIntent(Intent intent, String extrasKeySuffix) {
  103 + intent
  104 + .putExtra(EXTENSION_EXTRA + extrasKeySuffix, extension)
  105 + .putExtra(
  106 + AD_TAG_URI_EXTRA + extrasKeySuffix, adTagUri != null ? adTagUri.toString() : null);
  107 + if (drmInfo != null) {
  108 + drmInfo.addToIntent(intent, extrasKeySuffix);
  109 + }
  110 + if (subtitleInfo != null) {
  111 + subtitleInfo.addToIntent(intent, extrasKeySuffix);
  112 + }
  113 + }
  114 + }
  115 +
  116 + public static final class PlaylistSample extends Sample {
  117 +
  118 + public final UriSample[] children;
  119 +
  120 + public PlaylistSample(String name, UriSample... children) {
  121 + super(name);
  122 + this.children = children;
  123 + }
  124 +
  125 + @Override
  126 + public void addToIntent(Intent intent) {
  127 + intent.setAction(ACTION_VIEW_LIST);
  128 + for (int i = 0; i < children.length; i++) {
  129 + children[i].addToPlaylistIntent(intent, /* extrasKeySuffix= */ "_" + i);
  130 + }
  131 + }
  132 + }
  133 +
  134 + public static final class DrmInfo {
  135 +
  136 + public static DrmInfo createFromIntent(Intent intent, String extrasKeySuffix) {
  137 + String schemeKey = DRM_SCHEME_EXTRA + extrasKeySuffix;
  138 + String schemeUuidKey = DRM_SCHEME_UUID_EXTRA + extrasKeySuffix;
  139 + if (!intent.hasExtra(schemeKey) && !intent.hasExtra(schemeUuidKey)) {
  140 + return null;
  141 + }
  142 + String drmSchemeExtra =
  143 + intent.hasExtra(schemeKey)
  144 + ? intent.getStringExtra(schemeKey)
  145 + : intent.getStringExtra(schemeUuidKey);
  146 + UUID drmScheme = Util.getDrmUuid(drmSchemeExtra);
  147 + String drmLicenseUrl = intent.getStringExtra(DRM_LICENSE_URL_EXTRA + extrasKeySuffix);
  148 + String[] keyRequestPropertiesArray =
  149 + intent.getStringArrayExtra(DRM_KEY_REQUEST_PROPERTIES_EXTRA + extrasKeySuffix);
  150 + boolean drmMultiSession =
  151 + intent.getBooleanExtra(DRM_MULTI_SESSION_EXTRA + extrasKeySuffix, false);
  152 + return new DrmInfo(drmScheme, drmLicenseUrl, keyRequestPropertiesArray, drmMultiSession);
  153 + }
  154 +
  155 + public final UUID drmScheme;
  156 + public final String drmLicenseUrl;
  157 + public final String[] drmKeyRequestProperties;
  158 + public final boolean drmMultiSession;
  159 +
  160 + public DrmInfo(
  161 + UUID drmScheme,
  162 + String drmLicenseUrl,
  163 + String[] drmKeyRequestProperties,
  164 + boolean drmMultiSession) {
  165 + this.drmScheme = drmScheme;
  166 + this.drmLicenseUrl = drmLicenseUrl;
  167 + this.drmKeyRequestProperties = drmKeyRequestProperties;
  168 + this.drmMultiSession = drmMultiSession;
  169 + }
  170 +
  171 + public void addToIntent(Intent intent, String extrasKeySuffix) {
  172 + Assertions.checkNotNull(intent);
  173 + intent.putExtra(DRM_SCHEME_EXTRA + extrasKeySuffix, drmScheme.toString());
  174 + intent.putExtra(DRM_LICENSE_URL_EXTRA + extrasKeySuffix, drmLicenseUrl);
  175 + intent.putExtra(DRM_KEY_REQUEST_PROPERTIES_EXTRA + extrasKeySuffix, drmKeyRequestProperties);
  176 + intent.putExtra(DRM_MULTI_SESSION_EXTRA + extrasKeySuffix, drmMultiSession);
  177 + }
  178 + }
  179 +
  180 + public static final class SubtitleInfo {
  181 +
  182 + @Nullable
  183 + public static SubtitleInfo createFromIntent(Intent intent, String extrasKeySuffix) {
  184 + if (!intent.hasExtra(SUBTITLE_URI_EXTRA + extrasKeySuffix)) {
  185 + return null;
  186 + }
  187 + return new SubtitleInfo(
  188 + Uri.parse(intent.getStringExtra(SUBTITLE_URI_EXTRA + extrasKeySuffix)),
  189 + intent.getStringExtra(SUBTITLE_MIME_TYPE_EXTRA + extrasKeySuffix),
  190 + intent.getStringExtra(SUBTITLE_LANGUAGE_EXTRA + extrasKeySuffix));
  191 + }
  192 +
  193 + public final Uri uri;
  194 + public final String mimeType;
  195 + @Nullable public final String language;
  196 +
  197 + public SubtitleInfo(Uri uri, String mimeType, @Nullable String language) {
  198 + this.uri = Assertions.checkNotNull(uri);
  199 + this.mimeType = Assertions.checkNotNull(mimeType);
  200 + this.language = language;
  201 + }
  202 +
  203 + public void addToIntent(Intent intent, String extrasKeySuffix) {
  204 + intent.putExtra(SUBTITLE_URI_EXTRA + extrasKeySuffix, uri.toString());
  205 + intent.putExtra(SUBTITLE_MIME_TYPE_EXTRA + extrasKeySuffix, mimeType);
  206 + intent.putExtra(SUBTITLE_LANGUAGE_EXTRA + extrasKeySuffix, language);
  207 + }
  208 + }
  209 +
  210 + public static Sample createFromIntent(Intent intent) {
  211 + if (ACTION_VIEW_LIST.equals(intent.getAction())) {
  212 + ArrayList<String> intentUris = new ArrayList<>();
  213 + int index = 0;
  214 + while (intent.hasExtra(URI_EXTRA + "_" + index)) {
  215 + intentUris.add(intent.getStringExtra(URI_EXTRA + "_" + index));
  216 + index++;
  217 + }
  218 + UriSample[] children = new UriSample[intentUris.size()];
  219 + for (int i = 0; i < children.length; i++) {
  220 + Uri uri = Uri.parse(intentUris.get(i));
  221 + children[i] = UriSample.createFromIntent(uri, intent, /* extrasKeySuffix= */ "_" + i);
  222 + }
  223 + return new PlaylistSample(/* name= */ null, children);
  224 + } else {
  225 + return UriSample.createFromIntent(intent.getData(), intent, /* extrasKeySuffix= */ "");
  226 + }
  227 + }
  228 +
  229 + @Nullable public final String name;
  230 +
  231 + public Sample(String name) {
  232 + this.name = name;
  233 + }
  234 +
  235 + public abstract void addToIntent(Intent intent);
  236 +}
... ...
app/strike/src/main/java/com/cnlive/strike/demo/exoplayer/demo/SampleChooserActivity.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2016 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.cnlive.strike.demo.exoplayer.demo;
  17 +
  18 +import android.content.Context;
  19 +import android.content.Intent;
  20 +import android.content.res.AssetManager;
  21 +import android.net.Uri;
  22 +import android.os.AsyncTask;
  23 +import android.os.Bundle;
  24 +import android.util.JsonReader;
  25 +import android.view.Menu;
  26 +import android.view.MenuInflater;
  27 +import android.view.MenuItem;
  28 +import android.view.View;
  29 +import android.view.View.OnClickListener;
  30 +import android.view.ViewGroup;
  31 +import android.widget.BaseExpandableListAdapter;
  32 +import android.widget.ExpandableListView;
  33 +import android.widget.ExpandableListView.OnChildClickListener;
  34 +import android.widget.ImageButton;
  35 +import android.widget.TextView;
  36 +import android.widget.Toast;
  37 +import androidx.annotation.Nullable;
  38 +import androidx.appcompat.app.AppCompatActivity;
  39 +
  40 +import com.cnlive.cloud.R;
  41 +import com.google.android.exoplayer2.ParserException;
  42 +import com.google.android.exoplayer2.RenderersFactory;
  43 +import com.google.android.exoplayer2.offline.DownloadService;
  44 +import com.google.android.exoplayer2.upstream.DataSource;
  45 +import com.google.android.exoplayer2.upstream.DataSourceInputStream;
  46 +import com.google.android.exoplayer2.upstream.DataSpec;
  47 +import com.google.android.exoplayer2.upstream.DefaultDataSource;
  48 +import com.google.android.exoplayer2.util.Assertions;
  49 +import com.google.android.exoplayer2.util.Log;
  50 +import com.google.android.exoplayer2.util.Util;
  51 +import java.io.IOException;
  52 +import java.io.InputStream;
  53 +import java.io.InputStreamReader;
  54 +import java.util.ArrayList;
  55 +import java.util.Arrays;
  56 +import java.util.Collections;
  57 +import java.util.List;
  58 +import com.cnlive.strike.demo.exoplayer.demo.Sample;
  59 +/** An activity for selecting from a list of media samples. */
  60 +public class SampleChooserActivity extends AppCompatActivity
  61 + implements DownloadTracker.Listener, OnChildClickListener {
  62 +
  63 + private static final String TAG = "SampleChooserActivity";
  64 +
  65 + private boolean useExtensionRenderers;
  66 + private DownloadTracker downloadTracker;
  67 + private SampleAdapter sampleAdapter;
  68 + private MenuItem preferExtensionDecodersMenuItem;
  69 + private MenuItem randomAbrMenuItem;
  70 + private MenuItem tunnelingMenuItem;
  71 +
  72 + @Override
  73 + public void onCreate(Bundle savedInstanceState) {
  74 + super.onCreate(savedInstanceState);
  75 + setContentView(R.layout.sample_chooser_activity);
  76 + sampleAdapter = new SampleAdapter();
  77 + ExpandableListView sampleListView = findViewById(R.id.sample_list);
  78 + sampleListView.setAdapter(sampleAdapter);
  79 + sampleListView.setOnChildClickListener(this);
  80 +
  81 + Intent intent = getIntent();
  82 + String dataUri = intent.getDataString();
  83 + String[] uris;
  84 + if (dataUri != null) {
  85 + uris = new String[] {dataUri};
  86 + } else {
  87 + ArrayList<String> uriList = new ArrayList<>();
  88 + AssetManager assetManager = getAssets();
  89 + try {
  90 + for (String asset : assetManager.list("")) {
  91 + if (asset.endsWith(".exolist.json")) {
  92 + uriList.add("asset:///" + asset);
  93 + }
  94 + }
  95 + } catch (IOException e) {
  96 + Toast.makeText(getApplicationContext(), R.string.sample_list_load_error, Toast.LENGTH_LONG)
  97 + .show();
  98 + }
  99 + uris = new String[uriList.size()];
  100 + uriList.toArray(uris);
  101 + Arrays.sort(uris);
  102 + }
  103 +
  104 + DemoApplication application = (DemoApplication) getApplication();
  105 + useExtensionRenderers = application.useExtensionRenderers();
  106 + downloadTracker = application.getDownloadTracker();
  107 + SampleListLoader loaderTask = new SampleListLoader();
  108 + loaderTask.execute(uris);
  109 +
  110 + // Start the download service if it should be running but it's not currently.
  111 + // Starting the service in the foreground causes notification flicker if there is no scheduled
  112 + // action. Starting it in the background throws an exception if the app is in the background too
  113 + // (e.g. if device screen is locked).
  114 + try {
  115 + DownloadService.start(this, DemoDownloadService.class);
  116 + } catch (IllegalStateException e) {
  117 + DownloadService.startForeground(this, DemoDownloadService.class);
  118 + }
  119 + }
  120 +
  121 + @Override
  122 + public boolean onCreateOptionsMenu(Menu menu) {
  123 + MenuInflater inflater = getMenuInflater();
  124 + inflater.inflate(R.menu.sample_chooser_menu, menu);
  125 + preferExtensionDecodersMenuItem = menu.findItem(R.id.prefer_extension_decoders);
  126 + preferExtensionDecodersMenuItem.setVisible(useExtensionRenderers);
  127 + randomAbrMenuItem = menu.findItem(R.id.random_abr);
  128 + tunnelingMenuItem = menu.findItem(R.id.tunneling);
  129 + if (Util.SDK_INT < 21) {
  130 + tunnelingMenuItem.setEnabled(false);
  131 + }
  132 + return true;
  133 + }
  134 +
  135 + @Override
  136 + public boolean onOptionsItemSelected(MenuItem item) {
  137 + item.setChecked(!item.isChecked());
  138 + return true;
  139 + }
  140 +
  141 + @Override
  142 + public void onStart() {
  143 + super.onStart();
  144 + downloadTracker.addListener(this);
  145 + sampleAdapter.notifyDataSetChanged();
  146 + }
  147 +
  148 + @Override
  149 + public void onStop() {
  150 + downloadTracker.removeListener(this);
  151 + super.onStop();
  152 + }
  153 +
  154 + @Override
  155 + public void onDownloadsChanged() {
  156 + sampleAdapter.notifyDataSetChanged();
  157 + }
  158 +
  159 + private void onSampleGroups(final List<SampleGroup> groups, boolean sawError) {
  160 + if (sawError) {
  161 + Toast.makeText(getApplicationContext(), R.string.sample_list_load_error, Toast.LENGTH_LONG)
  162 + .show();
  163 + }
  164 + sampleAdapter.setSampleGroups(groups);
  165 + }
  166 +
  167 + @Override
  168 + public boolean onChildClick(
  169 + ExpandableListView parent, View view, int groupPosition, int childPosition, long id) {
  170 + Sample sample = (Sample) view.getTag();
  171 + Intent intent = new Intent(this, PlayerActivity.class);
  172 + intent.putExtra(
  173 + PlayerActivity.PREFER_EXTENSION_DECODERS_EXTRA,
  174 + isNonNullAndChecked(preferExtensionDecodersMenuItem));
  175 + String abrAlgorithm =
  176 + isNonNullAndChecked(randomAbrMenuItem)
  177 + ? PlayerActivity.ABR_ALGORITHM_RANDOM
  178 + : PlayerActivity.ABR_ALGORITHM_DEFAULT;
  179 + intent.putExtra(PlayerActivity.ABR_ALGORITHM_EXTRA, abrAlgorithm);
  180 + intent.putExtra(PlayerActivity.TUNNELING_EXTRA, isNonNullAndChecked(tunnelingMenuItem));
  181 + sample.addToIntent(intent);
  182 + startActivity(intent);
  183 + return true;
  184 + }
  185 +
  186 + private void onSampleDownloadButtonClicked(Sample sample) {
  187 + int downloadUnsupportedStringId = getDownloadUnsupportedStringId(sample);
  188 + if (downloadUnsupportedStringId != 0) {
  189 + Toast.makeText(getApplicationContext(), downloadUnsupportedStringId, Toast.LENGTH_LONG)
  190 + .show();
  191 + } else {
  192 + Sample.UriSample UriSample = (Sample.UriSample) sample;
  193 + RenderersFactory renderersFactory =
  194 + ((DemoApplication) getApplication())
  195 + .buildRenderersFactory(isNonNullAndChecked(preferExtensionDecodersMenuItem));
  196 + downloadTracker.toggleDownload(
  197 + getSupportFragmentManager(),
  198 + sample.name,
  199 + UriSample.uri,
  200 + UriSample.extension,
  201 + renderersFactory);
  202 + }
  203 + }
  204 +
  205 + private int getDownloadUnsupportedStringId(Sample sample) {
  206 + if (sample instanceof Sample.PlaylistSample) {
  207 + return R.string.download_playlist_unsupported;
  208 + }
  209 + Sample.UriSample UriSample = (Sample.UriSample) sample;
  210 + if (UriSample.drmInfo != null) {
  211 + return R.string.download_drm_unsupported;
  212 + }
  213 + if (UriSample.isLive) {
  214 + return R.string.download_live_unsupported;
  215 + }
  216 + if (UriSample.adTagUri != null) {
  217 + return R.string.download_ads_unsupported;
  218 + }
  219 + String scheme = UriSample.uri.getScheme();
  220 + if (!("http".equals(scheme) || "https".equals(scheme))) {
  221 + return R.string.download_scheme_unsupported;
  222 + }
  223 + return 0;
  224 + }
  225 +
  226 + private static boolean isNonNullAndChecked(@Nullable MenuItem menuItem) {
  227 + // Temporary workaround for layouts that do not inflate the options menu.
  228 + return menuItem != null && menuItem.isChecked();
  229 + }
  230 +
  231 + private final class SampleListLoader extends AsyncTask<String, Void, List<SampleGroup>> {
  232 +
  233 + private boolean sawError;
  234 +
  235 + @Override
  236 + protected List<SampleGroup> doInBackground(String... uris) {
  237 + List<SampleGroup> result = new ArrayList<>();
  238 + Context context = getApplicationContext();
  239 + String userAgent = Util.getUserAgent(context, "ExoPlayerDemo");
  240 + DataSource dataSource =
  241 + new DefaultDataSource(context, userAgent, /* allowCrossProtocolRedirects= */ false);
  242 + for (String uri : uris) {
  243 + DataSpec dataSpec = new DataSpec(Uri.parse(uri));
  244 + InputStream inputStream = new DataSourceInputStream(dataSource, dataSpec);
  245 + try {
  246 + readSampleGroups(new JsonReader(new InputStreamReader(inputStream, "UTF-8")), result);
  247 + } catch (Exception e) {
  248 + Log.e(TAG, "Error loading sample list: " + uri, e);
  249 + sawError = true;
  250 + } finally {
  251 + Util.closeQuietly(dataSource);
  252 + }
  253 + }
  254 + return result;
  255 + }
  256 +
  257 + @Override
  258 + protected void onPostExecute(List<SampleGroup> result) {
  259 + onSampleGroups(result, sawError);
  260 + }
  261 +
  262 + private void readSampleGroups(JsonReader reader, List<SampleGroup> groups) throws IOException {
  263 + reader.beginArray();
  264 + while (reader.hasNext()) {
  265 + readSampleGroup(reader, groups);
  266 + }
  267 + reader.endArray();
  268 + }
  269 +
  270 + private void readSampleGroup(JsonReader reader, List<SampleGroup> groups) throws IOException {
  271 + String groupName = "";
  272 + ArrayList<Sample> samples = new ArrayList<>();
  273 +
  274 + reader.beginObject();
  275 + while (reader.hasNext()) {
  276 + String name = reader.nextName();
  277 + switch (name) {
  278 + case "name":
  279 + groupName = reader.nextString();
  280 + break;
  281 + case "samples":
  282 + reader.beginArray();
  283 + while (reader.hasNext()) {
  284 + samples.add(readEntry(reader, false));
  285 + }
  286 + reader.endArray();
  287 + break;
  288 + case "_comment":
  289 + reader.nextString(); // Ignore.
  290 + break;
  291 + default:
  292 + throw new ParserException("Unsupported name: " + name);
  293 + }
  294 + }
  295 + reader.endObject();
  296 +
  297 + SampleGroup group = getGroup(groupName, groups);
  298 + group.samples.addAll(samples);
  299 + }
  300 +
  301 + private Sample readEntry(JsonReader reader, boolean insidePlaylist) throws IOException {
  302 + String sampleName = null;
  303 + Uri uri = null;
  304 + String extension = null;
  305 + boolean isLive = false;
  306 + String drmScheme = null;
  307 + String drmLicenseUrl = null;
  308 + String[] drmKeyRequestProperties = null;
  309 + boolean drmMultiSession = false;
  310 + ArrayList<Sample.UriSample> playlistSamples = null;
  311 + String adTagUri = null;
  312 + String sphericalStereoMode = null;
  313 + List<Sample.SubtitleInfo> subtitleInfos = new ArrayList<>();
  314 + Uri subtitleUri = null;
  315 + String subtitleMimeType = null;
  316 + String subtitleLanguage = null;
  317 +
  318 + reader.beginObject();
  319 + while (reader.hasNext()) {
  320 + String name = reader.nextName();
  321 + switch (name) {
  322 + case "name":
  323 + sampleName = reader.nextString();
  324 + break;
  325 + case "uri":
  326 + uri = Uri.parse(reader.nextString());
  327 + break;
  328 + case "extension":
  329 + extension = reader.nextString();
  330 + break;
  331 + case "drm_scheme":
  332 + drmScheme = reader.nextString();
  333 + break;
  334 + case "is_live":
  335 + isLive = reader.nextBoolean();
  336 + break;
  337 + case "drm_license_url":
  338 + drmLicenseUrl = reader.nextString();
  339 + break;
  340 + case "drm_key_request_properties":
  341 + ArrayList<String> drmKeyRequestPropertiesList = new ArrayList<>();
  342 + reader.beginObject();
  343 + while (reader.hasNext()) {
  344 + drmKeyRequestPropertiesList.add(reader.nextName());
  345 + drmKeyRequestPropertiesList.add(reader.nextString());
  346 + }
  347 + reader.endObject();
  348 + drmKeyRequestProperties = drmKeyRequestPropertiesList.toArray(new String[0]);
  349 + break;
  350 + case "drm_multi_session":
  351 + drmMultiSession = reader.nextBoolean();
  352 + break;
  353 + case "playlist":
  354 + Assertions.checkState(!insidePlaylist, "Invalid nesting of playlists");
  355 + playlistSamples = new ArrayList<Sample.UriSample>();
  356 + reader.beginArray();
  357 + while (reader.hasNext()) {
  358 + playlistSamples.add((Sample.UriSample) readEntry(reader, /* insidePlaylist= */ true));
  359 + }
  360 + reader.endArray();
  361 + break;
  362 + case "ad_tag_uri":
  363 + adTagUri = reader.nextString();
  364 + break;
  365 + case "spherical_stereo_mode":
  366 + Assertions.checkState(
  367 + !insidePlaylist, "Invalid attribute on nested item: spherical_stereo_mode");
  368 + sphericalStereoMode = reader.nextString();
  369 + break;
  370 + case "subtitle_uri":
  371 + subtitleUri = Uri.parse(reader.nextString());
  372 + break;
  373 + case "subtitle_mime_type":
  374 + subtitleMimeType = reader.nextString();
  375 + break;
  376 + case "subtitle_language":
  377 + subtitleLanguage = reader.nextString();
  378 + break;
  379 + default:
  380 + throw new ParserException("Unsupported attribute name: " + name);
  381 + }
  382 + }
  383 + reader.endObject();
  384 + Sample.DrmInfo drmInfo =
  385 + drmScheme == null
  386 + ? null
  387 + : new Sample.DrmInfo(
  388 + Util.getDrmUuid(drmScheme),
  389 + drmLicenseUrl,
  390 + drmKeyRequestProperties,
  391 + drmMultiSession);
  392 + Sample.SubtitleInfo subtitleInfo =
  393 + subtitleUri == null
  394 + ? null
  395 + : new Sample.SubtitleInfo(
  396 + subtitleUri,
  397 + Assertions.checkNotNull(
  398 + subtitleMimeType, "subtitle_mime_type is required if subtitle_uri is set."),
  399 + subtitleLanguage);
  400 + if (playlistSamples != null) {
  401 + Sample.UriSample[] playlistSamplesArray = playlistSamples.toArray(new Sample.UriSample[0]);
  402 + return new Sample.PlaylistSample(sampleName, playlistSamplesArray);
  403 + } else {
  404 + return new Sample.UriSample(
  405 + sampleName,
  406 + uri,
  407 + extension,
  408 + isLive,
  409 + drmInfo,
  410 + adTagUri != null ? Uri.parse(adTagUri) : null,
  411 + sphericalStereoMode,
  412 + subtitleInfo);
  413 + }
  414 + }
  415 +
  416 + private SampleGroup getGroup(String groupName, List<SampleGroup> groups) {
  417 + for (int i = 0; i < groups.size(); i++) {
  418 + if (Util.areEqual(groupName, groups.get(i).title)) {
  419 + return groups.get(i);
  420 + }
  421 + }
  422 + SampleGroup group = new SampleGroup(groupName);
  423 + groups.add(group);
  424 + return group;
  425 + }
  426 +
  427 + }
  428 +
  429 + private final class SampleAdapter extends BaseExpandableListAdapter implements OnClickListener {
  430 +
  431 + private List<SampleGroup> sampleGroups;
  432 +
  433 + public SampleAdapter() {
  434 + sampleGroups = Collections.emptyList();
  435 + }
  436 +
  437 + public void setSampleGroups(List<SampleGroup> sampleGroups) {
  438 + this.sampleGroups = sampleGroups;
  439 + notifyDataSetChanged();
  440 + }
  441 +
  442 + @Override
  443 + public Sample getChild(int groupPosition, int childPosition) {
  444 + return getGroup(groupPosition).samples.get(childPosition);
  445 + }
  446 +
  447 + @Override
  448 + public long getChildId(int groupPosition, int childPosition) {
  449 + return childPosition;
  450 + }
  451 +
  452 + @Override
  453 + public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
  454 + View convertView, ViewGroup parent) {
  455 + View view = convertView;
  456 + if (view == null) {
  457 + view = getLayoutInflater().inflate(R.layout.sample_list_item, parent, false);
  458 + View downloadButton = view.findViewById(R.id.download_button);
  459 + downloadButton.setOnClickListener(this);
  460 + downloadButton.setFocusable(false);
  461 + }
  462 + initializeChildView(view, getChild(groupPosition, childPosition));
  463 + return view;
  464 + }
  465 +
  466 + @Override
  467 + public int getChildrenCount(int groupPosition) {
  468 + return getGroup(groupPosition).samples.size();
  469 + }
  470 +
  471 + @Override
  472 + public SampleGroup getGroup(int groupPosition) {
  473 + return sampleGroups.get(groupPosition);
  474 + }
  475 +
  476 + @Override
  477 + public long getGroupId(int groupPosition) {
  478 + return groupPosition;
  479 + }
  480 +
  481 + @Override
  482 + public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
  483 + ViewGroup parent) {
  484 + View view = convertView;
  485 + if (view == null) {
  486 + view =
  487 + getLayoutInflater()
  488 + .inflate(android.R.layout.simple_expandable_list_item_1, parent, false);
  489 + }
  490 + ((TextView) view).setText(getGroup(groupPosition).title);
  491 + return view;
  492 + }
  493 +
  494 + @Override
  495 + public int getGroupCount() {
  496 + return sampleGroups.size();
  497 + }
  498 +
  499 + @Override
  500 + public boolean hasStableIds() {
  501 + return false;
  502 + }
  503 +
  504 + @Override
  505 + public boolean isChildSelectable(int groupPosition, int childPosition) {
  506 + return true;
  507 + }
  508 +
  509 + @Override
  510 + public void onClick(View view) {
  511 + onSampleDownloadButtonClicked((Sample) view.getTag());
  512 + }
  513 +
  514 + private void initializeChildView(View view, Sample sample) {
  515 + view.setTag(sample);
  516 + TextView sampleTitle = view.findViewById(R.id.sample_title);
  517 + sampleTitle.setText(sample.name);
  518 +
  519 + boolean canDownload = getDownloadUnsupportedStringId(sample) == 0;
  520 + boolean isDownloaded = canDownload && downloadTracker.isDownloaded(((Sample.UriSample) sample).uri);
  521 + ImageButton downloadButton = view.findViewById(R.id.download_button);
  522 + downloadButton.setTag(sample);
  523 + downloadButton.setColorFilter(
  524 + canDownload ? (isDownloaded ? 0xFF42A5F5 : 0xFFBDBDBD) : 0xFF666666);
  525 + downloadButton.setImageResource(
  526 + isDownloaded ? R.drawable.ic_download_done : R.drawable.ic_download);
  527 + }
  528 + }
  529 +
  530 + private static final class SampleGroup {
  531 +
  532 + public final String title;
  533 + public final List<Sample> samples;
  534 +
  535 + public SampleGroup(String title) {
  536 + this.title = title;
  537 + this.samples = new ArrayList<>();
  538 + }
  539 +
  540 + }
  541 +}
... ...
app/strike/src/main/java/com/cnlive/strike/demo/exoplayer/demo/TrackSelectionDialog.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2019 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.cnlive.strike.demo.exoplayer.demo;
  17 +
  18 +import android.app.Dialog;
  19 +import android.content.DialogInterface;
  20 +import android.content.res.Resources;
  21 +import android.os.Bundle;
  22 +import android.util.SparseArray;
  23 +import android.view.LayoutInflater;
  24 +import android.view.View;
  25 +import android.view.ViewGroup;
  26 +import android.widget.Button;
  27 +import androidx.annotation.Nullable;
  28 +import androidx.appcompat.app.AppCompatDialog;
  29 +import androidx.fragment.app.DialogFragment;
  30 +import androidx.fragment.app.Fragment;
  31 +import androidx.fragment.app.FragmentManager;
  32 +import androidx.fragment.app.FragmentPagerAdapter;
  33 +import androidx.viewpager.widget.ViewPager;
  34 +
  35 +import com.cnlive.cloud.R;
  36 +import com.google.android.exoplayer2.C;
  37 +import com.google.android.exoplayer2.source.TrackGroupArray;
  38 +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
  39 +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
  40 +import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
  41 +import com.google.android.exoplayer2.ui.TrackSelectionView;
  42 +import com.google.android.exoplayer2.util.Assertions;
  43 +import com.google.android.material.tabs.TabLayout;
  44 +import java.util.ArrayList;
  45 +import java.util.Collections;
  46 +import java.util.List;
  47 +
  48 +/** Dialog to select tracks. */
  49 +public final class TrackSelectionDialog extends DialogFragment {
  50 +
  51 + private final SparseArray<TrackSelectionViewFragment> tabFragments;
  52 + private final ArrayList<Integer> tabTrackTypes;
  53 +
  54 + private int titleId;
  55 + private DialogInterface.OnClickListener onClickListener;
  56 + private DialogInterface.OnDismissListener onDismissListener;
  57 +
  58 + /**
  59 + * Returns whether a track selection dialog will have content to display if initialized with the
  60 + * specified {@link DefaultTrackSelector} in its current state.
  61 + */
  62 + public static boolean willHaveContent(DefaultTrackSelector trackSelector) {
  63 + MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
  64 + return mappedTrackInfo != null && willHaveContent(mappedTrackInfo);
  65 + }
  66 +
  67 + /**
  68 + * Returns whether a track selection dialog will have content to display if initialized with the
  69 + * specified {@link MappedTrackInfo}.
  70 + */
  71 + public static boolean willHaveContent(MappedTrackInfo mappedTrackInfo) {
  72 + for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
  73 + if (showTabForRenderer(mappedTrackInfo, i)) {
  74 + return true;
  75 + }
  76 + }
  77 + return false;
  78 + }
  79 +
  80 + /**
  81 + * Creates a dialog for a given {@link DefaultTrackSelector}, whose parameters will be
  82 + * automatically updated when tracks are selected.
  83 + *
  84 + * @param trackSelector The {@link DefaultTrackSelector}.
  85 + * @param onDismissListener A {@link DialogInterface.OnDismissListener} to call when the dialog is
  86 + * dismissed.
  87 + */
  88 + public static TrackSelectionDialog createForTrackSelector(
  89 + DefaultTrackSelector trackSelector, DialogInterface.OnDismissListener onDismissListener) {
  90 + MappedTrackInfo mappedTrackInfo =
  91 + Assertions.checkNotNull(trackSelector.getCurrentMappedTrackInfo());
  92 + TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog();
  93 + DefaultTrackSelector.Parameters parameters = trackSelector.getParameters();
  94 + trackSelectionDialog.init(
  95 + /* titleId= */ R.string.track_selection_title,
  96 + mappedTrackInfo,
  97 + /* initialParameters = */ parameters,
  98 + /* allowAdaptiveSelections =*/ true,
  99 + /* allowMultipleOverrides= */ false,
  100 + /* onClickListener= */ (dialog, which) -> {
  101 + DefaultTrackSelector.ParametersBuilder builder = parameters.buildUpon();
  102 + for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
  103 + builder
  104 + .clearSelectionOverrides(/* rendererIndex= */ i)
  105 + .setRendererDisabled(
  106 + /* rendererIndex= */ i,
  107 + trackSelectionDialog.getIsDisabled(/* rendererIndex= */ i));
  108 + List<SelectionOverride> overrides =
  109 + trackSelectionDialog.getOverrides(/* rendererIndex= */ i);
  110 + if (!overrides.isEmpty()) {
  111 + builder.setSelectionOverride(
  112 + /* rendererIndex= */ i,
  113 + mappedTrackInfo.getTrackGroups(/* rendererIndex= */ i),
  114 + overrides.get(0));
  115 + }
  116 + }
  117 + trackSelector.setParameters(builder);
  118 + },
  119 + onDismissListener);
  120 + return trackSelectionDialog;
  121 + }
  122 +
  123 + /**
  124 + * Creates a dialog for given {@link MappedTrackInfo} and {@link DefaultTrackSelector.Parameters}.
  125 + *
  126 + * @param titleId The resource id of the dialog title.
  127 + * @param mappedTrackInfo The {@link MappedTrackInfo} to display.
  128 + * @param initialParameters The {@link DefaultTrackSelector.Parameters} describing the initial
  129 + * track selection.
  130 + * @param allowAdaptiveSelections Whether adaptive selections (consisting of more than one track)
  131 + * can be made.
  132 + * @param allowMultipleOverrides Whether tracks from multiple track groups can be selected.
  133 + * @param onClickListener {@link DialogInterface.OnClickListener} called when tracks are selected.
  134 + * @param onDismissListener {@link DialogInterface.OnDismissListener} called when the dialog is
  135 + * dismissed.
  136 + */
  137 + public static TrackSelectionDialog createForMappedTrackInfoAndParameters(
  138 + int titleId,
  139 + MappedTrackInfo mappedTrackInfo,
  140 + DefaultTrackSelector.Parameters initialParameters,
  141 + boolean allowAdaptiveSelections,
  142 + boolean allowMultipleOverrides,
  143 + DialogInterface.OnClickListener onClickListener,
  144 + DialogInterface.OnDismissListener onDismissListener) {
  145 + TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog();
  146 + trackSelectionDialog.init(
  147 + titleId,
  148 + mappedTrackInfo,
  149 + initialParameters,
  150 + allowAdaptiveSelections,
  151 + allowMultipleOverrides,
  152 + onClickListener,
  153 + onDismissListener);
  154 + return trackSelectionDialog;
  155 + }
  156 +
  157 + public TrackSelectionDialog() {
  158 + tabFragments = new SparseArray<>();
  159 + tabTrackTypes = new ArrayList<>();
  160 + // Retain instance across activity re-creation to prevent losing access to init data.
  161 + setRetainInstance(true);
  162 + }
  163 +
  164 + private void init(
  165 + int titleId,
  166 + MappedTrackInfo mappedTrackInfo,
  167 + DefaultTrackSelector.Parameters initialParameters,
  168 + boolean allowAdaptiveSelections,
  169 + boolean allowMultipleOverrides,
  170 + DialogInterface.OnClickListener onClickListener,
  171 + DialogInterface.OnDismissListener onDismissListener) {
  172 + this.titleId = titleId;
  173 + this.onClickListener = onClickListener;
  174 + this.onDismissListener = onDismissListener;
  175 + for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
  176 + if (showTabForRenderer(mappedTrackInfo, i)) {
  177 + int trackType = mappedTrackInfo.getRendererType(/* rendererIndex= */ i);
  178 + TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(i);
  179 + TrackSelectionViewFragment tabFragment = new TrackSelectionViewFragment();
  180 + tabFragment.init(
  181 + mappedTrackInfo,
  182 + /* rendererIndex= */ i,
  183 + initialParameters.getRendererDisabled(/* rendererIndex= */ i),
  184 + initialParameters.getSelectionOverride(/* rendererIndex= */ i, trackGroupArray),
  185 + allowAdaptiveSelections,
  186 + allowMultipleOverrides);
  187 + tabFragments.put(i, tabFragment);
  188 + tabTrackTypes.add(trackType);
  189 + }
  190 + }
  191 + }
  192 +
  193 + /**
  194 + * Returns whether a renderer is disabled.
  195 + *
  196 + * @param rendererIndex Renderer index.
  197 + * @return Whether the renderer is disabled.
  198 + */
  199 + public boolean getIsDisabled(int rendererIndex) {
  200 + TrackSelectionViewFragment rendererView = tabFragments.get(rendererIndex);
  201 + return rendererView != null && rendererView.isDisabled;
  202 + }
  203 +
  204 + /**
  205 + * Returns the list of selected track selection overrides for the specified renderer. There will
  206 + * be at most one override for each track group.
  207 + *
  208 + * @param rendererIndex Renderer index.
  209 + * @return The list of track selection overrides for this renderer.
  210 + */
  211 + public List<SelectionOverride> getOverrides(int rendererIndex) {
  212 + TrackSelectionViewFragment rendererView = tabFragments.get(rendererIndex);
  213 + return rendererView == null ? Collections.emptyList() : rendererView.overrides;
  214 + }
  215 +
  216 + @Override
  217 + public Dialog onCreateDialog(Bundle savedInstanceState) {
  218 + // We need to own the view to let tab layout work correctly on all API levels. We can't use
  219 + // AlertDialog because it owns the view itself, so we use AppCompatDialog instead, themed using
  220 + // the AlertDialog theme overlay with force-enabled title.
  221 + AppCompatDialog dialog =
  222 + new AppCompatDialog(getActivity(), R.style.TrackSelectionDialogThemeOverlay);
  223 + dialog.setTitle(titleId);
  224 + return dialog;
  225 + }
  226 +
  227 + @Override
  228 + public void onDismiss(DialogInterface dialog) {
  229 + super.onDismiss(dialog);
  230 + onDismissListener.onDismiss(dialog);
  231 + }
  232 +
  233 + @Nullable
  234 + @Override
  235 + public View onCreateView(
  236 + LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  237 +
  238 + View dialogView = inflater.inflate(R.layout.track_selection_dialog, container, false);
  239 + TabLayout tabLayout = dialogView.findViewById(R.id.track_selection_dialog_tab_layout);
  240 + ViewPager viewPager = dialogView.findViewById(R.id.track_selection_dialog_view_pager);
  241 + Button cancelButton = dialogView.findViewById(R.id.track_selection_dialog_cancel_button);
  242 + Button okButton = dialogView.findViewById(R.id.track_selection_dialog_ok_button);
  243 + viewPager.setAdapter(new FragmentAdapter(getChildFragmentManager()));
  244 + tabLayout.setupWithViewPager(viewPager);
  245 + tabLayout.setVisibility(tabFragments.size() > 1 ? View.VISIBLE : View.GONE);
  246 + cancelButton.setOnClickListener(view -> dismiss());
  247 + okButton.setOnClickListener(
  248 + view -> {
  249 + onClickListener.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
  250 + dismiss();
  251 + });
  252 + return dialogView;
  253 + }
  254 +
  255 + private static boolean showTabForRenderer(MappedTrackInfo mappedTrackInfo, int rendererIndex) {
  256 + TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex);
  257 + if (trackGroupArray.length == 0) {
  258 + return false;
  259 + }
  260 + int trackType = mappedTrackInfo.getRendererType(rendererIndex);
  261 + return isSupportedTrackType(trackType);
  262 + }
  263 +
  264 + private static boolean isSupportedTrackType(int trackType) {
  265 + switch (trackType) {
  266 + case C.TRACK_TYPE_VIDEO:
  267 + case C.TRACK_TYPE_AUDIO:
  268 + case C.TRACK_TYPE_TEXT:
  269 + return true;
  270 + default:
  271 + return false;
  272 + }
  273 + }
  274 +
  275 + private static String getTrackTypeString(Resources resources, int trackType) {
  276 + switch (trackType) {
  277 + case C.TRACK_TYPE_VIDEO:
  278 + return resources.getString(R.string.exo_track_selection_title_video);
  279 + case C.TRACK_TYPE_AUDIO:
  280 + return resources.getString(R.string.exo_track_selection_title_audio);
  281 + case C.TRACK_TYPE_TEXT:
  282 + return resources.getString(R.string.exo_track_selection_title_text);
  283 + default:
  284 + throw new IllegalArgumentException();
  285 + }
  286 + }
  287 +
  288 + private final class FragmentAdapter extends FragmentPagerAdapter {
  289 +
  290 + public FragmentAdapter(FragmentManager fragmentManager) {
  291 + super(fragmentManager);
  292 + }
  293 +
  294 + @Override
  295 + public Fragment getItem(int position) {
  296 + return tabFragments.valueAt(position);
  297 + }
  298 +
  299 + @Override
  300 + public int getCount() {
  301 + return tabFragments.size();
  302 + }
  303 +
  304 + @Nullable
  305 + @Override
  306 + public CharSequence getPageTitle(int position) {
  307 + return getTrackTypeString(getResources(), tabTrackTypes.get(position));
  308 + }
  309 + }
  310 +
  311 + /** Fragment to show a track selection in tab of the track selection dialog. */
  312 + public static final class TrackSelectionViewFragment extends Fragment
  313 + implements TrackSelectionView.TrackSelectionListener {
  314 +
  315 + private MappedTrackInfo mappedTrackInfo;
  316 + private int rendererIndex;
  317 + private boolean allowAdaptiveSelections;
  318 + private boolean allowMultipleOverrides;
  319 +
  320 + /* package */ boolean isDisabled;
  321 + /* package */ List<SelectionOverride> overrides;
  322 +
  323 + public TrackSelectionViewFragment() {
  324 + // Retain instance across activity re-creation to prevent losing access to init data.
  325 + setRetainInstance(true);
  326 + }
  327 +
  328 + public void init(
  329 + MappedTrackInfo mappedTrackInfo,
  330 + int rendererIndex,
  331 + boolean initialIsDisabled,
  332 + @Nullable SelectionOverride initialOverride,
  333 + boolean allowAdaptiveSelections,
  334 + boolean allowMultipleOverrides) {
  335 + this.mappedTrackInfo = mappedTrackInfo;
  336 + this.rendererIndex = rendererIndex;
  337 + this.isDisabled = initialIsDisabled;
  338 + this.overrides =
  339 + initialOverride == null
  340 + ? Collections.emptyList()
  341 + : Collections.singletonList(initialOverride);
  342 + this.allowAdaptiveSelections = allowAdaptiveSelections;
  343 + this.allowMultipleOverrides = allowMultipleOverrides;
  344 + }
  345 +
  346 + @Nullable
  347 + @Override
  348 + public View onCreateView(
  349 + LayoutInflater inflater,
  350 + @Nullable ViewGroup container,
  351 + @Nullable Bundle savedInstanceState) {
  352 + View rootView =
  353 + inflater.inflate(
  354 + R.layout.exo_track_selection_dialog, container, /* attachToRoot= */ false);
  355 + TrackSelectionView trackSelectionView = rootView.findViewById(R.id.exo_track_selection_view);
  356 + trackSelectionView.setShowDisableOption(true);
  357 + trackSelectionView.setAllowMultipleOverrides(allowMultipleOverrides);
  358 + trackSelectionView.setAllowAdaptiveSelections(allowAdaptiveSelections);
  359 + trackSelectionView.init(
  360 + mappedTrackInfo, rendererIndex, isDisabled, overrides, /* listener= */ this);
  361 + return rootView;
  362 + }
  363 +
  364 + @Override
  365 + public void onTrackSelectionChanged(boolean isDisabled, List<SelectionOverride> overrides) {
  366 + this.isDisabled = isDisabled;
  367 + this.overrides = overrides;
  368 + }
  369 + }
  370 +}
... ...
app/strike/src/main/java/com/cnlive/strike/imgutil/ImgActivity.java
... ... @@ -10,7 +10,10 @@ import android.os.Message;
10 10 import android.view.View;
11 11 import android.widget.ImageView;
12 12  
  13 +import com.bumptech.glide.Glide;
  14 +import com.bumptech.glide.load.DecodeFormat;
13 15 import com.bumptech.glide.load.engine.DiskCacheStrategy;
  16 +import com.bumptech.glide.request.RequestOptions;
14 17 import com.bumptech.glide.request.transition.ViewPropertyTransition;
15 18 import com.cnlive.core.imageload.callback.ImageDownLoadCallBack;
16 19 import com.cnlive.core.imageload.config.PriorityMode;
... ... @@ -122,10 +125,13 @@ public class ImgActivity extends AppCompatActivity {
122 125 data.setShowImg(URL1);
123 126 iv_test1.setOnClickListener(view -> PreviewImageViewActivity.startPreviewPictrue(this, view, data));
124 127  
125   - ImageLoader.with(this)
126   - .url(URL1)
127   - .scale(ScaleMode.FIT_CENTER)
128   - .into(iv_test2);
  128 +// ImageLoader.with(this)
  129 +// .url(URL1)
  130 +// .scale(ScaleMode.FIT_CENTER)
  131 +// .into(iv_test2);
  132 + RequestOptions options = (new RequestOptions()).centerCrop().skipMemoryCache(true).format(DecodeFormat.PREFER_ARGB_8888).diskCacheStrategy(DiskCacheStrategy.RESOURCE);
  133 + Glide.with(this).load(URL1).apply(options).into(iv_test2);
  134 +// Glide.with(this).load(URL1).apply(new RequestOptions().centerCrop()).into(iv_test2);
129 135  
130 136 // Glide.with(this).asGif().diskCacheStrategy(DiskCacheStrategy.RESOURCE).load(URL2).into(iv_test3);
131 137  
... ...
app/strike/src/main/res/drawable-hdpi/ic_download.png 0 → 100644

199 Bytes

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

218 Bytes

app/strike/src/main/res/layout/activity_demo_mvp_main.xml
... ... @@ -93,17 +93,45 @@
93 93 android:text="动画特效" />
94 94  
95 95 <Button
  96 + android:id="@+id/btn_shanghai"
  97 + android:layout_width="match_parent"
  98 + android:layout_height="wrap_content"
  99 + android:layout_margin="5dp"
  100 + android:text="上海电商" />
  101 +
  102 + <Button
  103 + android:id="@+id/btn_uid"
  104 + android:layout_width="match_parent"
  105 + android:layout_height="wrap_content"
  106 + android:layout_margin="5dp"
  107 + android:text="ARouter获取uid" />
  108 +
  109 + <Button
96 110 android:id="@+id/btn_cnPlayer"
97 111 android:layout_width="match_parent"
98 112 android:layout_height="wrap_content"
99 113 android:layout_margin="5dp"
100 114 android:text="点播" />
  115 +
101 116 <Button
102 117 android:id="@+id/btn_livePlayer"
103 118 android:layout_width="match_parent"
104 119 android:layout_height="wrap_content"
105 120 android:layout_margin="5dp"
106 121 android:text="直播" />
  122 +
  123 + <Button
  124 + android:id="@+id/btn_exo"
  125 + android:layout_width="match_parent"
  126 + android:layout_height="wrap_content"
  127 + android:layout_margin="5dp"
  128 + android:text="Exoplayer" />
  129 + <Button
  130 + android:id="@+id/btn_exo_home"
  131 + android:layout_width="match_parent"
  132 + android:layout_height="wrap_content"
  133 + android:layout_margin="5dp"
  134 + android:text="Exoplayer官方" />
107 135 <Button
108 136 android:id="@+id/btn_pay"
109 137 android:layout_width="match_parent"
... ...
app/strike/src/main/res/layout/activity_explayer.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<layout xmlns:app="http://schemas.android.com/apk/res-auto">
  3 +
  4 + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  5 + android:layout_width="match_parent"
  6 + android:layout_height="match_parent"
  7 + android:orientation="vertical">
  8 +
  9 + <com.libs.video.video.exoplayer.CNLiveVideoView
  10 + android:id="@+id/playerView"
  11 + android:layout_width="match_parent"
  12 + android:layout_height="match_parent"
  13 + />
  14 + </LinearLayout>
  15 +</layout>
0 16 \ No newline at end of file
... ...
app/strike/src/main/res/layout/activity_img.xml
1 1 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
  2 + xmlns:lb="http://schemas.android.com/apk/res-auto"
2 3 android:layout_width="match_parent"
3   - android:layout_height="match_parent"
4   - xmlns:lb="http://schemas.android.com/apk/res-auto">
  4 + android:layout_height="match_parent">
5 5  
6 6 <LinearLayout
7 7 android:layout_width="match_parent"
... ... @@ -11,40 +11,41 @@
11 11 <TextView
12 12 android:layout_width="wrap_content"
13 13 android:layout_height="wrap_content"
14   - android:text="加载动效自定义:"
15   - />
  14 + android:text="加载动效自定义:" />
  15 +
16 16 <ImageView
17 17 android:id="@+id/iv_test1"
18 18 android:layout_width="300dp"
19 19 android:layout_height="300dp"
20 20 android:padding="10dp" />
  21 +
21 22 <TextView
22 23 android:layout_width="wrap_content"
23 24 android:layout_height="wrap_content"
24   - android:text="放大缩小:"
25   - />
26   - <ImageView
27   - android:id="@+id/iv_test2"
28   - android:layout_width="200dp"
29   - android:layout_height="200dp"
30   - android:padding="10dp" />
  25 + android:text="放大缩小:" />
  26 +
  27 + <ImageView
  28 + android:id="@+id/iv_test2"
  29 + android:layout_width="200dp"
  30 + android:layout_height="200dp"
  31 + android:padding="10dp" />
31 32  
32 33 <TextView
33 34 android:layout_width="wrap_content"
34 35 android:layout_height="wrap_content"
35   - android:text="网络gif:"
36   - />
  36 + android:text="网络gif:" />
37 37  
38 38 <ImageView
39 39 android:id="@+id/iv_test3"
40 40 android:layout_width="300dp"
41 41 android:layout_height="300dp"
42 42 android:padding="10dp" />
  43 +
43 44 <TextView
44 45 android:layout_width="wrap_content"
45 46 android:layout_height="wrap_content"
46   - android:text="mipmap下的gif:"
47   - />
  47 + android:text="mipmap下的gif:" />
  48 +
48 49 <ImageView
49 50 android:id="@+id/iv_test4"
50 51 android:layout_width="300dp"
... ... @@ -54,8 +55,8 @@
54 55 <TextView
55 56 android:layout_width="wrap_content"
56 57 android:layout_height="wrap_content"
57   - android:text="mipmap下的图片:"
58   - />
  58 + android:text="mipmap下的图片:" />
  59 +
59 60 <ImageView
60 61 android:id="@+id/iv_test5"
61 62 android:layout_width="300dp"
... ... @@ -65,8 +66,8 @@
65 66 <TextView
66 67 android:layout_width="wrap_content"
67 68 android:layout_height="wrap_content"
68   - android:text="滤镜优先级设置"
69   - />
  69 + android:text="滤镜优先级设置" />
  70 +
70 71 <ImageView
71 72 android:id="@+id/iv_test6"
72 73 android:layout_width="300dp"
... ... @@ -76,8 +77,8 @@
76 77 <TextView
77 78 android:layout_width="wrap_content"
78 79 android:layout_height="wrap_content"
79   - android:text="素描"
80   - />
  80 + android:text="素描" />
  81 +
81 82 <ImageView
82 83 android:id="@+id/iv_test7"
83 84 android:layout_width="300dp"
... ... @@ -87,8 +88,7 @@
87 88 <TextView
88 89 android:layout_width="wrap_content"
89 90 android:layout_height="wrap_content"
90   - android:text="加载asserts下面的图片"
91   - />
  91 + android:text="加载asserts下面的图片" />
92 92  
93 93 <ImageView
94 94 android:id="@+id/iv_test8"
... ... @@ -99,8 +99,8 @@
99 99 <TextView
100 100 android:layout_width="wrap_content"
101 101 android:layout_height="wrap_content"
102   - android:text="加载raw下面的图片为圆"
103   - />
  102 + android:text="加载raw下面的图片为圆" />
  103 +
104 104 <ImageView
105 105 android:id="@+id/iv_test9"
106 106 android:layout_width="300dp"
... ... @@ -110,8 +110,8 @@
110 110 <TextView
111 111 android:layout_width="wrap_content"
112 112 android:layout_height="wrap_content"
113   - android:text="图片设置圆角"
114   - />
  113 + android:text="图片设置圆角" />
  114 +
115 115 <ImageView
116 116 android:id="@+id/iv_test10"
117 117 android:layout_width="300dp"
... ... @@ -121,13 +121,13 @@
121 121 <TextView
122 122 android:layout_width="wrap_content"
123 123 android:layout_height="wrap_content"
124   - android:text="图片下载显示"
125   - />
  124 + android:text="图片下载显示" />
  125 +
126 126 <Button
127 127 android:id="@+id/down"
128 128 android:layout_width="wrap_content"
129 129 android:layout_height="wrap_content"
130   - android:text="下载"/>
  130 + android:text="下载" />
131 131  
132 132 <ImageView
133 133 android:id="@+id/iv_test11"
... ... @@ -138,45 +138,42 @@
138 138 <TextView
139 139 android:layout_width="wrap_content"
140 140 android:layout_height="wrap_content"
141   - android:text="直接代码设置,五边形"/>
  141 + android:text="直接代码设置,五边形" />
142 142  
143 143 <com.cnlive.core.imageload.util.MultiView
144 144 android:layout_width="300dp"
145 145 android:layout_height="300dp"
146 146 android:layout_margin="30dp"
147   - lb:angleCount="5"
148   - lb:type="multi"
149 147 android:src="@mipmap/timgs"
150   - />
  148 + lb:angleCount="5"
  149 + lb:shapeType="polygon" />
151 150  
152 151 <TextView
153 152 android:layout_width="wrap_content"
154 153 android:layout_height="wrap_content"
155   - android:text="直接代码设置,设置圆角"/>
  154 + android:text="直接代码设置,设置圆角" />
156 155  
157 156 <com.cnlive.core.imageload.util.MultiView
158 157 android:layout_width="200dp"
159 158 android:layout_height="200dp"
160   - android:layout_margin="30dp"
161 159 android:layout_gravity="center"
162   - lb:borderRadius="10dp"
163   - lb:type="round"
  160 + android:layout_margin="30dp"
164 161 android:src="@mipmap/timgs"
165   - />
  162 + lb:borderRadius="10dp"
  163 + lb:shapeType="round" />
166 164  
167 165 <TextView
168 166 android:layout_width="wrap_content"
169 167 android:layout_height="wrap_content"
170   - android:text="直接代码设置圆"/>
  168 + android:text="直接代码设置圆" />
171 169  
172 170 <com.cnlive.core.imageload.util.MultiView
173   - android:layout_margin="30dp"
174 171 android:layout_width="200dp"
175 172 android:layout_height="200dp"
176 173 android:layout_gravity="center"
  174 + android:layout_margin="30dp"
177 175 android:src="@mipmap/timgs"
178   - lb:type="circle"
179   - />
  176 + lb:shapeType="circle" />
180 177  
181 178 </LinearLayout>
182 179 </ScrollView>
... ...
app/strike/src/main/res/layout/player_activity.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!-- Copyright (C) 2016 The Android Open Source Project
  3 +
  4 + Licensed under the Apache License, Version 2.0 (the "License");
  5 + you may not use this file except in compliance with the License.
  6 + You may obtain a copy of the License at
  7 +
  8 + http://www.apache.org/licenses/LICENSE-2.0
  9 +
  10 + Unless required by applicable law or agreed to in writing, software
  11 + distributed under the License is distributed on an "AS IS" BASIS,
  12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + See the License for the specific language governing permissions and
  14 + limitations under the License.
  15 +-->
  16 +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  17 + xmlns:tools="http://schemas.android.com/tools"
  18 + android:id="@+id/root"
  19 + android:layout_width="match_parent"
  20 + android:layout_height="match_parent"
  21 + android:keepScreenOn="true">
  22 +
  23 + <com.google.android.exoplayer2.ui.PlayerView android:id="@+id/player_view"
  24 + android:layout_width="match_parent"
  25 + android:layout_height="match_parent"/>
  26 +
  27 + <LinearLayout
  28 + android:layout_width="match_parent"
  29 + android:layout_height="wrap_content"
  30 + android:background="#88000000"
  31 + android:orientation="vertical">
  32 +
  33 + <TextView android:id="@+id/debug_text_view"
  34 + android:layout_width="match_parent"
  35 + android:layout_height="wrap_content"
  36 + android:paddingLeft="4dp"
  37 + android:paddingRight="4dp"
  38 + android:textSize="10sp"
  39 + tools:ignore="SmallSp"/>
  40 +
  41 + <LinearLayout android:id="@+id/controls_root"
  42 + android:layout_width="match_parent"
  43 + android:layout_height="wrap_content"
  44 + android:orientation="horizontal"
  45 + android:visibility="gone">
  46 +
  47 + <Button android:id="@+id/select_tracks_button"
  48 + android:layout_width="wrap_content"
  49 + android:layout_height="wrap_content"
  50 + android:text="@string/track_selection_title"
  51 + android:enabled="false"/>
  52 +
  53 + </LinearLayout>
  54 +
  55 + </LinearLayout>
  56 +
  57 +</FrameLayout>
... ...
app/strike/src/main/res/layout/sample_chooser_activity.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!-- Copyright (C) 2016 The Android Open Source Project
  3 +
  4 + Licensed under the Apache License, Version 2.0 (the "License");
  5 + you may not use this file except in compliance with the License.
  6 + You may obtain a copy of the License at
  7 +
  8 + http://www.apache.org/licenses/LICENSE-2.0
  9 +
  10 + Unless required by applicable law or agreed to in writing, software
  11 + distributed under the License is distributed on an "AS IS" BASIS,
  12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + See the License for the specific language governing permissions and
  14 + limitations under the License.
  15 +-->
  16 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  17 + android:layout_width="match_parent"
  18 + android:layout_height="match_parent"
  19 + android:orientation="vertical">
  20 +
  21 + <ExpandableListView android:id="@+id/sample_list"
  22 + android:layout_width="match_parent"
  23 + android:layout_height="match_parent"/>
  24 +
  25 +</LinearLayout>
... ...
app/strike/src/main/res/layout/sample_list_item.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<!-- Copyright (C) 2018 The Android Open Source Project
  3 +
  4 + Licensed under the Apache License, Version 2.0 (the "License");
  5 + you may not use this file except in compliance with the License.
  6 + You may obtain a copy of the License at
  7 +
  8 + http://www.apache.org/licenses/LICENSE-2.0
  9 +
  10 + Unless required by applicable law or agreed to in writing, software
  11 + distributed under the License is distributed on an "AS IS" BASIS,
  12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + See the License for the specific language governing permissions and
  14 + limitations under the License.
  15 +-->
  16 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  17 + android:layout_width="match_parent"
  18 + android:layout_height="wrap_content"
  19 + android:paddingStart="12dp"
  20 + android:paddingEnd="12dp"
  21 + android:gravity="center_vertical"
  22 + android:orientation="horizontal">
  23 +
  24 + <TextView android:id="@+id/sample_title"
  25 + android:layout_width="0dp"
  26 + android:layout_height="wrap_content"
  27 + android:layout_weight="1"
  28 + android:gravity="center_vertical"
  29 + android:minHeight="?android:attr/listPreferredItemHeightSmall"
  30 + android:textAppearance="?android:attr/textAppearanceListItemSmall"/>
  31 +
  32 + <ImageButton android:id="@+id/download_button"
  33 + android:layout_width="wrap_content"
  34 + android:layout_height="wrap_content"
  35 + android:contentDescription="@string/exo_download_description"
  36 + android:background="@android:color/transparent"/>
  37 +
  38 +</LinearLayout>
... ...
app/strike/src/main/res/layout/track_selection_dialog.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<!-- Copyright (C) 2018 The Android Open Source Project
  3 +
  4 + Licensed under the Apache License, Version 2.0 (the "License");
  5 + you may not use this file except in compliance with the License.
  6 + You may obtain a copy of the License at
  7 +
  8 + http://www.apache.org/licenses/LICENSE-2.0
  9 +
  10 + Unless required by applicable law or agreed to in writing, software
  11 + distributed under the License is distributed on an "AS IS" BASIS,
  12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + See the License for the specific language governing permissions and
  14 + limitations under the License.
  15 +-->
  16 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  17 + xmlns:app="http://schemas.android.com/apk/res-auto"
  18 + android:orientation="vertical"
  19 + android:layout_width="match_parent"
  20 + android:layout_height="match_parent">
  21 +
  22 + <androidx.viewpager.widget.ViewPager
  23 + android:id="@+id/track_selection_dialog_view_pager"
  24 + android:layout_width="match_parent"
  25 + android:layout_height="0dp"
  26 + android:layout_weight="1">
  27 +
  28 + <com.google.android.material.tabs.TabLayout
  29 + android:id="@+id/track_selection_dialog_tab_layout"
  30 + android:layout_width="match_parent"
  31 + android:layout_height="wrap_content"
  32 + app:tabGravity="fill"
  33 + app:tabMode="fixed"/>
  34 +
  35 + </androidx.viewpager.widget.ViewPager>
  36 +
  37 + <LinearLayout
  38 + android:orientation="horizontal"
  39 + android:layout_width="match_parent"
  40 + android:layout_height="wrap_content"
  41 + android:gravity="end">
  42 +
  43 + <Button
  44 + android:id="@+id/track_selection_dialog_cancel_button"
  45 + android:layout_width="wrap_content"
  46 + android:layout_height="wrap_content"
  47 + android:text="@android:string/cancel"
  48 + style="?android:attr/borderlessButtonStyle"/>
  49 +
  50 + <Button
  51 + android:id="@+id/track_selection_dialog_ok_button"
  52 + android:layout_width="wrap_content"
  53 + android:layout_height="wrap_content"
  54 + android:text="@android:string/ok"
  55 + style="?android:attr/borderlessButtonStyle"/>
  56 +
  57 + </LinearLayout>
  58 +
  59 +</LinearLayout>
... ...
app/strike/src/main/res/menu/sample_chooser_menu.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<!-- Copyright (C) 2018 The Android Open Source Project
  3 +
  4 + Licensed under the Apache License, Version 2.0 (the "License");
  5 + you may not use this file except in compliance with the License.
  6 + You may obtain a copy of the License at
  7 +
  8 + http://www.apache.org/licenses/LICENSE-2.0
  9 +
  10 + Unless required by applicable law or agreed to in writing, software
  11 + distributed under the License is distributed on an "AS IS" BASIS,
  12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + See the License for the specific language governing permissions and
  14 + limitations under the License.
  15 +-->
  16 +<menu xmlns:android="http://schemas.android.com/apk/res/android"
  17 + xmlns:app="http://schemas.android.com/apk/res-auto">
  18 + <item android:id="@+id/prefer_extension_decoders"
  19 + android:title="@string/prefer_extension_decoders"
  20 + android:checkable="true"
  21 + app:showAsAction="never"/>
  22 + <item android:id="@+id/random_abr"
  23 + android:title="@string/random_abr"
  24 + android:checkable="true"
  25 + app:showAsAction="never"/>
  26 + <item android:id="@+id/tunneling"
  27 + android:title="@string/tunneling"
  28 + android:checkable="true"
  29 + app:showAsAction="never"/>
  30 +</menu>
... ...
app/strike/src/main/res/values/strings.xml
1   -<resources>
2   - <!-- <string name="app_name">strike</string>-->
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<!-- Copyright (C) 2016 The Android Open Source Project
  3 +
  4 + Licensed under the Apache License, Version 2.0 (the "License");
  5 + you may not use this file except in compliance with the License.
  6 + You may obtain a copy of the License at
  7 +
  8 + http://www.apache.org/licenses/LICENSE-2.0
  9 +
  10 + Unless required by applicable law or agreed to in writing, software
  11 + distributed under the License is distributed on an "AS IS" BASIS,
  12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + See the License for the specific language governing permissions and
  14 + limitations under the License.
  15 +-->
  16 +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  17 +
  18 + <string name="application_name">ExoPlayer</string>
  19 +
  20 + <string name="track_selection_title">Select tracks</string>
  21 +
  22 + <string name="unexpected_intent_action">Unexpected intent action: <xliff:g id="action">%1$s</xliff:g></string>
  23 +
  24 + <string name="error_cleartext_not_permitted">Cleartext traffic not permitted</string>
  25 +
  26 + <string name="error_generic">Playback failed</string>
  27 +
  28 + <string name="error_unrecognized_abr_algorithm">Unrecognized ABR algorithm</string>
  29 +
  30 + <string name="error_unrecognized_stereo_mode">Unrecognized stereo mode</string>
  31 +
  32 + <string name="error_drm_unsupported_before_api_18">Protected content not supported on API levels below 18</string>
  33 +
  34 + <string name="error_drm_unsupported_scheme">This device does not support the required DRM scheme</string>
  35 +
  36 + <string name="error_drm_unknown">An unknown DRM error occurred</string>
  37 +
  38 + <string name="error_no_decoder">This device does not provide a decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
  39 +
  40 + <string name="error_no_secure_decoder">This device does not provide a secure decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
  41 +
  42 + <string name="error_querying_decoders">Unable to query device decoders</string>
  43 +
  44 + <string name="error_instantiating_decoder">Unable to instantiate decoder <xliff:g id="decoder_name">%1$s</xliff:g></string>
  45 +
  46 + <string name="error_unsupported_video">Media includes video tracks, but none are playable by this device</string>
  47 +
  48 + <string name="error_unsupported_audio">Media includes audio tracks, but none are playable by this device</string>
  49 +
  50 + <string name="storage_permission_denied">Permission to access storage was denied</string>
  51 +
  52 + <string name="sample_list_load_error">One or more sample lists failed to load</string>
  53 +
  54 + <string name="ima_not_loaded">Playing sample without ads, as the IMA extension was not loaded</string>
  55 +
  56 + <string name="unsupported_ads_in_concatenation">Playing sample without ads, as ads are not supported in concatenations</string>
  57 +
  58 + <string name="download_start_error">Failed to start download</string>
  59 +
  60 + <string name="download_playlist_unsupported">This demo app does not support downloading playlists</string>
  61 +
  62 + <string name="download_drm_unsupported">This demo app does not support downloading protected content</string>
  63 +
  64 + <string name="download_scheme_unsupported">This demo app only supports downloading http streams</string>
  65 +
  66 + <string name="download_live_unsupported">This demo app does not support downloading live content</string>
  67 +
  68 + <string name="download_ads_unsupported">IMA does not support offline ads</string>
  69 +
  70 + <string name="prefer_extension_decoders">Prefer extension decoders</string>
  71 +
  72 + <string name="random_abr">Enable random ABR</string>
  73 +
  74 + <string name="tunneling">Request multimedia tunneling</string>
3 75  
4 76 </resources>
... ...
app/strike/src/main/res/values/styles.xml
... ... @@ -9,4 +9,17 @@
9 9 <!-- <item name="colorAccent">@color/colorAccent</item>-->
10 10 </style>
11 11  
  12 +
  13 +
  14 + <style name="TrackSelectionDialogThemeOverlay" parent="ThemeOverlay.AppCompat.Dialog.Alert">
  15 + <item name="windowNoTitle">false</item>
  16 + </style>
  17 +
  18 + <style name="PlayerTheme" parent="Theme.AppCompat.NoActionBar">
  19 + <item name="android:windowBackground">@android:color/black</item>
  20 + </style>
  21 +
  22 + <style name="PlayerTheme.Spherical">
  23 + <item name="surface_type">spherical_gl_surface_view</item>
  24 + </style>
12 25 </resources>
... ...
app/strike/src/main/res/xml/app_files.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <paths>
  4 + <external-path
  5 + name="external_files"
  6 + path="" />
  7 +
  8 + <path>
  9 + <root-path
  10 + name="root_path"
  11 + path="." />
  12 + </path>
  13 +
  14 + </paths>
  15 +</resources>
0 16 \ No newline at end of file
... ...
build.gradle
... ... @@ -27,6 +27,12 @@ allprojects {
27 27 // configurations.all {
28 28 ////// resolutionStrategy.force 'androidx.appcompat:appcompat:1.1.0'
29 29 // }
  30 + //添加java编译指令,防止引用时,形参变成var1
  31 + gradle.projectsEvaluated {
  32 + tasks.withType(JavaCompile) {
  33 + options.compilerArgs.add('-parameters')
  34 + }
  35 + }
30 36 afterEvaluate {
31 37 if (project.parent == null || project.parent == rootProject) return
32 38  
... ... @@ -129,6 +135,10 @@ allprojects {
129 135 url maven.mixedUrl
130 136 credentials { username = nexusId; password = nexusPw }
131 137 }
  138 + maven {
  139 + url 'http://android.nexus.cnlive.com/repository/cnlive-shop/'
  140 + credentials { username = 'shop'; password = 'ShangHa1' }
  141 + }
132 142 //------------------ 资源库配置 END------------------
133 143 maven { url 'https://dl.bintray.com/umsdk/release' }
134 144 //关联微博
... ... @@ -139,6 +149,7 @@ allprojects {
139 149 maven {
140 150 url 'https://dl.bintray.com/granzon/cnliveLibs'
141 151 }
  152 +
142 153 maven {
143 154 url "https://jitpack.io"
144 155 }
... ...
cloud/user/src/main/java/com/cnlive/cloud/user/router/user/CNLiveUserServiceImpl.java
... ... @@ -6,6 +6,7 @@ import android.text.TextUtils;
6 6 import com.alibaba.android.arouter.facade.annotation.Route;
7 7 import com.alibaba.android.arouter.launcher.ARouter;
8 8 import com.cnlive.cloud.user.auth.UserService;
  9 +import com.cnlive.core.libs.base.arouter.RouterConst;
9 10 import com.cnlive.core.libs.base.arouter.user.CNLiveUserService;
10 11 import com.cnlive.core.libs.base.arouter.user.OpenUserInfo;
11 12  
... ... @@ -14,7 +15,7 @@ import com.cnlive.core.libs.base.arouter.user.OpenUserInfo;
14 15 *
15 16 * @author ys
16 17 */
17   -@Route(path = CNLiveUserService.PATH, name = CNLiveUserService.NAME)
  18 +@Route(path = RouterConst.OPEN_USER_SERVICE_PATH, name = RouterConst.OPEN_USER_SERVICE_NAME)
18 19 public class CNLiveUserServiceImpl implements CNLiveUserService {
19 20  
20 21 @Override
... ...
cloud/video/build.gradle
... ... @@ -13,4 +13,6 @@ dependencies {
13 13 implementation "com.umeng.umsdk:analytics:8.0.0"
14 14 implementation "com.umeng.umsdk:common:2.0.0"
15 15 implementation project(path: ':core:network')
  16 + api 'com.google.android.exoplayer:exoplayer:2.11.0'
  17 + api 'com.google.android.exoplayer:extension-rtmp:2.11.0'
16 18 }
... ...
cloud/video/src/main/AndroidManifest.xml
1 1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2   - package="com.cnlive.cloud.video" />
  2 + package="com.cnlive.cloud.video">
  3 + <application>
  4 + </application>
  5 +
  6 +</manifest>
... ...
cloud/video/src/main/assets/media.exolist.json 0 → 100644
  1 +[
  2 + {
  3 + "name": "YouTube DASH",
  4 + "samples": [
  5 + {
  6 + "name": "Google Glass (MP4,H264)",
  7 + "uri": "https://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0",
  8 + "extension": "mpd"
  9 + },
  10 + {
  11 + "name": "Google Play (MP4,H264)",
  12 + "uri": "https://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=A2716F75795F5D2AF0E88962FFCD10DB79384F29.84308FF04844498CE6FBCE4731507882B8307798&key=ik0",
  13 + "extension": "mpd"
  14 + },
  15 + {
  16 + "name": "Google Glass (WebM,VP9)",
  17 + "uri": "https://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=249B04F79E984D7F86B4D8DB48AE6FAF41C17AB3.7B9F0EC0505E1566E59B8E488E9419F253DDF413&key=ik0",
  18 + "extension": "mpd"
  19 + },
  20 + {
  21 + "name": "Google Play (WebM,VP9)",
  22 + "uri": "https://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=B1C2A74783AC1CC4865EB312D7DD2D48230CC9FD.BD153B9882175F1F94BFE5141A5482313EA38E8D&key=ik0",
  23 + "extension": "mpd"
  24 + }
  25 + ]
  26 + },
  27 + {
  28 + "name": "Widevine DASH Policy Tests (GTS)",
  29 + "samples": [
  30 + {
  31 + "name": "WV: HDCP not specified",
  32 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  33 + "drm_scheme": "widevine",
  34 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=d286538032258a1c&provider=widevine_test"
  35 + },
  36 + {
  37 + "name": "WV: HDCP not required",
  38 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  39 + "drm_scheme": "widevine",
  40 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=48fcc369939ac96c&provider=widevine_test"
  41 + },
  42 + {
  43 + "name": "WV: HDCP required",
  44 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  45 + "drm_scheme": "widevine",
  46 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=e06c39f1151da3df&provider=widevine_test"
  47 + },
  48 + {
  49 + "name": "WV: Secure video path required (MP4,H264)",
  50 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  51 + "drm_scheme": "widevine",
  52 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=0894c7c8719b28a0&provider=widevine_test"
  53 + },
  54 + {
  55 + "name": "WV: Secure video path required (WebM,VP9)",
  56 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd",
  57 + "drm_scheme": "widevine",
  58 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=0894c7c8719b28a0&provider=widevine_test"
  59 + },
  60 + {
  61 + "name": "WV: Secure video path required (MP4,H265)",
  62 + "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears.mpd",
  63 + "drm_scheme": "widevine",
  64 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=0894c7c8719b28a0&provider=widevine_test"
  65 + },
  66 + {
  67 + "name": "WV: HDCP + secure video path required",
  68 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  69 + "drm_scheme": "widevine",
  70 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=efd045b1eb61888a&provider=widevine_test"
  71 + },
  72 + {
  73 + "name": "WV: 30s license duration (fails at ~30s)",
  74 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  75 + "drm_scheme": "widevine",
  76 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=f9a34cab7b05881a&provider=widevine_test"
  77 + }
  78 + ]
  79 + },
  80 + {
  81 + "name": "Widevine HDCP Capabilities Tests",
  82 + "samples": [
  83 + {
  84 + "name": "WV: HDCP: None (not required)",
  85 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  86 + "drm_scheme": "widevine",
  87 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=HDCP_None&provider=widevine_test"
  88 + },
  89 + {
  90 + "name": "WV: HDCP: 1.0 required",
  91 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  92 + "drm_scheme": "widevine",
  93 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=HDCP_V1&provider=widevine_test"
  94 + },
  95 + {
  96 + "name": "WV: HDCP: 2.0 required",
  97 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  98 + "drm_scheme": "widevine",
  99 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=HDCP_V2&provider=widevine_test"
  100 + },
  101 + {
  102 + "name": "WV: HDCP: 2.1 required",
  103 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  104 + "drm_scheme": "widevine",
  105 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=HDCP_V2_1&provider=widevine_test"
  106 + },
  107 + {
  108 + "name": "WV: HDCP: 2.2 required",
  109 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  110 + "drm_scheme": "widevine",
  111 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=HDCP_V2_2&provider=widevine_test"
  112 + },
  113 + {
  114 + "name": "WV: HDCP: No digital output",
  115 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  116 + "drm_scheme": "widevine",
  117 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=HDCP_NO_DIGTAL_OUTPUT&provider=widevine_test"
  118 + }
  119 + ]
  120 + },
  121 + {
  122 + "name": "Widevine DASH: MP4,H264",
  123 + "samples": [
  124 + {
  125 + "name": "WV: Clear SD & HD (MP4,H264)",
  126 + "uri": "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd"
  127 + },
  128 + {
  129 + "name": "WV: Clear SD (MP4,H264)",
  130 + "uri": "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears_sd.mpd"
  131 + },
  132 + {
  133 + "name": "WV: Clear HD (MP4,H264)",
  134 + "uri": "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears_hd.mpd"
  135 + },
  136 + {
  137 + "name": "WV: Clear UHD (MP4,H264)",
  138 + "uri": "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears_uhd.mpd"
  139 + },
  140 + {
  141 + "name": "WV: Secure SD & HD (cenc,MP4,H264)",
  142 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
  143 + "drm_scheme": "widevine",
  144 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  145 + },
  146 + {
  147 + "name": "WV: Secure SD (cenc,MP4,H264)",
  148 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd",
  149 + "drm_scheme": "widevine",
  150 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  151 + },
  152 + {
  153 + "name": "WV: Secure HD (cenc,MP4,H264)",
  154 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_hd.mpd",
  155 + "drm_scheme": "widevine",
  156 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  157 + },
  158 + {
  159 + "name": "WV: Secure UHD (cenc,MP4,H264)",
  160 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_uhd.mpd",
  161 + "drm_scheme": "widevine",
  162 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  163 + },
  164 + {
  165 + "name": "WV: Secure SD & HD (cbc1,MP4,H264)",
  166 + "uri": "https://storage.googleapis.com/wvmedia/cbc1/h264/tears/tears_aes_cbc1.mpd",
  167 + "drm_scheme": "widevine",
  168 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  169 + },
  170 + {
  171 + "name": "WV: Secure SD (cbc1,MP4,H264)",
  172 + "uri": "https://storage.googleapis.com/wvmedia/cbc1/h264/tears/tears_aes_cbc1_sd.mpd",
  173 + "drm_scheme": "widevine",
  174 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  175 + },
  176 + {
  177 + "name": "WV: Secure HD (cbc1,MP4,H264)",
  178 + "uri": "https://storage.googleapis.com/wvmedia/cbc1/h264/tears/tears_aes_cbc1_hd.mpd",
  179 + "drm_scheme": "widevine",
  180 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  181 + },
  182 + {
  183 + "name": "WV: Secure UHD (cbc1,MP4,H264)",
  184 + "uri": "https://storage.googleapis.com/wvmedia/cbc1/h264/tears/tears_aes_cbc1_uhd.mpd",
  185 + "drm_scheme": "widevine",
  186 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  187 + },
  188 + {
  189 + "name": "WV: Secure SD & HD (cbcs,MP4,H264)",
  190 + "uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd",
  191 + "drm_scheme": "widevine",
  192 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  193 + },
  194 + {
  195 + "name": "WV: Secure SD (cbcs,MP4,H264)",
  196 + "uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_sd.mpd",
  197 + "drm_scheme": "widevine",
  198 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  199 + },
  200 + {
  201 + "name": "WV: Secure HD (cbcs,MP4,H264)",
  202 + "uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_hd.mpd",
  203 + "drm_scheme": "widevine",
  204 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  205 + },
  206 + {
  207 + "name": "WV: Secure UHD (cbcs,MP4,H264)",
  208 + "uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_uhd.mpd",
  209 + "drm_scheme": "widevine",
  210 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  211 + }
  212 + ]
  213 + },
  214 + {
  215 + "name": "Widevine DASH: WebM,VP9",
  216 + "samples": [
  217 + {
  218 + "name": "WV: Clear SD & HD (WebM,VP9)",
  219 + "uri": "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears.mpd"
  220 + },
  221 + {
  222 + "name": "WV: Clear SD (WebM,VP9)",
  223 + "uri": "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears_sd.mpd"
  224 + },
  225 + {
  226 + "name": "WV: Clear HD (WebM,VP9)",
  227 + "uri": "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears_hd.mpd"
  228 + },
  229 + {
  230 + "name": "WV: Clear UHD (WebM,VP9)",
  231 + "uri": "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears_uhd.mpd"
  232 + },
  233 + {
  234 + "name": "WV: Secure Fullsample SD & HD (WebM,VP9)",
  235 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd",
  236 + "drm_scheme": "widevine",
  237 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  238 + },
  239 + {
  240 + "name": "WV: Secure Fullsample SD (WebM,VP9)",
  241 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_sd.mpd",
  242 + "drm_scheme": "widevine",
  243 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  244 + },
  245 + {
  246 + "name": "WV: Secure Fullsample HD (WebM,VP9)",
  247 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_hd.mpd",
  248 + "drm_scheme": "widevine",
  249 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  250 + },
  251 + {
  252 + "name": "WV: Secure Fullsample UHD (WebM,VP9)",
  253 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_uhd.mpd",
  254 + "drm_scheme": "widevine",
  255 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  256 + },
  257 + {
  258 + "name": "WV: Secure Subsample SD & HD (WebM,VP9)",
  259 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears.mpd",
  260 + "drm_scheme": "widevine",
  261 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  262 + },
  263 + {
  264 + "name": "WV: Secure Subsample SD (WebM,VP9)",
  265 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_sd.mpd",
  266 + "drm_scheme": "widevine",
  267 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  268 + },
  269 + {
  270 + "name": "WV: Secure Subsample HD (WebM,VP9)",
  271 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_hd.mpd",
  272 + "drm_scheme": "widevine",
  273 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  274 + },
  275 + {
  276 + "name": "WV: Secure Subsample UHD (WebM,VP9)",
  277 + "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_uhd.mpd",
  278 + "drm_scheme": "widevine",
  279 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  280 + }
  281 + ]
  282 + },
  283 + {
  284 + "name": "Widevine DASH: MP4,H265",
  285 + "samples": [
  286 + {
  287 + "name": "WV: Clear SD & HD (MP4,H265)",
  288 + "uri": "https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears.mpd"
  289 + },
  290 + {
  291 + "name": "WV: Clear SD (MP4,H265)",
  292 + "uri": "https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears_sd.mpd"
  293 + },
  294 + {
  295 + "name": "WV: Clear HD (MP4,H265)",
  296 + "uri": "https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears_hd.mpd"
  297 + },
  298 + {
  299 + "name": "WV: Clear UHD (MP4,H265)",
  300 + "uri": "https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears_uhd.mpd"
  301 + },
  302 + {
  303 + "name": "WV: Secure SD & HD (MP4,H265)",
  304 + "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears.mpd",
  305 + "drm_scheme": "widevine",
  306 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  307 + },
  308 + {
  309 + "name": "WV: Secure SD (MP4,H265)",
  310 + "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_sd.mpd",
  311 + "drm_scheme": "widevine",
  312 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  313 + },
  314 + {
  315 + "name": "WV: Secure HD (MP4,H265)",
  316 + "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_hd.mpd",
  317 + "drm_scheme": "widevine",
  318 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  319 + },
  320 + {
  321 + "name": "WV: Secure UHD (MP4,H265)",
  322 + "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_uhd.mpd",
  323 + "drm_scheme": "widevine",
  324 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  325 + }
  326 + ]
  327 + },
  328 + {
  329 + "name": "SmoothStreaming",
  330 + "samples": [
  331 + {
  332 + "name": "Super speed",
  333 + "uri": "https://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/Manifest"
  334 + },
  335 + {
  336 + "name": "Super speed (PlayReady)",
  337 + "uri": "https://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism/Manifest",
  338 + "drm_scheme": "playready"
  339 + }
  340 + ]
  341 + },
  342 + {
  343 + "name": "HLS",
  344 + "samples": [
  345 + {
  346 + "name": "Apple 4x3 basic stream",
  347 + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"
  348 + },
  349 + {
  350 + "name": "Apple 16x9 basic stream",
  351 + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
  352 + },
  353 + {
  354 + "name": "Apple master playlist advanced (TS)",
  355 + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8"
  356 + },
  357 + {
  358 + "name": "Apple master playlist advanced (fMP4)",
  359 + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8"
  360 + },
  361 + {
  362 + "name": "Apple TS media playlist",
  363 + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8"
  364 + },
  365 + {
  366 + "name": "Apple AAC media playlist",
  367 + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear0/prog_index.m3u8"
  368 + }
  369 + ]
  370 + },
  371 + {
  372 + "name": "Misc",
  373 + "samples": [
  374 + {
  375 + "name": "Dizzy (MP4)",
  376 + "uri": "https://html5demos.com/assets/dizzy.mp4"
  377 + },
  378 + {
  379 + "name": "Apple 10s (AAC)",
  380 + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
  381 + },
  382 + {
  383 + "name": "Apple 10s (TS)",
  384 + "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts"
  385 + },
  386 + {
  387 + "name": "Android screens (MKV)",
  388 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv"
  389 + },
  390 + {
  391 + "name": "Screens 360p video (WebM,VP9)",
  392 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/video-vp9-360.webm"
  393 + },
  394 + {
  395 + "name": "Screens 480p video (FMP4,H264)",
  396 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/video-avc-baseline-480.mp4"
  397 + },
  398 + {
  399 + "name": "Screens 1080p video (FMP4,H264)",
  400 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/video-137.mp4"
  401 + },
  402 + {
  403 + "name": "Screens audio (FMP4)",
  404 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4"
  405 + },
  406 + {
  407 + "name": "Google Play (MP3)",
  408 + "uri": "https://storage.googleapis.com/exoplayer-test-media-0/play.mp3"
  409 + },
  410 + {
  411 + "name": "Google Play (Ogg/Vorbis)",
  412 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/ogg/play.ogg"
  413 + },
  414 + {
  415 + "name": "Big Buck Bunny video (FLV)",
  416 + "uri": "https://vod.leasewebcdn.com/bbb.flv?ri=1024&rs=150&start=0"
  417 + },
  418 + {
  419 + "name": "Big Buck Bunny 480p video (MP4,AV1)",
  420 + "uri": "https://storage.googleapis.com/downloads.webmproject.org/av1/exoplayer/bbb-av1-480p.mp4"
  421 + }
  422 + ]
  423 + },
  424 + {
  425 + "name": "Playlists",
  426 + "samples": [
  427 + {
  428 + "name": "Cats -> Dogs",
  429 + "playlist": [
  430 + {
  431 + "uri": "https://html5demos.com/assets/dizzy.mp4"
  432 + },
  433 + {
  434 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv"
  435 + }
  436 + ]
  437 + },
  438 + {
  439 + "name": "Audio -> Video -> Audio",
  440 + "playlist": [
  441 + {
  442 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4"
  443 + },
  444 + {
  445 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv"
  446 + },
  447 + {
  448 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4"
  449 + }
  450 + ]
  451 + },
  452 + {
  453 + "name": "Clear -> Enc -> Clear -> Enc -> Enc",
  454 + "playlist": [
  455 + {
  456 + "uri": "https://html5demos.com/assets/dizzy.mp4"
  457 + },
  458 + {
  459 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd",
  460 + "drm_scheme": "widevine",
  461 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  462 + },
  463 + {
  464 + "uri": "https://html5demos.com/assets/dizzy.mp4"
  465 + },
  466 + {
  467 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd",
  468 + "drm_scheme": "widevine",
  469 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  470 + },
  471 + {
  472 + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd",
  473 + "drm_scheme": "widevine",
  474 + "drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
  475 + }
  476 + ]
  477 + }
  478 + ]
  479 + },
  480 + {
  481 + "name": "IMA sample ad tags",
  482 + "samples": [
  483 + {
  484 + "name": "Single inline linear",
  485 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  486 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator="
  487 + },
  488 + {
  489 + "name": "Single skippable inline",
  490 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  491 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dskippablelinear&correlator="
  492 + },
  493 + {
  494 + "name": "Single redirect linear",
  495 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  496 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dredirectlinear&correlator="
  497 + },
  498 + {
  499 + "name": "Single redirect error",
  500 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  501 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dredirecterror&nofb=1&correlator="
  502 + },
  503 + {
  504 + "name": "Single redirect broken (fallback)",
  505 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  506 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dredirecterror&correlator="
  507 + },
  508 + {
  509 + "name": "VMAP pre-roll",
  510 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  511 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpreonly&cmsid=496&vid=short_onecue&correlator="
  512 + },
  513 + {
  514 + "name": "VMAP pre-roll + bumper",
  515 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  516 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpreonlybumper&cmsid=496&vid=short_onecue&correlator="
  517 + },
  518 + {
  519 + "name": "VMAP post-roll",
  520 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  521 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpostonly&cmsid=496&vid=short_onecue&correlator="
  522 + },
  523 + {
  524 + "name": "VMAP post-roll + bumper",
  525 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  526 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpostonlybumper&cmsid=496&vid=short_onecue&correlator="
  527 + },
  528 + {
  529 + "name": "VMAP pre-, mid- and post-rolls, single ads",
  530 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  531 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator="
  532 + },
  533 + {
  534 + "name": "VMAP pre-roll single ad, mid-roll standard pod with 3 ads, post-roll single ad",
  535 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  536 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpostpod&cmsid=496&vid=short_onecue&correlator="
  537 + },
  538 + {
  539 + "name": "VMAP pre-roll single ad, mid-roll optimized pod with 3 ads, post-roll single ad",
  540 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  541 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpostoptimizedpod&cmsid=496&vid=short_onecue&correlator="
  542 + },
  543 + {
  544 + "name": "VMAP pre-roll single ad, mid-roll standard pod with 3 ads, post-roll single ad (bumpers around all ad breaks)",
  545 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  546 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpostpodbumper&cmsid=496&vid=short_onecue&correlator="
  547 + },
  548 + {
  549 + "name": "VMAP pre-roll single ad, mid-roll optimized pod with 3 ads, post-roll single ad (bumpers around all ad breaks)",
  550 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  551 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpostoptimizedpodbumper&cmsid=496&vid=short_onecue&correlator="
  552 + },
  553 + {
  554 + "name": "VMAP pre-roll single ad, mid-roll standard pods with 5 ads every 10 seconds for 1:40, post-roll single ad",
  555 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  556 + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpostlongpod&cmsid=496&vid=short_tencue&correlator="
  557 + },
  558 + {
  559 + "name": "VMAP empty midroll",
  560 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  561 + "ad_tag_uri": "https://vastsynthesizer.appspot.com/empty-midroll"
  562 + },
  563 + {
  564 + "name": "VMAP full, empty, full midrolls",
  565 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
  566 + "ad_tag_uri": "https://vastsynthesizer.appspot.com/empty-midroll-2"
  567 + }
  568 + ]
  569 + },
  570 + {
  571 + "name": "360",
  572 + "samples": [
  573 + {
  574 + "name": "Congo (360 top-bottom stereo)",
  575 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/360/congo.mp4",
  576 + "spherical_stereo_mode": "top_bottom"
  577 + },
  578 + {
  579 + "name": "Sphericalv2 (180 top-bottom stereo)",
  580 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/360/sphericalv2.mp4",
  581 + "spherical_stereo_mode": "top_bottom"
  582 + },
  583 + {
  584 + "name": "Iceland (360 top-bottom stereo ts)",
  585 + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/360/iceland0.ts",
  586 + "spherical_stereo_mode": "top_bottom"
  587 + }
  588 + ]
  589 + },
  590 + {
  591 + "name": "Subtitles",
  592 + "samples": [
  593 + {
  594 + "name": "TTML",
  595 + "uri": "https://html5demos.com/assets/dizzy.mp4",
  596 + "subtitle_uri": "https://storage.googleapis.com/exoplayer-test-media-1/ttml/netflix_ttml_sample.xml",
  597 + "subtitle_mime_type": "application/ttml+xml",
  598 + "subtitle_language": "en"
  599 + }
  600 + ]
  601 + }
  602 +]
... ...
cloud/video/src/main/java/com/libs/video/video/base/IDataSource.java
... ... @@ -8,6 +8,7 @@ import java.io.Serializable;
8 8  
9 9 public interface IDataSource extends Serializable {
10 10  
  11 +
11 12 String getDataSourceId();
12 13  
13 14 String getDataSourceType();
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/BaseTimerBar.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import android.annotation.TargetApi;
  4 +import android.content.Context;
  5 +import android.content.res.Resources;
  6 +import android.content.res.TypedArray;
  7 +import android.graphics.Canvas;
  8 +import android.graphics.Paint;
  9 +import android.graphics.Point;
  10 +import android.graphics.Rect;
  11 +import android.graphics.drawable.Drawable;
  12 +import android.os.Bundle;
  13 +import android.util.AttributeSet;
  14 +import android.util.DisplayMetrics;
  15 +import android.view.KeyEvent;
  16 +import android.view.MotionEvent;
  17 +import android.view.View;
  18 +import android.view.ViewParent;
  19 +import android.view.accessibility.AccessibilityEvent;
  20 +import android.view.accessibility.AccessibilityNodeInfo;
  21 +
  22 +import androidx.annotation.ColorInt;
  23 +import androidx.annotation.Nullable;
  24 +
  25 +import com.cnlive.cloud.video.R;
  26 +import com.google.android.exoplayer2.C;
  27 +import com.google.android.exoplayer2.ui.TimeBar;
  28 +import com.google.android.exoplayer2.util.Assertions;
  29 +import com.google.android.exoplayer2.util.Util;
  30 +
  31 +import java.util.Formatter;
  32 +import java.util.Locale;
  33 +import java.util.concurrent.CopyOnWriteArraySet;
  34 +
  35 +public class BaseTimerBar extends View implements TimeBar {
  36 +
  37 + /** Default height for the time bar, in dp. */
  38 + public static final int DEFAULT_BAR_HEIGHT_DP = 4;
  39 + /** Default height for the touch target, in dp. */
  40 + public static final int DEFAULT_TOUCH_TARGET_HEIGHT_DP = 26;
  41 + /** Default width for ad markers, in dp. */
  42 + public static final int DEFAULT_AD_MARKER_WIDTH_DP = 4;
  43 + /** Default diameter for the scrubber when enabled, in dp. */
  44 + public static final int DEFAULT_SCRUBBER_ENABLED_SIZE_DP = 12;
  45 + /** Default diameter for the scrubber when disabled, in dp. */
  46 + public static final int DEFAULT_SCRUBBER_DISABLED_SIZE_DP = 0;
  47 + /** Default diameter for the scrubber when dragged, in dp. */
  48 + public static final int DEFAULT_SCRUBBER_DRAGGED_SIZE_DP = 16;
  49 + /** Default color for the played portion of the time bar. */
  50 + public static final int DEFAULT_PLAYED_COLOR = 0xFFFFFFFF;
  51 + /** Default color for the unplayed portion of the time bar. */
  52 + public static final int DEFAULT_UNPLAYED_COLOR = 0x33FFFFFF;
  53 + /** Default color for the buffered portion of the time bar. */
  54 + public static final int DEFAULT_BUFFERED_COLOR = 0xCCFFFFFF;
  55 + /** Default color for the scrubber handle. */
  56 + public static final int DEFAULT_SCRUBBER_COLOR = 0xFFFFFFFF;
  57 + /** Default color for ad markers. */
  58 + public static final int DEFAULT_AD_MARKER_COLOR = 0xB2FFFF00;
  59 + /** Default color for played ad markers. */
  60 + public static final int DEFAULT_PLAYED_AD_MARKER_COLOR = 0x33FFFF00;
  61 +
  62 + /** The threshold in dps above the bar at which touch events trigger fine scrub mode. */
  63 + private static final int FINE_SCRUB_Y_THRESHOLD_DP = -50;
  64 + /** The ratio by which times are reduced in fine scrub mode. */
  65 + private static final int FINE_SCRUB_RATIO = 3;
  66 + /**
  67 + * The time after which the scrubbing listener is notified that scrubbing has stopped after
  68 + * performing an incremental scrub using key input.
  69 + */
  70 + private static final long STOP_SCRUBBING_TIMEOUT_MS = 1000;
  71 +
  72 + private static final int DEFAULT_INCREMENT_COUNT = 20;
  73 +
  74 + /**
  75 + * The name of the Android SDK view that most closely resembles this custom view. Used as the
  76 + * class name for accessibility.
  77 + */
  78 + private static final String ACCESSIBILITY_CLASS_NAME = "android.widget.SeekBar";
  79 +
  80 + private final Rect seekBounds;
  81 + private final Rect progressBar;
  82 + private final Rect bufferedBar;
  83 + private final Rect scrubberBar;
  84 + private final Paint playedPaint;
  85 + private final Paint bufferedPaint;
  86 + private final Paint unplayedPaint;
  87 + private final Paint adMarkerPaint;
  88 + private final Paint playedAdMarkerPaint;
  89 + private final Paint scrubberPaint;
  90 + @Nullable
  91 + private final Drawable scrubberDrawable;
  92 + private final int barHeight;
  93 + private final int touchTargetHeight;
  94 + private final int adMarkerWidth;
  95 + private final int scrubberEnabledSize;
  96 + private final int scrubberDisabledSize;
  97 + private final int scrubberDraggedSize;
  98 + private final int scrubberPadding;
  99 + private final int fineScrubYThreshold;
  100 + private final StringBuilder formatBuilder;
  101 + private final Formatter formatter;
  102 + private final Runnable stopScrubbingRunnable;
  103 + private final CopyOnWriteArraySet<TimeBar.OnScrubListener> listeners;
  104 + private final int[] locationOnScreen;
  105 + private final Point touchPosition;
  106 + private final float density;
  107 +
  108 + private int keyCountIncrement;
  109 + private long keyTimeIncrement;
  110 + private int lastCoarseScrubXPosition;
  111 +
  112 + private boolean scrubbing;
  113 + private long scrubPosition;
  114 + private long duration;
  115 + private long position;
  116 + private long bufferedPosition;
  117 + private int adGroupCount;
  118 + @Nullable private long[] adGroupTimesMs;
  119 + @Nullable private boolean[] playedAdGroups;
  120 +
  121 + public BaseTimerBar(Context context) {
  122 + this(context, null);
  123 + }
  124 +
  125 + public BaseTimerBar(Context context, @Nullable AttributeSet attrs) {
  126 + this(context, attrs, 0);
  127 + }
  128 +
  129 + public BaseTimerBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  130 + this(context, attrs, defStyleAttr, attrs);
  131 + }
  132 +
  133 + // Suppress warnings due to usage of View methods in the constructor.
  134 + // the constructor does not initialize fields: adGroupTimesMs, playedAdGroups
  135 + @SuppressWarnings({
  136 + "nullness:method.invocation.invalid",
  137 + "nullness:initialization.fields.uninitialized"
  138 + })
  139 + public BaseTimerBar(
  140 + Context context,
  141 + @Nullable AttributeSet attrs,
  142 + int defStyleAttr,
  143 + @Nullable AttributeSet timebarAttrs) {
  144 + super(context, attrs, defStyleAttr);
  145 + seekBounds = new Rect();
  146 + progressBar = new Rect();
  147 + bufferedBar = new Rect();
  148 + scrubberBar = new Rect();
  149 + playedPaint = new Paint();
  150 + bufferedPaint = new Paint();
  151 + unplayedPaint = new Paint();
  152 + adMarkerPaint = new Paint();
  153 + playedAdMarkerPaint = new Paint();
  154 + scrubberPaint = new Paint();
  155 + scrubberPaint.setAntiAlias(true);
  156 + listeners = new CopyOnWriteArraySet<>();
  157 + locationOnScreen = new int[2];
  158 + touchPosition = new Point();
  159 +
  160 + // Calculate the dimensions and paints for drawn elements.
  161 + Resources res = context.getResources();
  162 + DisplayMetrics displayMetrics = res.getDisplayMetrics();
  163 + density = displayMetrics.density;
  164 + fineScrubYThreshold = dpToPx(density, FINE_SCRUB_Y_THRESHOLD_DP);
  165 + int defaultBarHeight = dpToPx(density, DEFAULT_BAR_HEIGHT_DP);
  166 + int defaultTouchTargetHeight = dpToPx(density, DEFAULT_TOUCH_TARGET_HEIGHT_DP);
  167 + int defaultAdMarkerWidth = dpToPx(density, DEFAULT_AD_MARKER_WIDTH_DP);
  168 + int defaultScrubberEnabledSize = dpToPx(density, DEFAULT_SCRUBBER_ENABLED_SIZE_DP);
  169 + int defaultScrubberDisabledSize = dpToPx(density, DEFAULT_SCRUBBER_DISABLED_SIZE_DP);
  170 + int defaultScrubberDraggedSize = dpToPx(density, DEFAULT_SCRUBBER_DRAGGED_SIZE_DP);
  171 + if (timebarAttrs != null) {
  172 + TypedArray a =
  173 + context.getTheme().obtainStyledAttributes(timebarAttrs, R.styleable.DefaultTimeBar, 0, 0);
  174 + try {
  175 + scrubberDrawable = a.getDrawable(R.styleable.DefaultTimeBar_scrubber_drawable);
  176 + if (scrubberDrawable != null) {
  177 + setDrawableLayoutDirection(scrubberDrawable);
  178 + defaultTouchTargetHeight =
  179 + Math.max(scrubberDrawable.getMinimumHeight(), defaultTouchTargetHeight);
  180 + }
  181 + barHeight = a.getDimensionPixelSize(R.styleable.DefaultTimeBar_bar_height,
  182 + defaultBarHeight);
  183 + touchTargetHeight = a.getDimensionPixelSize(R.styleable.DefaultTimeBar_touch_target_height,
  184 + defaultTouchTargetHeight);
  185 + adMarkerWidth = a.getDimensionPixelSize(R.styleable.DefaultTimeBar_ad_marker_width,
  186 + defaultAdMarkerWidth);
  187 + scrubberEnabledSize = a.getDimensionPixelSize(
  188 + R.styleable.DefaultTimeBar_scrubber_enabled_size, defaultScrubberEnabledSize);
  189 + scrubberDisabledSize = a.getDimensionPixelSize(
  190 + R.styleable.DefaultTimeBar_scrubber_disabled_size, defaultScrubberDisabledSize);
  191 + scrubberDraggedSize = a.getDimensionPixelSize(
  192 + R.styleable.DefaultTimeBar_scrubber_dragged_size, defaultScrubberDraggedSize);
  193 + int playedColor = a.getInt(R.styleable.DefaultTimeBar_played_color, DEFAULT_PLAYED_COLOR);
  194 + int scrubberColor =
  195 + a.getInt(R.styleable.DefaultTimeBar_scrubber_color, DEFAULT_SCRUBBER_COLOR);
  196 + int bufferedColor =
  197 + a.getInt(R.styleable.DefaultTimeBar_buffered_color, DEFAULT_BUFFERED_COLOR);
  198 + int unplayedColor =
  199 + a.getInt(R.styleable.DefaultTimeBar_unplayed_color, DEFAULT_UNPLAYED_COLOR);
  200 + int adMarkerColor = a.getInt(R.styleable.DefaultTimeBar_ad_marker_color,
  201 + DEFAULT_AD_MARKER_COLOR);
  202 + int playedAdMarkerColor =
  203 + a.getInt(
  204 + R.styleable.DefaultTimeBar_played_ad_marker_color, DEFAULT_PLAYED_AD_MARKER_COLOR);
  205 + playedPaint.setColor(playedColor);
  206 + scrubberPaint.setColor(scrubberColor);
  207 + bufferedPaint.setColor(bufferedColor);
  208 + unplayedPaint.setColor(unplayedColor);
  209 + adMarkerPaint.setColor(adMarkerColor);
  210 + playedAdMarkerPaint.setColor(playedAdMarkerColor);
  211 + } finally {
  212 + a.recycle();
  213 + }
  214 + } else {
  215 + barHeight = defaultBarHeight;
  216 + touchTargetHeight = defaultTouchTargetHeight;
  217 + adMarkerWidth = defaultAdMarkerWidth;
  218 + scrubberEnabledSize = defaultScrubberEnabledSize;
  219 + scrubberDisabledSize = defaultScrubberDisabledSize;
  220 + scrubberDraggedSize = defaultScrubberDraggedSize;
  221 + playedPaint.setColor(DEFAULT_PLAYED_COLOR);
  222 + scrubberPaint.setColor(DEFAULT_SCRUBBER_COLOR);
  223 + bufferedPaint.setColor(DEFAULT_BUFFERED_COLOR);
  224 + unplayedPaint.setColor(DEFAULT_UNPLAYED_COLOR);
  225 + adMarkerPaint.setColor(DEFAULT_AD_MARKER_COLOR);
  226 + playedAdMarkerPaint.setColor(DEFAULT_PLAYED_AD_MARKER_COLOR);
  227 + scrubberDrawable = null;
  228 + }
  229 + formatBuilder = new StringBuilder();
  230 + formatter = new Formatter(formatBuilder, Locale.getDefault());
  231 + stopScrubbingRunnable = () -> stopScrubbing(/* canceled= */ false);
  232 + if (scrubberDrawable != null) {
  233 + scrubberPadding = (scrubberDrawable.getMinimumWidth() + 1) / 2;
  234 + } else {
  235 + scrubberPadding =
  236 + (Math.max(scrubberDisabledSize, Math.max(scrubberEnabledSize, scrubberDraggedSize)) + 1)
  237 + / 2;
  238 + }
  239 + duration = C.TIME_UNSET;
  240 + keyTimeIncrement = C.TIME_UNSET;
  241 + keyCountIncrement = DEFAULT_INCREMENT_COUNT;
  242 + setFocusable(true);
  243 + if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
  244 + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
  245 + }
  246 + }
  247 +
  248 + /**
  249 + * Sets the color for the portion of the time bar representing media before the playback position.
  250 + *
  251 + * @param playedColor The color for the portion of the time bar representing media before the
  252 + * playback position.
  253 + */
  254 + public void setPlayedColor(@ColorInt int playedColor) {
  255 + playedPaint.setColor(playedColor);
  256 + invalidate(seekBounds);
  257 + }
  258 +
  259 + /**
  260 + * Sets the color for the scrubber handle.
  261 + *
  262 + * @param scrubberColor The color for the scrubber handle.
  263 + */
  264 + public void setScrubberColor(@ColorInt int scrubberColor) {
  265 + scrubberPaint.setColor(scrubberColor);
  266 + invalidate(seekBounds);
  267 + }
  268 +
  269 + /**
  270 + * Sets the color for the portion of the time bar after the current played position up to the
  271 + * current buffered position.
  272 + *
  273 + * @param bufferedColor The color for the portion of the time bar after the current played
  274 + * position up to the current buffered position.
  275 + */
  276 + public void setBufferedColor(@ColorInt int bufferedColor) {
  277 + bufferedPaint.setColor(bufferedColor);
  278 + invalidate(seekBounds);
  279 + }
  280 +
  281 + /**
  282 + * Sets the color for the portion of the time bar after the current played position.
  283 + *
  284 + * @param unplayedColor The color for the portion of the time bar after the current played
  285 + * position.
  286 + */
  287 + public void setUnplayedColor(@ColorInt int unplayedColor) {
  288 + unplayedPaint.setColor(unplayedColor);
  289 + invalidate(seekBounds);
  290 + }
  291 +
  292 + /**
  293 + * Sets the color for unplayed ad markers.
  294 + *
  295 + * @param adMarkerColor The color for unplayed ad markers.
  296 + */
  297 + public void setAdMarkerColor(@ColorInt int adMarkerColor) {
  298 + adMarkerPaint.setColor(adMarkerColor);
  299 + invalidate(seekBounds);
  300 + }
  301 +
  302 + /**
  303 + * Sets the color for played ad markers.
  304 + *
  305 + * @param playedAdMarkerColor The color for played ad markers.
  306 + */
  307 + public void setPlayedAdMarkerColor(@ColorInt int playedAdMarkerColor) {
  308 + playedAdMarkerPaint.setColor(playedAdMarkerColor);
  309 + invalidate(seekBounds);
  310 + }
  311 +
  312 + // TimeBar implementation.
  313 +
  314 + @Override
  315 + public void addListener(TimeBar.OnScrubListener listener) {
  316 + listeners.add(listener);
  317 + }
  318 +
  319 + @Override
  320 + public void removeListener(TimeBar.OnScrubListener listener) {
  321 + listeners.remove(listener);
  322 + }
  323 +
  324 + @Override
  325 + public void setKeyTimeIncrement(long time) {
  326 + Assertions.checkArgument(time > 0);
  327 + keyCountIncrement = C.INDEX_UNSET;
  328 + keyTimeIncrement = time;
  329 + }
  330 +
  331 + @Override
  332 + public void setKeyCountIncrement(int count) {
  333 + Assertions.checkArgument(count > 0);
  334 + keyCountIncrement = count;
  335 + keyTimeIncrement = C.TIME_UNSET;
  336 + }
  337 +
  338 + @Override
  339 + public void setPosition(long position) {
  340 + this.position = position;
  341 + setContentDescription(getProgressText());
  342 + update();
  343 + }
  344 +
  345 + @Override
  346 + public void setBufferedPosition(long bufferedPosition) {
  347 + this.bufferedPosition = bufferedPosition;
  348 + update();
  349 + }
  350 +
  351 + @Override
  352 + public void setDuration(long duration) {
  353 + this.duration = duration;
  354 + if (scrubbing && duration == C.TIME_UNSET) {
  355 + stopScrubbing(/* canceled= */ true);
  356 + }
  357 + update();
  358 + }
  359 +
  360 + @Override
  361 + public long getPreferredUpdateDelay() {
  362 + int timeBarWidthDp = pxToDp(density, progressBar.width());
  363 + return timeBarWidthDp == 0 || duration == 0 || duration == C.TIME_UNSET
  364 + ? Long.MAX_VALUE
  365 + : duration / timeBarWidthDp;
  366 + }
  367 +
  368 + @Override
  369 + public void setAdGroupTimesMs(@Nullable long[] adGroupTimesMs, @Nullable boolean[] playedAdGroups,
  370 + int adGroupCount) {
  371 + Assertions.checkArgument(adGroupCount == 0
  372 + || (adGroupTimesMs != null && playedAdGroups != null));
  373 + this.adGroupCount = adGroupCount;
  374 + this.adGroupTimesMs = adGroupTimesMs;
  375 + this.playedAdGroups = playedAdGroups;
  376 + update();
  377 + }
  378 +
  379 + // View methods.
  380 +
  381 + @Override
  382 + public void setEnabled(boolean enabled) {
  383 + super.setEnabled(enabled);
  384 + if (scrubbing && !enabled) {
  385 + stopScrubbing(/* canceled= */ true);
  386 + }
  387 + }
  388 +
  389 + @Override
  390 + public void onDraw(Canvas canvas) {
  391 + canvas.save();
  392 + drawTimeBar(canvas);
  393 + drawPlayhead(canvas);
  394 + canvas.restore();
  395 + }
  396 +
  397 + @Override
  398 + public boolean onTouchEvent(MotionEvent event) {
  399 + if (!isEnabled() || duration <= 0) {
  400 + return false;
  401 + }
  402 + Point touchPosition = resolveRelativeTouchPosition(event);
  403 + int x = touchPosition.x;
  404 + int y = touchPosition.y;
  405 + switch (event.getAction()) {
  406 + case MotionEvent.ACTION_DOWN:
  407 + if (isInSeekBar(x, y)) {
  408 + positionScrubber(x);
  409 + startScrubbing(getScrubberPosition());
  410 + update();
  411 + invalidate();
  412 + return true;
  413 + }
  414 + break;
  415 + case MotionEvent.ACTION_MOVE:
  416 + if (scrubbing) {
  417 + if (y < fineScrubYThreshold) {
  418 + int relativeX = x - lastCoarseScrubXPosition;
  419 + positionScrubber(lastCoarseScrubXPosition + relativeX / FINE_SCRUB_RATIO);
  420 + } else {
  421 + lastCoarseScrubXPosition = x;
  422 + positionScrubber(x);
  423 + }
  424 + updateScrubbing(getScrubberPosition());
  425 + update();
  426 + invalidate();
  427 + return true;
  428 + }
  429 + break;
  430 + case MotionEvent.ACTION_UP:
  431 + case MotionEvent.ACTION_CANCEL:
  432 + if (scrubbing) {
  433 + stopScrubbing(/* canceled= */ event.getAction() == MotionEvent.ACTION_CANCEL);
  434 + return true;
  435 + }
  436 + break;
  437 + default:
  438 + // Do nothing.
  439 + }
  440 + return false;
  441 + }
  442 +
  443 + @Override
  444 + public boolean onKeyDown(int keyCode, KeyEvent event) {
  445 + if (isEnabled()) {
  446 + long positionIncrement = getPositionIncrement();
  447 + switch (keyCode) {
  448 + case KeyEvent.KEYCODE_DPAD_LEFT:
  449 + positionIncrement = -positionIncrement;
  450 + // Fall through.
  451 + case KeyEvent.KEYCODE_DPAD_RIGHT:
  452 + if (scrubIncrementally(positionIncrement)) {
  453 + removeCallbacks(stopScrubbingRunnable);
  454 + postDelayed(stopScrubbingRunnable, STOP_SCRUBBING_TIMEOUT_MS);
  455 + return true;
  456 + }
  457 + break;
  458 + case KeyEvent.KEYCODE_DPAD_CENTER:
  459 + case KeyEvent.KEYCODE_ENTER:
  460 + if (scrubbing) {
  461 + stopScrubbing(/* canceled= */ false);
  462 + return true;
  463 + }
  464 + break;
  465 + default:
  466 + // Do nothing.
  467 + }
  468 + }
  469 + return super.onKeyDown(keyCode, event);
  470 + }
  471 +
  472 + @Override
  473 + protected void onFocusChanged(
  474 + boolean gainFocus, int direction, @Nullable Rect previouslyFocusedRect) {
  475 + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
  476 + if (scrubbing && !gainFocus) {
  477 + stopScrubbing(/* canceled= */ false);
  478 + }
  479 + }
  480 +
  481 + @Override
  482 + protected void drawableStateChanged() {
  483 + super.drawableStateChanged();
  484 + updateDrawableState();
  485 + }
  486 +
  487 + @Override
  488 + public void jumpDrawablesToCurrentState() {
  489 + super.jumpDrawablesToCurrentState();
  490 + if (scrubberDrawable != null) {
  491 + scrubberDrawable.jumpToCurrentState();
  492 + }
  493 + }
  494 +
  495 + @Override
  496 + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  497 + int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
  498 + int heightSize = View.MeasureSpec.getSize(heightMeasureSpec);
  499 + int height = heightMode == View.MeasureSpec.UNSPECIFIED ? touchTargetHeight
  500 + : heightMode == View.MeasureSpec.EXACTLY ? heightSize : Math.min(touchTargetHeight, heightSize);
  501 + setMeasuredDimension(View.MeasureSpec.getSize(widthMeasureSpec), height);
  502 + updateDrawableState();
  503 + }
  504 +
  505 + @Override
  506 + protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  507 + int width = right - left;
  508 + int height = bottom - top;
  509 + int barY = (height - touchTargetHeight) / 2;
  510 + int seekLeft = getPaddingLeft();
  511 + int seekRight = width - getPaddingRight();
  512 + int progressY = barY + (touchTargetHeight - barHeight) / 2;
  513 + seekBounds.set(seekLeft, barY, seekRight, barY + touchTargetHeight);
  514 + progressBar.set(seekBounds.left + scrubberPadding, progressY,
  515 + seekBounds.right - scrubberPadding, progressY + barHeight);
  516 + update();
  517 + }
  518 +
  519 + @Override
  520 + public void onRtlPropertiesChanged(int layoutDirection) {
  521 + if (scrubberDrawable != null && setDrawableLayoutDirection(scrubberDrawable, layoutDirection)) {
  522 + invalidate();
  523 + }
  524 + }
  525 +
  526 + @Override
  527 + public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
  528 + super.onInitializeAccessibilityEvent(event);
  529 + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SELECTED) {
  530 + event.getText().add(getProgressText());
  531 + }
  532 + event.setClassName(ACCESSIBILITY_CLASS_NAME);
  533 + }
  534 +
  535 + @TargetApi(21)
  536 + @Override
  537 + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
  538 + super.onInitializeAccessibilityNodeInfo(info);
  539 + info.setClassName(ACCESSIBILITY_CLASS_NAME);
  540 + info.setContentDescription(getProgressText());
  541 + if (duration <= 0) {
  542 + return;
  543 + }
  544 + if (Util.SDK_INT >= 21) {
  545 + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
  546 + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
  547 + } else {
  548 + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
  549 + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
  550 + }
  551 + }
  552 +
  553 + @Override
  554 + public boolean performAccessibilityAction(int action, @Nullable Bundle args) {
  555 + if (super.performAccessibilityAction(action, args)) {
  556 + return true;
  557 + }
  558 + if (duration <= 0) {
  559 + return false;
  560 + }
  561 + if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
  562 + if (scrubIncrementally(-getPositionIncrement())) {
  563 + stopScrubbing(/* canceled= */ false);
  564 + }
  565 + } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
  566 + if (scrubIncrementally(getPositionIncrement())) {
  567 + stopScrubbing(/* canceled= */ false);
  568 + }
  569 + } else {
  570 + return false;
  571 + }
  572 + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
  573 + return true;
  574 + }
  575 +
  576 + // Internal methods.
  577 +
  578 + private void startScrubbing(long scrubPosition) {
  579 + this.scrubPosition = scrubPosition;
  580 + scrubbing = true;
  581 + setPressed(true);
  582 + ViewParent parent = getParent();
  583 + if (parent != null) {
  584 + parent.requestDisallowInterceptTouchEvent(true);
  585 + }
  586 + for (TimeBar.OnScrubListener listener : listeners) {
  587 + listener.onScrubStart(this, scrubPosition);
  588 + }
  589 + }
  590 +
  591 + private void updateScrubbing(long scrubPosition) {
  592 + if (this.scrubPosition == scrubPosition) {
  593 + return;
  594 + }
  595 + this.scrubPosition = scrubPosition;
  596 + for (TimeBar.OnScrubListener listener : listeners) {
  597 + listener.onScrubMove(this, scrubPosition);
  598 + }
  599 + }
  600 +
  601 + private void stopScrubbing(boolean canceled) {
  602 + removeCallbacks(stopScrubbingRunnable);
  603 + scrubbing = false;
  604 + setPressed(false);
  605 + ViewParent parent = getParent();
  606 + if (parent != null) {
  607 + parent.requestDisallowInterceptTouchEvent(false);
  608 + }
  609 + invalidate();
  610 + for (TimeBar.OnScrubListener listener : listeners) {
  611 + listener.onScrubStop(this, scrubPosition, canceled);
  612 + }
  613 + }
  614 +
  615 + /**
  616 + * Incrementally scrubs the position by {@code positionChange}.
  617 + *
  618 + * @param positionChange The change in the scrubber position, in milliseconds. May be negative.
  619 + * @return Returns whether the scrubber position changed.
  620 + */
  621 + private boolean scrubIncrementally(long positionChange) {
  622 + if (duration <= 0) {
  623 + return false;
  624 + }
  625 + long previousPosition = scrubbing ? scrubPosition : position;
  626 + long scrubPosition = Util.constrainValue(previousPosition + positionChange, 0, duration);
  627 + if (scrubPosition == previousPosition) {
  628 + return false;
  629 + }
  630 + if (!scrubbing) {
  631 + startScrubbing(scrubPosition);
  632 + } else {
  633 + updateScrubbing(scrubPosition);
  634 + }
  635 + update();
  636 + return true;
  637 + }
  638 +
  639 + private void update() {
  640 + bufferedBar.set(progressBar);
  641 + scrubberBar.set(progressBar);
  642 + long newScrubberTime = scrubbing ? scrubPosition : position;
  643 + if (duration > 0) {
  644 + int bufferedPixelWidth = (int) ((progressBar.width() * bufferedPosition) / duration);
  645 + bufferedBar.right = Math.min(progressBar.left + bufferedPixelWidth, progressBar.right);
  646 + int scrubberPixelPosition = (int) ((progressBar.width() * newScrubberTime) / duration);
  647 + scrubberBar.right = Math.min(progressBar.left + scrubberPixelPosition, progressBar.right);
  648 + } else {
  649 + bufferedBar.right = progressBar.left;
  650 + scrubberBar.right = progressBar.left;
  651 + }
  652 + invalidate(seekBounds);
  653 + }
  654 +
  655 + private void positionScrubber(float xPosition) {
  656 + scrubberBar.right = Util.constrainValue((int) xPosition, progressBar.left, progressBar.right);
  657 + }
  658 +
  659 + private Point resolveRelativeTouchPosition(MotionEvent motionEvent) {
  660 + getLocationOnScreen(locationOnScreen);
  661 + touchPosition.set(
  662 + ((int) motionEvent.getRawX()) - locationOnScreen[0],
  663 + ((int) motionEvent.getRawY()) - locationOnScreen[1]);
  664 + return touchPosition;
  665 + }
  666 +
  667 + private long getScrubberPosition() {
  668 + if (progressBar.width() <= 0 || duration == C.TIME_UNSET) {
  669 + return 0;
  670 + }
  671 + return (scrubberBar.width() * duration) / progressBar.width();
  672 + }
  673 +
  674 + private boolean isInSeekBar(float x, float y) {
  675 + return seekBounds.contains((int) x, (int) y);
  676 + }
  677 +
  678 + private void drawTimeBar(Canvas canvas) {
  679 + int progressBarHeight = progressBar.height();
  680 + int barTop = progressBar.centerY() - progressBarHeight / 2;
  681 + int barBottom = barTop + progressBarHeight;
  682 + if (duration <= 0) {
  683 + canvas.drawRect(progressBar.left, barTop, progressBar.right, barBottom, unplayedPaint);
  684 + return;
  685 + }
  686 + int bufferedLeft = bufferedBar.left;
  687 + int bufferedRight = bufferedBar.right;
  688 + int progressLeft = Math.max(Math.max(progressBar.left, bufferedRight), scrubberBar.right);
  689 + if (progressLeft < progressBar.right) {
  690 + canvas.drawRect(progressLeft, barTop, progressBar.right, barBottom, unplayedPaint);
  691 + }
  692 + bufferedLeft = Math.max(bufferedLeft, scrubberBar.right);
  693 + if (bufferedRight > bufferedLeft) {
  694 + canvas.drawRect(bufferedLeft, barTop, bufferedRight, barBottom, bufferedPaint);
  695 + }
  696 + if (scrubberBar.width() > 0) {
  697 + canvas.drawRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, playedPaint);
  698 + }
  699 + if (adGroupCount == 0) {
  700 + return;
  701 + }
  702 + long[] adGroupTimesMs = Assertions.checkNotNull(this.adGroupTimesMs);
  703 + boolean[] playedAdGroups = Assertions.checkNotNull(this.playedAdGroups);
  704 + int adMarkerOffset = adMarkerWidth / 2;
  705 + for (int i = 0; i < adGroupCount; i++) {
  706 + long adGroupTimeMs = Util.constrainValue(adGroupTimesMs[i], 0, duration);
  707 + int markerPositionOffset =
  708 + (int) (progressBar.width() * adGroupTimeMs / duration) - adMarkerOffset;
  709 + int markerLeft = progressBar.left + Math.min(progressBar.width() - adMarkerWidth,
  710 + Math.max(0, markerPositionOffset));
  711 + Paint paint = playedAdGroups[i] ? playedAdMarkerPaint : adMarkerPaint;
  712 + canvas.drawRect(markerLeft, barTop, markerLeft + adMarkerWidth, barBottom, paint);
  713 + }
  714 + }
  715 +
  716 + private void drawPlayhead(Canvas canvas) {
  717 + if (duration <= 0) {
  718 + return;
  719 + }
  720 + int playheadX = Util.constrainValue(scrubberBar.right, scrubberBar.left, progressBar.right);
  721 + int playheadY = scrubberBar.centerY();
  722 + if (scrubberDrawable == null) {
  723 + int scrubberSize = (scrubbing || isFocused()) ? scrubberDraggedSize
  724 + : (isEnabled() ? scrubberEnabledSize : scrubberDisabledSize);
  725 + int playheadRadius = scrubberSize / 2;
  726 + canvas.drawCircle(playheadX, playheadY, playheadRadius, scrubberPaint);
  727 + } else {
  728 + int scrubberDrawableWidth = scrubberDrawable.getIntrinsicWidth();
  729 + int scrubberDrawableHeight = scrubberDrawable.getIntrinsicHeight();
  730 + scrubberDrawable.setBounds(
  731 + playheadX - scrubberDrawableWidth / 2,
  732 + playheadY - scrubberDrawableHeight / 2,
  733 + playheadX + scrubberDrawableWidth / 2,
  734 + playheadY + scrubberDrawableHeight / 2);
  735 + scrubberDrawable.draw(canvas);
  736 + }
  737 + }
  738 +
  739 + private void updateDrawableState() {
  740 + if (scrubberDrawable != null && scrubberDrawable.isStateful()
  741 + && scrubberDrawable.setState(getDrawableState())) {
  742 + invalidate();
  743 + }
  744 + }
  745 +
  746 + private String getProgressText() {
  747 + return Util.getStringForTime(formatBuilder, formatter, position);
  748 + }
  749 +
  750 + private long getPositionIncrement() {
  751 + return keyTimeIncrement == C.TIME_UNSET
  752 + ? (duration == C.TIME_UNSET ? 0 : (duration / keyCountIncrement)) : keyTimeIncrement;
  753 + }
  754 +
  755 + private boolean setDrawableLayoutDirection(Drawable drawable) {
  756 + return Util.SDK_INT >= 23 && setDrawableLayoutDirection(drawable, getLayoutDirection());
  757 + }
  758 +
  759 + private static boolean setDrawableLayoutDirection(Drawable drawable, int layoutDirection) {
  760 + return Util.SDK_INT >= 23 && drawable.setLayoutDirection(layoutDirection);
  761 + }
  762 +
  763 + private static int dpToPx(float density, int dps) {
  764 + return (int) (dps * density + 0.5f);
  765 + }
  766 +
  767 + private static int pxToDp(float density, int px) {
  768 + return (int) (px / density);
  769 + }
  770 +}
0 771 \ No newline at end of file
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/CNLiveBaseVideoControlView.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import android.annotation.SuppressLint;
  4 +import android.app.Activity;
  5 +import android.content.Context;
  6 +import android.content.pm.ActivityInfo;
  7 +import android.content.res.Configuration;
  8 +import android.content.res.Resources;
  9 +import android.content.res.TypedArray;
  10 +import android.graphics.drawable.Drawable;
  11 +import android.os.Looper;
  12 +import android.os.SystemClock;
  13 +import android.util.AttributeSet;
  14 +import android.view.KeyEvent;
  15 +import android.view.LayoutInflater;
  16 +import android.view.MotionEvent;
  17 +import android.view.View;
  18 +import android.view.ViewGroup;
  19 +import android.view.WindowManager;
  20 +import android.widget.FrameLayout;
  21 +import android.widget.ImageView;
  22 +import android.widget.TextView;
  23 +
  24 +import androidx.annotation.ColorInt;
  25 +import androidx.annotation.Nullable;
  26 +
  27 +import com.cnlive.cloud.video.R;
  28 +import com.google.android.exoplayer2.C;
  29 +import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
  30 +import com.google.android.exoplayer2.PlaybackPreparer;
  31 +import com.google.android.exoplayer2.Player;
  32 +import com.google.android.exoplayer2.Timeline;
  33 +import com.google.android.exoplayer2.ui.TimeBar;
  34 +import com.google.android.exoplayer2.util.Assertions;
  35 +import com.google.android.exoplayer2.util.RepeatModeUtil;
  36 +import com.google.android.exoplayer2.util.Util;
  37 +
  38 +import java.util.Arrays;
  39 +import java.util.Formatter;
  40 +import java.util.Locale;
  41 +import java.util.concurrent.CopyOnWriteArrayList;
  42 +
  43 +public abstract class CNLiveBaseVideoControlView extends FrameLayout {
  44 +
  45 + static {
  46 + ExoPlayerLibraryInfo.registerModule("goog.exo.ui");
  47 + }
  48 +
  49 + /**
  50 + * Listener to be notified about changes of the visibility of the UI control.
  51 + */
  52 + public interface VisibilityListener {
  53 +
  54 + /**
  55 + * Called when the visibility changes.
  56 + *
  57 + * @param visibility The new visibility. Either {@link View#VISIBLE} or {@link View#GONE}.
  58 + */
  59 + void onVisibilityChange(int visibility);
  60 + }
  61 +
  62 + /**
  63 + * Listener to be notified when progress has been updated.
  64 + */
  65 + public interface ProgressUpdateListener {
  66 +
  67 + /**
  68 + * Called when progress needs to be updated.
  69 + *
  70 + * @param position The current position.
  71 + * @param bufferedPosition The current buffered position.
  72 + */
  73 + void onProgressUpdate(long position, long bufferedPosition);
  74 + }
  75 +
  76 + /**
  77 + * The default fast forward increment, in milliseconds.
  78 + */
  79 + public static final int DEFAULT_FAST_FORWARD_MS = 15000;
  80 + /**
  81 + * The default rewind increment, in milliseconds.
  82 + */
  83 + public static final int DEFAULT_REWIND_MS = 5000;
  84 + /**
  85 + * The default show timeout, in milliseconds.
  86 + */
  87 + public static final int DEFAULT_SHOW_TIMEOUT_MS = 5000;
  88 + /**
  89 + * The default repeat toggle modes.
  90 + */
  91 + public static final @RepeatModeUtil.RepeatToggleModes
  92 + int DEFAULT_REPEAT_TOGGLE_MODES =
  93 + RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE;
  94 + /**
  95 + * The default minimum interval between time bar position updates.
  96 + */
  97 + public static final int DEFAULT_TIME_BAR_MIN_UPDATE_INTERVAL_MS = 200;
  98 + /**
  99 + * The maximum number of windows that can be shown in a multi-window time bar.
  100 + */
  101 + public static final int MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR = 100;
  102 +
  103 + private static final long MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000;
  104 + /**
  105 + * The maximum interval between time bar position updates.
  106 + */
  107 + private static final int MAX_UPDATE_INTERVAL_MS = 1000;
  108 +
  109 + private CNLiveBaseVideoControlView.ComponentListener componentListener;
  110 + private CopyOnWriteArrayList<CNLiveBaseVideoControlView.VisibilityListener> visibilityListeners;
  111 + @Nullable
  112 + private View previousButton;
  113 + @Nullable
  114 + private View nextButton;
  115 + @Nullable
  116 + private View playButton;
  117 + @Nullable
  118 + private View pauseButton;
  119 + @Nullable
  120 + private View fastForwardButton;
  121 + @Nullable
  122 + private View rewindButton;
  123 + @Nullable
  124 + private ImageView repeatToggleButton;
  125 + @Nullable
  126 + private ImageView shuffleButton;
  127 + @Nullable
  128 + private View vrButton;
  129 + @Nullable
  130 + private TextView durationView;
  131 + @Nullable
  132 + private TextView positionView;
  133 + @Nullable
  134 + private TimeBar timeBar;
  135 + private StringBuilder formatBuilder;
  136 + private Formatter formatter;
  137 + private Timeline.Period period;
  138 + private Timeline.Window window;
  139 + private Runnable updateProgressAction;
  140 + private Runnable hideAction;
  141 + private int controllerLayoutId;
  142 + private Drawable repeatOffButtonDrawable;
  143 + private Drawable repeatOneButtonDrawable;
  144 + private Drawable repeatAllButtonDrawable;
  145 + private String repeatOffButtonContentDescription;
  146 + private String repeatOneButtonContentDescription;
  147 + private String repeatAllButtonContentDescription;
  148 + private Drawable shuffleOnButtonDrawable;
  149 + private Drawable shuffleOffButtonDrawable;
  150 + private float buttonAlphaEnabled;
  151 + private float buttonAlphaDisabled;
  152 + private String shuffleOnContentDescription;
  153 + private String shuffleOffContentDescription;
  154 + //当前屏幕朝向
  155 + private final int PORTRAIT_SCREEN = 0;//竖屏
  156 + @Nullable
  157 + private VideoPlayer player;
  158 + // private CNLiveControlDispatcher controlDispatcher;
  159 +// @Nullable
  160 +// private CNLiveBaseVideoControlView.ProgressUpdateListener progressUpdateListener;
  161 +// @Nullable
  162 + private PlaybackPreparer playbackPreparer;
  163 +
  164 + private boolean isAttachedToWindow;
  165 + private boolean showMultiWindowTimeBar;
  166 + private boolean multiWindowTimeBar;
  167 + private boolean scrubbing;
  168 + private int rewindMs;
  169 + private int fastForwardMs;
  170 + private int showTimeoutMs;
  171 + private int timeBarMinUpdateIntervalMs;
  172 + private @RepeatModeUtil.RepeatToggleModes
  173 + int repeatToggleModes;
  174 + private boolean showShuffleButton;
  175 + private long hideAtMs;
  176 + private long[] adGroupTimesMs;
  177 + private boolean[] playedAdGroups;
  178 + private long[] extraAdGroupTimesMs;
  179 + private boolean[] extraPlayedAdGroups;
  180 + private long currentWindowOffset;
  181 + private Activity activity;
  182 + private TextView tvTopViewTitle;//顶部标题视图
  183 + //横竖屏
  184 + @Nullable
  185 + private View switchScreenButton;
  186 + public View basePortraitView;
  187 + public View baseLandscapeView;
  188 +
  189 + public Context context;
  190 +
  191 + public CNLiveBaseVideoControlView(Context context) {
  192 + this(context, /* attrs= */ null, 0, null);
  193 + this.context = context;
  194 + }
  195 +
  196 + protected int getControllViewId() {
  197 + return -1;
  198 + }
  199 +
  200 + protected abstract void onViewClick(View view);
  201 +
  202 + @SuppressWarnings({
  203 + "nullness:argument.type.incompatible",
  204 + "nullness:method.invocation.invalid",
  205 + "nullness:methodref.receiver.bound.invalid"
  206 + })
  207 + public CNLiveBaseVideoControlView(
  208 + Context context,
  209 + @Nullable AttributeSet attrs,
  210 + int defStyleAttr,
  211 + @Nullable AttributeSet playbackAttrs) {
  212 + super(context, attrs, defStyleAttr);
  213 + this.context = context;
  214 + if (context instanceof Activity) {
  215 + this.activity = (Activity) context;
  216 + }
  217 + componentListener = new CNLiveBaseVideoControlView.ComponentListener();
  218 +// controlDispatcher = new CNLiveDefaultControlDispatcher();
  219 +
  220 + controllerLayoutId = R.layout.layout_cnlive_control_view;
  221 + if (null != playbackAttrs) {
  222 + rewindMs = DEFAULT_REWIND_MS;
  223 + fastForwardMs = DEFAULT_FAST_FORWARD_MS;
  224 + showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS;
  225 + repeatToggleModes = DEFAULT_REPEAT_TOGGLE_MODES;
  226 + timeBarMinUpdateIntervalMs = DEFAULT_TIME_BAR_MIN_UPDATE_INTERVAL_MS;
  227 + hideAtMs = C.TIME_UNSET;
  228 + showShuffleButton = false;
  229 + if (playbackAttrs != null) {
  230 + TypedArray a =
  231 + context
  232 + .getTheme()
  233 + .obtainStyledAttributes(playbackAttrs, R.styleable.PlayerControlView, 0, 0);
  234 + try {
  235 + rewindMs = a.getInt(R.styleable.PlayerControlView_rewind_increment, rewindMs);
  236 + fastForwardMs =
  237 + a.getInt(R.styleable.PlayerControlView_fastforward_increment, fastForwardMs);
  238 + showTimeoutMs = a.getInt(R.styleable.PlayerControlView_show_timeout, showTimeoutMs);
  239 + controllerLayoutId =
  240 + a.getResourceId(R.styleable.PlayerControlView_controller_layout_id, controllerLayoutId);
  241 + repeatToggleModes = getRepeatToggleModes(a, repeatToggleModes);
  242 + showShuffleButton =
  243 + a.getBoolean(R.styleable.PlayerControlView_show_shuffle_button, showShuffleButton);
  244 + setTimeBarMinUpdateInterval(
  245 + a.getInt(
  246 + R.styleable.PlayerControlView_time_bar_min_update_interval,
  247 + timeBarMinUpdateIntervalMs));
  248 + } finally {
  249 + a.recycle();
  250 + }
  251 + }
  252 + visibilityListeners = new CopyOnWriteArrayList<>();
  253 + period = new Timeline.Period();
  254 + window = new Timeline.Window();
  255 + formatBuilder = new StringBuilder();
  256 + formatter = new Formatter(formatBuilder, Locale.getDefault());
  257 + adGroupTimesMs = new long[0];
  258 + playedAdGroups = new boolean[0];
  259 + extraAdGroupTimesMs = new long[0];
  260 + extraPlayedAdGroups = new boolean[0];
  261 + updateProgressAction = this::updateProgress;
  262 + hideAction = this::hide;
  263 +
  264 + basePortraitView = LayoutInflater.from(context).inflate(controllerLayoutId, /* root= */ this);
  265 + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
  266 +
  267 + TimeBar customTimeBar = findViewById(R.id.cnlive_progress);
  268 + View timeBarPlaceholder = findViewById(R.id.cnlive_progress_placeholder);
  269 + if (customTimeBar != null) {
  270 + timeBar = customTimeBar;
  271 + } else if (timeBarPlaceholder != null) {
  272 + // Propagate attrs as timebarAttrs so that DefaultTimeBar's custom attributes are transferred,
  273 + // but standard attributes (e.g. background) are not.
  274 + BaseTimerBar defaultTimeBar = new BaseTimerBar(context, null, 0, playbackAttrs);
  275 + //todo 设置播放的颜色
  276 +// defaultTimeBar.setPlayedColor(ContextCompat.getColor(context,R.color.qmui_config_color_red));
  277 + //todo 设置缓冲颜色
  278 +// defaultTimeBar.setBufferedColor(ContextCompat.getColor(context,R.color.qmui_config_color_red));
  279 + defaultTimeBar.setId(R.id.cnlive_progress);
  280 + defaultTimeBar.setLayoutParams(timeBarPlaceholder.getLayoutParams());
  281 + ViewGroup parent = ((ViewGroup) timeBarPlaceholder.getParent());
  282 + int timeBarIndex = parent.indexOfChild(timeBarPlaceholder);
  283 + parent.removeView(timeBarPlaceholder);
  284 + parent.addView(defaultTimeBar, timeBarIndex);
  285 + timeBar = defaultTimeBar;
  286 + } else {
  287 + timeBar = null;
  288 + }
  289 + durationView = findViewById(R.id.cnlive_duration);
  290 + positionView = findViewById(R.id.cnlive_position);
  291 + //初始化顶部视图
  292 + //顶部标题
  293 + tvTopViewTitle = findViewById(R.id.cnlive_top_title);
  294 + //设置标题名称
  295 + if (null != tvTopViewTitle) {
  296 + tvTopViewTitle.setText("测试");
  297 + }
  298 + if (timeBar != null) {
  299 + timeBar.addListener(componentListener);
  300 + }
  301 + playButton = findViewById(R.id.cnlive_play);
  302 + if (playButton != null) {
  303 + playButton.setOnClickListener(componentListener);
  304 + }
  305 + pauseButton = findViewById(R.id.cnlive_pause);
  306 + if (pauseButton != null) {
  307 + pauseButton.setOnClickListener(componentListener);
  308 + }
  309 + previousButton = findViewById(R.id.cnlive_prev);
  310 + if (previousButton != null) {
  311 + previousButton.setOnClickListener(componentListener);
  312 + }
  313 + nextButton = findViewById(R.id.cnlive_next);
  314 + if (nextButton != null) {
  315 + nextButton.setOnClickListener(componentListener);
  316 + }
  317 + rewindButton = findViewById(R.id.cnlive_rew);
  318 + if (rewindButton != null) {
  319 + rewindButton.setOnClickListener(componentListener);
  320 + }
  321 + fastForwardButton = findViewById(R.id.cnlive_ffwd);
  322 + if (fastForwardButton != null) {
  323 + fastForwardButton.setOnClickListener(componentListener);
  324 + }
  325 + repeatToggleButton = findViewById(R.id.cnlive_repeat_toggle);
  326 + if (repeatToggleButton != null) {
  327 + repeatToggleButton.setOnClickListener(componentListener);
  328 + }
  329 + repeatToggleButton.setVisibility(GONE);
  330 + shuffleButton = findViewById(R.id.cnlive_shuffle);
  331 + if (shuffleButton != null) {
  332 + shuffleButton.setOnClickListener(componentListener);
  333 + }
  334 + //初始化横竖屏切换
  335 + switchScreenButton = findViewById(R.id.cnlive_switch_screen);
  336 + if (switchScreenButton != null) {
  337 + switchScreenButton.setOnClickListener(componentListener);
  338 + }
  339 + vrButton = findViewById(R.id.cnlive_vr);
  340 + setShowVrButton(false);
  341 +
  342 + Resources resources = context.getResources();
  343 +
  344 + buttonAlphaEnabled =
  345 + (float) resources.getInteger(R.integer.exo_media_button_opacity_percentage_enabled) / 100;
  346 + buttonAlphaDisabled =
  347 + (float) resources.getInteger(R.integer.exo_media_button_opacity_percentage_disabled) / 100;
  348 +
  349 + repeatOffButtonDrawable = resources.getDrawable(R.drawable.exo_controls_repeat_off);
  350 + repeatOneButtonDrawable = resources.getDrawable(R.drawable.exo_controls_repeat_one);
  351 + repeatAllButtonDrawable = resources.getDrawable(R.drawable.exo_controls_repeat_all);
  352 + shuffleOnButtonDrawable = resources.getDrawable(R.drawable.exo_controls_shuffle_on);
  353 + shuffleOffButtonDrawable = resources.getDrawable(R.drawable.exo_controls_shuffle_off);
  354 + repeatOffButtonContentDescription =
  355 + resources.getString(R.string.exo_controls_repeat_off_description);
  356 + repeatOneButtonContentDescription =
  357 + resources.getString(R.string.exo_controls_repeat_one_description);
  358 + repeatAllButtonContentDescription =
  359 + resources.getString(R.string.exo_controls_repeat_all_description);
  360 + shuffleOnContentDescription = resources.getString(R.string.exo_controls_shuffle_on_description);
  361 + shuffleOffContentDescription =
  362 + resources.getString(R.string.exo_controls_shuffle_off_description);
  363 + } else {
  364 + basePortraitView = LayoutInflater.from(context).inflate(getControllViewId(), /* root= */ this);
  365 + }
  366 +
  367 + if (null != componentListener && null != basePortraitView) {
  368 + initPortraitView(basePortraitView, componentListener);
  369 + }
  370 + }
  371 +
  372 +
  373 + protected abstract void initPortraitView(View basePortraitView, CNLiveBaseVideoControlView.ComponentListener onClickListener);
  374 +
  375 + @SuppressWarnings("ResourceType")
  376 + private static @RepeatModeUtil.RepeatToggleModes
  377 + int getRepeatToggleModes(
  378 + TypedArray a, @RepeatModeUtil.RepeatToggleModes int repeatToggleModes) {
  379 + return a.getInt(R.styleable.PlayerControlView_repeat_toggle_modes, repeatToggleModes);
  380 + }
  381 +
  382 + /**
  383 + * Returns the {@link Player} currently being controlled by this view, or null if no player is
  384 + * set.
  385 + */
  386 + @Nullable
  387 + public VideoPlayer getPlayer() {
  388 + return player;
  389 + }
  390 +
  391 + /**
  392 + * Sets the {@link Player} to control.
  393 + *
  394 + * @param player The {@link Player} to control, or {@code null} to detach the current player. Only
  395 + * players which are accessed on the main thread are supported ({@code
  396 + * player.getApplicationLooper() == Looper.getMainLooper()}).
  397 + */
  398 + public void setPlayer(@Nullable VideoPlayer player) {
  399 + Assertions.checkState(Looper.myLooper() == Looper.getMainLooper());
  400 + Assertions.checkArgument(
  401 + player == null || player.getApplicationLooper() == Looper.getMainLooper());
  402 + if (this.player == player) {
  403 + return;
  404 + }
  405 + if (this.player != null && null != componentListener) {
  406 + this.player.removeListener(componentListener);
  407 + }
  408 + this.player = player;
  409 + if (player != null && null != componentListener) {
  410 + player.addListener(componentListener);
  411 + }
  412 + updateAll();
  413 + }
  414 +
  415 + /**
  416 + * Sets whether the time bar should show all windows, as opposed to just the current one. If the
  417 + * timeline has a period with unknown duration or more than {@link
  418 + * #MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR} windows the time bar will fall back to showing a single
  419 + * window.
  420 + *
  421 + * @param showMultiWindowTimeBar Whether the time bar should show all windows.
  422 + */
  423 + public void setShowMultiWindowTimeBar(boolean showMultiWindowTimeBar) {
  424 + this.showMultiWindowTimeBar = showMultiWindowTimeBar;
  425 + updateTimeline();
  426 + }
  427 +
  428 + /**
  429 + * Sets the millisecond positions of extra ad markers relative to the start of the window (or
  430 + * timeline, if in multi-window mode) and whether each extra ad has been played or not. The
  431 + * markers are shown in addition to any ad markers for ads in the player's timeline.
  432 + *
  433 + * @param extraAdGroupTimesMs The millisecond timestamps of the extra ad markers to show, or
  434 + * {@code null} to show no extra ad markers.
  435 + * @param extraPlayedAdGroups Whether each ad has been played. Must be the same length as {@code
  436 + * extraAdGroupTimesMs}, or {@code null} if {@code extraAdGroupTimesMs} is {@code null}.
  437 + */
  438 + public void setExtraAdGroupMarkers(
  439 + @Nullable long[] extraAdGroupTimesMs, @Nullable boolean[] extraPlayedAdGroups) {
  440 + if (extraAdGroupTimesMs == null) {
  441 + this.extraAdGroupTimesMs = new long[0];
  442 + this.extraPlayedAdGroups = new boolean[0];
  443 + } else {
  444 + extraPlayedAdGroups = Assertions.checkNotNull(extraPlayedAdGroups);
  445 + Assertions.checkArgument(extraAdGroupTimesMs.length == extraPlayedAdGroups.length);
  446 + this.extraAdGroupTimesMs = extraAdGroupTimesMs;
  447 + this.extraPlayedAdGroups = extraPlayedAdGroups;
  448 + }
  449 + updateTimeline();
  450 + }
  451 +
  452 + /**
  453 + * Adds a {@link CNLiveBaseVideoControlView.VisibilityListener}.
  454 + *
  455 + * @param listener The listener to be notified about visibility changes.
  456 + */
  457 + public void addVisibilityListener(CNLiveBaseVideoControlView.VisibilityListener listener) {
  458 + visibilityListeners.add(listener);
  459 + }
  460 +
  461 + /**
  462 + * Removes a {@link CNLiveBaseVideoControlView.VisibilityListener}.
  463 + *
  464 + * @param listener The listener to be removed.
  465 + */
  466 + public void removeVisibilityListener(CNLiveBaseVideoControlView.VisibilityListener listener) {
  467 + visibilityListeners.remove(listener);
  468 + }
  469 +
  470 + /**
  471 + * Sets the {@link CNLiveBaseVideoControlView.ProgressUpdateListener}.
  472 + *
  473 + * @param listener The listener to be notified about when progress is updated.
  474 + */
  475 + public void setProgressUpdateListener(@Nullable CNLiveBaseVideoControlView.ProgressUpdateListener listener) {
  476 +// this.progressUpdateListener = listener;
  477 +
  478 + }
  479 +
  480 + /**
  481 + * Sets the {@link PlaybackPreparer}.
  482 + *
  483 + * @param playbackPreparer The {@link PlaybackPreparer}, or null to remove the current playback
  484 + * preparer.
  485 + */
  486 + public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
  487 + this.playbackPreparer = playbackPreparer;
  488 + }
  489 +
  490 +// /**
  491 +// * Sets the {@link com.google.android.exoplayer2.ControlDispatcher}.
  492 +// *
  493 +// * @param controlDispatcher The {@link com.google.android.exoplayer2.ControlDispatcher}, or null
  494 +// * to use {@link com.google.android.exoplayer2.DefaultControlDispatcher}.
  495 +// */
  496 +// public void setControlDispatcher(
  497 +// @Nullable CNLiveControlDispatcher controlDispatcher) {
  498 +// this.controlDispatcher =
  499 +// controlDispatcher == null
  500 +// ? new CNLiveDefaultControlDispatcher()
  501 +// : controlDispatcher;
  502 +// }
  503 +
  504 + /**
  505 + * Sets the rewind increment in milliseconds.
  506 + *
  507 + * @param rewindMs The rewind increment in milliseconds. A non-positive value will cause the
  508 + * rewind button to be disabled.
  509 + */
  510 + public void setRewindIncrementMs(int rewindMs) {
  511 + this.rewindMs = rewindMs;
  512 + updateNavigation();
  513 + }
  514 +
  515 + /**
  516 + * Sets the fast forward increment in milliseconds.
  517 + *
  518 + * @param fastForwardMs The fast forward increment in milliseconds. A non-positive value will
  519 + * cause the fast forward button to be disabled.
  520 + */
  521 + public void setFastForwardIncrementMs(int fastForwardMs) {
  522 + this.fastForwardMs = fastForwardMs;
  523 + updateNavigation();
  524 + }
  525 +
  526 + /**
  527 + * Returns the playback controls timeout. The playback controls are automatically hidden after
  528 + * this duration of time has elapsed without user input.
  529 + *
  530 + * @return The duration in milliseconds. A non-positive value indicates that the controls will
  531 + * remain visible indefinitely.
  532 + */
  533 + public int getShowTimeoutMs() {
  534 + return showTimeoutMs;
  535 + }
  536 +
  537 + /**
  538 + * Sets the playback controls timeout. The playback controls are automatically hidden after this
  539 + * duration of time has elapsed without user input.
  540 + *
  541 + * @param showTimeoutMs The duration in milliseconds. A non-positive value will cause the controls
  542 + * to remain visible indefinitely.
  543 + */
  544 + public void setShowTimeoutMs(int showTimeoutMs) {
  545 + this.showTimeoutMs = showTimeoutMs;
  546 + if (isVisible()) {
  547 + // Reset the timeout.
  548 + hideAfterTimeout();
  549 + }
  550 + }
  551 +
  552 + /**
  553 + * Returns which repeat toggle modes are enabled.
  554 + *
  555 + * @return The currently enabled {@link RepeatModeUtil.RepeatToggleModes}.
  556 + */
  557 + public @RepeatModeUtil.RepeatToggleModes
  558 + int getRepeatToggleModes() {
  559 + return repeatToggleModes;
  560 + }
  561 +
  562 + /**
  563 + * Sets which repeat toggle modes are enabled.
  564 + *
  565 + * @param repeatToggleModes A set of {@link RepeatModeUtil.RepeatToggleModes}.
  566 + */
  567 + public void setRepeatToggleModes(@RepeatModeUtil.RepeatToggleModes int repeatToggleModes) {
  568 + this.repeatToggleModes = repeatToggleModes;
  569 + if (player != null) {
  570 + @Player.RepeatMode int currentMode = player.getRepeatMode();
  571 + if (repeatToggleModes == RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE
  572 + && currentMode != Player.REPEAT_MODE_OFF) {
  573 + setPlayerRepeatMode(Player.REPEAT_MODE_OFF);
  574 + } else if (repeatToggleModes == RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE
  575 + && currentMode == Player.REPEAT_MODE_ALL) {
  576 + setPlayerRepeatMode(Player.REPEAT_MODE_ONE);
  577 + } else if (repeatToggleModes == RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL
  578 + && currentMode == Player.REPEAT_MODE_ONE) {
  579 + setPlayerRepeatMode(Player.REPEAT_MODE_ALL);
  580 + }
  581 + }
  582 +// updateRepeatModeButton();
  583 + }
  584 +
  585 + /**
  586 + * Returns whether the shuffle button is shown.
  587 + */
  588 + public boolean getShowShuffleButton() {
  589 + return showShuffleButton;
  590 + }
  591 +
  592 + /**
  593 + * Sets whether the shuffle button is shown.
  594 + *
  595 + * @param showShuffleButton Whether the shuffle button is shown.
  596 + */
  597 + public void setShowShuffleButton(boolean showShuffleButton) {
  598 + this.showShuffleButton = showShuffleButton;
  599 + updateShuffleButton();
  600 + }
  601 +
  602 + /**
  603 + * Returns whether the VR button is shown.
  604 + */
  605 + public boolean getShowVrButton() {
  606 + return vrButton != null && vrButton.getVisibility() == VISIBLE;
  607 + }
  608 +
  609 + /**
  610 + * Sets whether the VR button is shown.
  611 + *
  612 + * @param showVrButton Whether the VR button is shown.
  613 + */
  614 + public void setShowVrButton(boolean showVrButton) {
  615 + if (vrButton != null) {
  616 + vrButton.setVisibility(showVrButton ? VISIBLE : GONE);
  617 + }
  618 + }
  619 +
  620 + /**
  621 + * Sets listener for the VR button.
  622 + *
  623 + * @param onClickListener Listener for the VR button, or null to clear the listener.
  624 + */
  625 + public void setVrButtonListener(@Nullable OnClickListener onClickListener) {
  626 + if (vrButton != null) {
  627 + vrButton.setOnClickListener(onClickListener);
  628 + }
  629 + }
  630 +
  631 + /**
  632 + * Sets the minimum interval between time bar position updates.
  633 + *
  634 + * <p>Note that smaller intervals, e.g. 33ms, will result in a smooth movement but will use more
  635 + * CPU resources while the time bar is visible, whereas larger intervals, e.g. 200ms, will result
  636 + * in a step-wise update with less CPU usage.
  637 + *
  638 + * @param minUpdateIntervalMs The minimum interval between time bar position updates, in
  639 + * milliseconds.
  640 + */
  641 + public void setTimeBarMinUpdateInterval(int minUpdateIntervalMs) {
  642 + // Do not accept values below 16ms (60fps) and larger than the maximum update interval.
  643 + timeBarMinUpdateIntervalMs =
  644 + Util.constrainValue(minUpdateIntervalMs, 16, MAX_UPDATE_INTERVAL_MS);
  645 + }
  646 +
  647 + /**
  648 + * Shows the playback controls. If {@link #getShowTimeoutMs()} is positive then the controls will
  649 + * be automatically hidden after this duration of time has elapsed without user input.
  650 + */
  651 + public void show() {
  652 + if (!isVisible()) {
  653 +// setVisibility(VISIBLE);
  654 + if (null != basePortraitView) {
  655 + basePortraitView.setVisibility(VISIBLE);
  656 + }
  657 + if (null != baseLandscapeView) {
  658 + baseLandscapeView.setVisibility(GONE);
  659 + }
  660 + if (null != visibilityListeners && visibilityListeners.size() > 0) {
  661 + for (CNLiveBaseVideoControlView.VisibilityListener visibilityListener : visibilityListeners) {
  662 + visibilityListener.onVisibilityChange(getVisibility());
  663 + }
  664 + }
  665 + updateAll();
  666 + requestPlayPauseFocus();
  667 + }
  668 + // Call hideAfterTimeout even if already visible to reset the timeout.
  669 + hideAfterTimeout();
  670 + }
  671 +
  672 + /**
  673 + * Hides the controller.
  674 + */
  675 + public void hide() {
  676 + if (isVisible()) {
  677 +// setVisibility(GONE);
  678 + if (null != basePortraitView) {
  679 + basePortraitView.setVisibility(GONE);
  680 + }
  681 + if (null != visibilityListeners && visibilityListeners.size() > 0) {
  682 + for (CNLiveBaseVideoControlView.VisibilityListener visibilityListener : visibilityListeners) {
  683 + visibilityListener.onVisibilityChange(getVisibility());
  684 + }
  685 + }
  686 + removeCallbacks(updateProgressAction);
  687 + removeCallbacks(hideAction);
  688 + hideAtMs = C.TIME_UNSET;
  689 + }
  690 + }
  691 +
  692 + /**
  693 + * Returns whether the controller is currently visible.
  694 + */
  695 + public boolean isVisible() {
  696 + return getVisibility() == VISIBLE;
  697 + }
  698 +
  699 + private void hideAfterTimeout() {
  700 + removeCallbacks(hideAction);
  701 + if (showTimeoutMs > 0) {
  702 + hideAtMs = SystemClock.uptimeMillis() + showTimeoutMs;
  703 + if (isAttachedToWindow) {
  704 + postDelayed(hideAction, showTimeoutMs);
  705 + }
  706 + } else {
  707 + hideAtMs = C.TIME_UNSET;
  708 + }
  709 + }
  710 +
  711 + private void updateAll() {
  712 + updatePlayPauseButton();
  713 + updateNavigation();
  714 +// updateRepeatModeButton();
  715 + updateShuffleButton();
  716 + updateTimeline();
  717 + }
  718 +
  719 + private void updatePlayPauseButton() {
  720 + if (!isVisible() || !isAttachedToWindow) {
  721 + return;
  722 + }
  723 + boolean requestPlayPauseFocus = false;
  724 + boolean shouldShowPauseButton = shouldShowPauseButton();
  725 + if (playButton != null) {
  726 + requestPlayPauseFocus |= shouldShowPauseButton && playButton.isFocused();
  727 + playButton.setVisibility(shouldShowPauseButton ? GONE : VISIBLE);
  728 + }
  729 + if (pauseButton != null) {
  730 + requestPlayPauseFocus |= !shouldShowPauseButton && pauseButton.isFocused();
  731 + pauseButton.setVisibility(shouldShowPauseButton ? VISIBLE : GONE);
  732 + }
  733 + if (requestPlayPauseFocus) {
  734 + requestPlayPauseFocus();
  735 + }
  736 + }
  737 +
  738 + private void updateNavigation() {
  739 + if (!isVisible() || !isAttachedToWindow) {
  740 + return;
  741 + }
  742 +
  743 + @Nullable VideoPlayer player = this.player;
  744 + boolean enableSeeking = false;
  745 + boolean enablePrevious = false;
  746 + boolean enableRewind = false;
  747 + boolean enableFastForward = false;
  748 + boolean enableNext = false;
  749 + if (player != null) {
  750 + Timeline timeline = player.getCurrentTimeline();
  751 + if (!timeline.isEmpty() && !player.isPlayingAd()) {
  752 + timeline.getWindow(player.getCurrentWindowIndex(), window);
  753 + boolean isSeekable = window.isSeekable;
  754 + enableSeeking = isSeekable;
  755 + enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious();
  756 + enableRewind = isSeekable && rewindMs > 0;
  757 + enableFastForward = isSeekable && fastForwardMs > 0;
  758 + enableNext = window.isDynamic || player.hasNext();
  759 + }
  760 + }
  761 +
  762 + setButtonEnabled(enablePrevious, previousButton);
  763 + setButtonEnabled(enableRewind, rewindButton);
  764 + setButtonEnabled(enableFastForward, fastForwardButton);
  765 + setButtonEnabled(enableNext, nextButton);
  766 + if (timeBar != null) {
  767 + timeBar.setEnabled(enableSeeking);
  768 + }
  769 + }
  770 +//
  771 +// private void updateRepeatModeButton() {
  772 +// if (!isVisible() || !isAttachedToWindow || repeatToggleButton == null) {
  773 +// return;
  774 +// }
  775 +//
  776 +// if (repeatToggleModes == RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE) {
  777 +// repeatToggleButton.setVisibility(VISIBLE);
  778 +// return;
  779 +// }
  780 +//
  781 +// @Nullable Player player = this.player;
  782 +// if (player == null) {
  783 +// setButtonEnabled(false, repeatToggleButton);
  784 +// repeatToggleButton.setImageDrawable(repeatOffButtonDrawable);
  785 +// repeatToggleButton.setContentDescription(repeatOffButtonContentDescription);
  786 +// return;
  787 +// }
  788 +//
  789 +// setButtonEnabled(true, repeatToggleButton);
  790 +// switch (player.getRepeatMode()) {
  791 +// case Player.REPEAT_MODE_OFF:
  792 +// repeatToggleButton.setImageDrawable(repeatOffButtonDrawable);
  793 +// repeatToggleButton.setContentDescription(repeatOffButtonContentDescription);
  794 +// break;
  795 +// case Player.REPEAT_MODE_ONE:
  796 +// repeatToggleButton.setImageDrawable(repeatOneButtonDrawable);
  797 +// repeatToggleButton.setContentDescription(repeatOneButtonContentDescription);
  798 +// break;
  799 +// case Player.REPEAT_MODE_ALL:
  800 +// repeatToggleButton.setImageDrawable(repeatAllButtonDrawable);
  801 +// repeatToggleButton.setContentDescription(repeatAllButtonContentDescription);
  802 +// break;
  803 +// default:
  804 +// // Never happens.
  805 +// }
  806 +// repeatToggleButton.setVisibility(VISIBLE);
  807 +// }
  808 +
  809 + private void updateShuffleButton() {
  810 + if (!isVisible() || !isAttachedToWindow || shuffleButton == null) {
  811 + return;
  812 + }
  813 +
  814 + @Nullable VideoPlayer player = this.player;
  815 + if (!showShuffleButton) {
  816 + shuffleButton.setVisibility(GONE);
  817 + } else if (player == null) {
  818 + setButtonEnabled(false, shuffleButton);
  819 + shuffleButton.setImageDrawable(shuffleOffButtonDrawable);
  820 + shuffleButton.setContentDescription(shuffleOffContentDescription);
  821 + } else {
  822 + setButtonEnabled(true, shuffleButton);
  823 + shuffleButton.setImageDrawable(
  824 + player.getShuffleModeEnabled() ? shuffleOnButtonDrawable : shuffleOffButtonDrawable);
  825 + shuffleButton.setContentDescription(
  826 + player.getShuffleModeEnabled()
  827 + ? shuffleOnContentDescription
  828 + : shuffleOffContentDescription);
  829 + }
  830 + }
  831 +
  832 + private void updateTimeline() {
  833 + @Nullable VideoPlayer player = this.player;
  834 + if (player == null) {
  835 + return;
  836 + }
  837 + if (null == adGroupTimesMs || null == playedAdGroups) {
  838 + return;
  839 + }
  840 + multiWindowTimeBar =
  841 + showMultiWindowTimeBar && canShowMultiWindowTimeBar(player.getCurrentTimeline(), window);
  842 + currentWindowOffset = 0;
  843 + long durationUs = 0;
  844 + int adGroupCount = 0;
  845 + Timeline timeline = player.getCurrentTimeline();
  846 + if (!timeline.isEmpty()) {
  847 + int currentWindowIndex = player.getCurrentWindowIndex();
  848 + int firstWindowIndex = multiWindowTimeBar ? 0 : currentWindowIndex;
  849 + int lastWindowIndex = multiWindowTimeBar ? timeline.getWindowCount() - 1 : currentWindowIndex;
  850 + for (int i = firstWindowIndex; i <= lastWindowIndex; i++) {
  851 + if (i == currentWindowIndex) {
  852 + currentWindowOffset = C.usToMs(durationUs);
  853 + }
  854 + timeline.getWindow(i, window);
  855 + if (window.durationUs == C.TIME_UNSET) {
  856 + Assertions.checkState(!multiWindowTimeBar);
  857 + break;
  858 + }
  859 + for (int j = window.firstPeriodIndex; j <= window.lastPeriodIndex; j++) {
  860 + timeline.getPeriod(j, period);
  861 + int periodAdGroupCount = period.getAdGroupCount();
  862 + for (int adGroupIndex = 0; adGroupIndex < periodAdGroupCount; adGroupIndex++) {
  863 + long adGroupTimeInPeriodUs = period.getAdGroupTimeUs(adGroupIndex);
  864 + if (adGroupTimeInPeriodUs == C.TIME_END_OF_SOURCE) {
  865 + if (period.durationUs == C.TIME_UNSET) {
  866 + // Don't show ad markers for postrolls in periods with unknown duration.
  867 + continue;
  868 + }
  869 + adGroupTimeInPeriodUs = period.durationUs;
  870 + }
  871 + long adGroupTimeInWindowUs = adGroupTimeInPeriodUs + period.getPositionInWindowUs();
  872 + if (adGroupTimeInWindowUs >= 0 && adGroupTimeInWindowUs <= window.durationUs) {
  873 + if (adGroupCount == adGroupTimesMs.length) {
  874 + int newLength = adGroupTimesMs.length == 0 ? 1 : adGroupTimesMs.length * 2;
  875 + adGroupTimesMs = Arrays.copyOf(adGroupTimesMs, newLength);
  876 + playedAdGroups = Arrays.copyOf(playedAdGroups, newLength);
  877 + }
  878 + adGroupTimesMs[adGroupCount] = C.usToMs(durationUs + adGroupTimeInWindowUs);
  879 + playedAdGroups[adGroupCount] = period.hasPlayedAdGroup(adGroupIndex);
  880 + adGroupCount++;
  881 + }
  882 + }
  883 + }
  884 + durationUs += window.durationUs;
  885 + }
  886 + }
  887 + long durationMs = C.usToMs(durationUs);
  888 + if (durationView != null) {
  889 + durationView.setText(Util.getStringForTime(formatBuilder, formatter, durationMs));
  890 + }
  891 + if (timeBar != null) {
  892 + timeBar.setDuration(durationMs);
  893 + int extraAdGroupCount = extraAdGroupTimesMs.length;
  894 + int totalAdGroupCount = adGroupCount + extraAdGroupCount;
  895 + if (totalAdGroupCount > adGroupTimesMs.length) {
  896 + adGroupTimesMs = Arrays.copyOf(adGroupTimesMs, totalAdGroupCount);
  897 + playedAdGroups = Arrays.copyOf(playedAdGroups, totalAdGroupCount);
  898 + }
  899 + System.arraycopy(extraAdGroupTimesMs, 0, adGroupTimesMs, adGroupCount, extraAdGroupCount);
  900 + System.arraycopy(extraPlayedAdGroups, 0, playedAdGroups, adGroupCount, extraAdGroupCount);
  901 + timeBar.setAdGroupTimesMs(adGroupTimesMs, playedAdGroups, totalAdGroupCount);
  902 + }
  903 + updateProgress();
  904 + }
  905 +
  906 + private void updateProgress() {
  907 + if (!isVisible() || !isAttachedToWindow) {
  908 + return;
  909 + }
  910 +
  911 + @Nullable VideoPlayer player = this.player;
  912 + long position = 0;
  913 + long bufferedPosition = 0;
  914 + if (player != null) {
  915 + position = currentWindowOffset + player.getContentPosition();
  916 + bufferedPosition = currentWindowOffset + player.getContentBufferedPosition();
  917 + }
  918 + if (positionView != null && !scrubbing) {
  919 + positionView.setText(Util.getStringForTime(formatBuilder, formatter, position));
  920 + }
  921 + if (timeBar != null) {
  922 + timeBar.setPosition(position);
  923 + timeBar.setBufferedPosition(bufferedPosition);
  924 + }
  925 +// if (progressUpdateListener != null) {
  926 +// progressUpdateListener.onProgressUpdate(position, bufferedPosition);
  927 +// }
  928 +
  929 + // Cancel any pending updates and schedule a new one if necessary.
  930 + removeCallbacks(updateProgressAction);
  931 + int playbackState = player == null ? Player.STATE_IDLE : player.getPlaybackState();
  932 + if (player != null && player.isPlaying()) {
  933 + long mediaTimeDelayMs =
  934 + timeBar != null ? timeBar.getPreferredUpdateDelay() : MAX_UPDATE_INTERVAL_MS;
  935 +
  936 + // Limit delay to the start of the next full second to ensure position display is smooth.
  937 + long mediaTimeUntilNextFullSecondMs = 1000 - position % 1000;
  938 + mediaTimeDelayMs = Math.min(mediaTimeDelayMs, mediaTimeUntilNextFullSecondMs);
  939 +
  940 + // Calculate the delay until the next update in real time, taking playbackSpeed into account.
  941 + float playbackSpeed = player.getPlaybackParameters().speed;
  942 + long delayMs =
  943 + playbackSpeed > 0 ? (long) (mediaTimeDelayMs / playbackSpeed) : MAX_UPDATE_INTERVAL_MS;
  944 +
  945 + // Constrain the delay to avoid too frequent / infrequent updates.
  946 + delayMs = Util.constrainValue(delayMs, timeBarMinUpdateIntervalMs, MAX_UPDATE_INTERVAL_MS);
  947 + postDelayed(updateProgressAction, delayMs);
  948 + } else if (playbackState != Player.STATE_ENDED && playbackState != Player.STATE_IDLE) {
  949 + postDelayed(updateProgressAction, MAX_UPDATE_INTERVAL_MS);
  950 + }
  951 + }
  952 +
  953 + private void requestPlayPauseFocus() {
  954 + boolean shouldShowPauseButton = shouldShowPauseButton();
  955 + if (!shouldShowPauseButton && playButton != null) {
  956 + playButton.requestFocus();
  957 + } else if (shouldShowPauseButton && pauseButton != null) {
  958 + pauseButton.requestFocus();
  959 + }
  960 + }
  961 +
  962 + private void setButtonEnabled(boolean enabled, @Nullable View view) {
  963 + if (view == null) {
  964 + return;
  965 + }
  966 + view.setEnabled(enabled);
  967 + view.setAlpha(enabled ? buttonAlphaEnabled : buttonAlphaDisabled);
  968 + view.setVisibility(VISIBLE);
  969 + }
  970 +
  971 + private void previous(VideoPlayer player) {
  972 + Timeline timeline = player.getCurrentTimeline();
  973 + if (timeline.isEmpty() || player.isPlayingAd()) {
  974 + return;
  975 + }
  976 + int windowIndex = player.getCurrentWindowIndex();
  977 + timeline.getWindow(windowIndex, window);
  978 + int previousWindowIndex = player.getPreviousWindowIndex();
  979 + if (previousWindowIndex != C.INDEX_UNSET
  980 + && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS
  981 + || (window.isDynamic && !window.isSeekable))) {
  982 + seekTo(player, previousWindowIndex, C.TIME_UNSET);
  983 + } else {
  984 + seekTo(player, windowIndex, /* positionMs= */ 0);
  985 + }
  986 + }
  987 +
  988 + /**
  989 + * 播放下一个视频
  990 + *
  991 + * @param player
  992 + */
  993 + public void next(VideoPlayer player) {
  994 + Timeline timeline = player.getCurrentTimeline();
  995 + if (timeline.isEmpty() || player.isPlayingAd()) {
  996 + return;
  997 + }
  998 + int windowIndex = player.getCurrentWindowIndex();
  999 + int nextWindowIndex = player.getNextWindowIndex();
  1000 + if (nextWindowIndex != C.INDEX_UNSET) {
  1001 + seekTo(player, nextWindowIndex, C.TIME_UNSET);
  1002 + } else if (timeline.getWindow(windowIndex, window).isDynamic) {
  1003 + seekTo(player, windowIndex, C.TIME_UNSET);
  1004 + }
  1005 + }
  1006 +
  1007 + public void rewind(VideoPlayer player) {
  1008 + if (player.isCurrentWindowSeekable() && rewindMs > 0) {
  1009 + seekToOffset(player, -rewindMs);
  1010 + }
  1011 + }
  1012 +
  1013 +
  1014 + private void seekToOffset(VideoPlayer player, long offsetMs) {
  1015 + long positionMs = player.getCurrentPosition() + offsetMs;
  1016 + long durationMs = player.getDuration();
  1017 + if (durationMs != C.TIME_UNSET) {
  1018 + positionMs = Math.min(positionMs, durationMs);
  1019 + }
  1020 + positionMs = Math.max(positionMs, 0);
  1021 + seekTo(player, player.getCurrentWindowIndex(), positionMs);
  1022 + }
  1023 +
  1024 + /**
  1025 + * 移动进度条,移动画面
  1026 + *
  1027 + * @param player
  1028 + * @param positionMs
  1029 + */
  1030 + public void seekToTimeBarPosition(VideoPlayer player, long positionMs) {
  1031 + int windowIndex;
  1032 + Timeline timeline = player.getCurrentTimeline();
  1033 + if (multiWindowTimeBar && !timeline.isEmpty()) {
  1034 + int windowCount = timeline.getWindowCount();
  1035 + windowIndex = 0;
  1036 + while (true) {
  1037 + long windowDurationMs = timeline.getWindow(windowIndex, window).getDurationMs();
  1038 + if (positionMs < windowDurationMs) {
  1039 + break;
  1040 + } else if (windowIndex == windowCount - 1) {
  1041 + // Seeking past the end of the last window should seek to the end of the timeline.
  1042 + positionMs = windowDurationMs;
  1043 + break;
  1044 + }
  1045 + positionMs -= windowDurationMs;
  1046 + windowIndex++;
  1047 + }
  1048 + } else {
  1049 + windowIndex = player.getCurrentWindowIndex();
  1050 + }
  1051 + boolean dispatched = seekTo(player, windowIndex, positionMs);
  1052 + if (!dispatched) {
  1053 + // The seek wasn't dispatched then the progress bar scrubber will be in the wrong position.
  1054 + // Trigger a progress update to snap it back.
  1055 + updateProgress();
  1056 + }
  1057 + }
  1058 +
  1059 + private boolean seekTo(VideoPlayer player, int windowIndex, long positionMs) {
  1060 + player.seekTo(windowIndex, positionMs);
  1061 + return true;
  1062 +// return controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs);
  1063 + }
  1064 +
  1065 + @Override
  1066 + public void onAttachedToWindow() {
  1067 + super.onAttachedToWindow();
  1068 +// isAttachedToWindow = true;
  1069 +// if (hideAtMs != C.TIME_UNSET) {
  1070 +// long delayMs = hideAtMs - SystemClock.uptimeMillis();
  1071 +// if (delayMs <= 0) {
  1072 +// hide();
  1073 +// } else {
  1074 +// postDelayed(hideAction, delayMs);
  1075 +// }
  1076 +// } else if (isVisible()) {
  1077 +// hideAfterTimeout();
  1078 +// }
  1079 +// updateAll();
  1080 + }
  1081 +
  1082 + @Override
  1083 + public void onDetachedFromWindow() {
  1084 + super.onDetachedFromWindow();
  1085 + isAttachedToWindow = false;
  1086 + removeCallbacks(updateProgressAction);
  1087 + removeCallbacks(hideAction);
  1088 + }
  1089 +
  1090 + @Override
  1091 + public final boolean dispatchTouchEvent(MotionEvent ev) {
  1092 + if (ev.getAction() == MotionEvent.ACTION_DOWN) {
  1093 + removeCallbacks(hideAction);
  1094 + } else if (ev.getAction() == MotionEvent.ACTION_UP) {
  1095 + hideAfterTimeout();
  1096 + }
  1097 + return super.dispatchTouchEvent(ev);
  1098 + }
  1099 +
  1100 + @Override
  1101 + public boolean dispatchKeyEvent(KeyEvent event) {
  1102 + return dispatchMediaKeyEvent(event) || super.dispatchKeyEvent(event);
  1103 + }
  1104 +
  1105 + /**
  1106 + * Called to process media key events. Any {@link KeyEvent} can be passed but only media key
  1107 + * events will be handled.
  1108 + *
  1109 + * @param event A key event.
  1110 + * @return Whether the key event was handled.
  1111 + */
  1112 + public boolean dispatchMediaKeyEvent(KeyEvent event) {
  1113 + int keyCode = event.getKeyCode();
  1114 + @Nullable VideoPlayer player = this.player;
  1115 + if (player == null || !isHandledMediaKey(keyCode)) {
  1116 + return false;
  1117 + }
  1118 + if (event.getAction() == KeyEvent.ACTION_DOWN) {
  1119 + if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
  1120 + fastForward(player);
  1121 + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) {
  1122 + rewind(player);
  1123 + } else if (event.getRepeatCount() == 0) {
  1124 + switch (keyCode) {
  1125 + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
  1126 + pauseVideo();
  1127 +// controlDispatcher.dispatchSetPlayWhenReady(player, !player.getPlayWhenReady());
  1128 + break;
  1129 + case KeyEvent.KEYCODE_MEDIA_PLAY:
  1130 + //播放视频
  1131 + playVideo();
  1132 + break;
  1133 + case KeyEvent.KEYCODE_MEDIA_PAUSE:
  1134 + //暂停视频
  1135 + pauseVideo();
  1136 + break;
  1137 + case KeyEvent.KEYCODE_MEDIA_NEXT:
  1138 + next(player);
  1139 + break;
  1140 + case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
  1141 + previous(player);
  1142 + break;
  1143 + default:
  1144 + break;
  1145 + }
  1146 + }
  1147 + }
  1148 + return true;
  1149 + }
  1150 +
  1151 + private boolean shouldShowPauseButton() {
  1152 + return player != null
  1153 + && player.getPlaybackState() != Player.STATE_ENDED
  1154 + && player.getPlaybackState() != Player.STATE_IDLE
  1155 + && player.getPlayWhenReady();
  1156 + }
  1157 +
  1158 + @SuppressLint("InlinedApi")
  1159 + private static boolean isHandledMediaKey(int keyCode) {
  1160 + return keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD
  1161 + || keyCode == KeyEvent.KEYCODE_MEDIA_REWIND
  1162 + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
  1163 + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY
  1164 + || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE
  1165 + || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT
  1166 + || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS;
  1167 + }
  1168 +
  1169 + /**
  1170 + * Returns whether the specified {@code timeline} can be shown on a multi-window time bar.
  1171 + *
  1172 + * @param timeline The {@link Timeline} to check.
  1173 + * @param window A scratch {@link Timeline.Window} instance.
  1174 + * @return Whether the specified timeline can be shown on a multi-window time bar.
  1175 + */
  1176 + private static boolean canShowMultiWindowTimeBar(Timeline timeline, Timeline.Window window) {
  1177 + if (timeline.getWindowCount() > MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR) {
  1178 + return false;
  1179 + }
  1180 + int windowCount = timeline.getWindowCount();
  1181 + for (int i = 0; i < windowCount; i++) {
  1182 + if (timeline.getWindow(i, window).durationUs == C.TIME_UNSET) {
  1183 + return false;
  1184 + }
  1185 + }
  1186 + return true;
  1187 + }
  1188 +
  1189 + public final class ComponentListener
  1190 + implements Player.EventListener, TimeBar.OnScrubListener, OnClickListener {
  1191 +
  1192 + @Override
  1193 + public void onScrubStart(TimeBar timeBar, long position) {
  1194 + scrubbing = true;
  1195 + if (positionView != null) {
  1196 + positionView.setText(Util.getStringForTime(formatBuilder, formatter, position));
  1197 + }
  1198 + }
  1199 +
  1200 + @Override
  1201 + public void onScrubMove(TimeBar timeBar, long position) {
  1202 + if (positionView != null) {
  1203 + positionView.setText(Util.getStringForTime(formatBuilder, formatter, position));
  1204 + }
  1205 + }
  1206 +
  1207 + @Override
  1208 + public void onScrubStop(TimeBar timeBar, long position, boolean canceled) {
  1209 + scrubbing = false;
  1210 + if (!canceled && player != null) {
  1211 + seekToTimeBarPosition(player, position);
  1212 + }
  1213 + }
  1214 +
  1215 + @Override
  1216 + public void onPlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) {
  1217 + updatePlayPauseButton();
  1218 + updateProgress();
  1219 + }
  1220 +
  1221 + @Override
  1222 + public void onIsPlayingChanged(boolean isPlaying) {
  1223 + updateProgress();
  1224 + }
  1225 +
  1226 + @Override
  1227 + public void onRepeatModeChanged(int repeatMode) {
  1228 +// updateRepeatModeButton();
  1229 + updateNavigation();
  1230 + }
  1231 +
  1232 + @Override
  1233 + public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
  1234 + updateShuffleButton();
  1235 + updateNavigation();
  1236 + }
  1237 +
  1238 + @Override
  1239 + public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) {
  1240 + updateNavigation();
  1241 + updateTimeline();
  1242 + }
  1243 +
  1244 + @Override
  1245 + public void onTimelineChanged(Timeline timeline, @Player.TimelineChangeReason int reason) {
  1246 + updateNavigation();
  1247 + updateTimeline();
  1248 + }
  1249 +
  1250 + @Override
  1251 + public void onClick(View view) {
  1252 + onViewClick(view);
  1253 + VideoPlayer player = CNLiveBaseVideoControlView.this.player;
  1254 + if (player == null) {
  1255 + return;
  1256 + }
  1257 + if (nextButton == view) {
  1258 + next(player);
  1259 + } else if (previousButton == view) {
  1260 + previous(player);
  1261 + } else if (fastForwardButton == view) {
  1262 + fastForward(player);
  1263 + } else if (rewindButton == view) {
  1264 + rewind(player);
  1265 + } else if (playButton == view) {
  1266 + if (player.getPlaybackState() == Player.STATE_IDLE) {
  1267 + if (playbackPreparer != null) {
  1268 + playbackPreparer.preparePlayback();
  1269 + }
  1270 + } else if (player.getPlaybackState() == Player.STATE_ENDED) {
  1271 + seekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);
  1272 + }
  1273 + playVideo();
  1274 + } else if (pauseButton == view) {
  1275 + pauseVideo();
  1276 + } else if (repeatToggleButton == view) {
  1277 + setPlayerRepeatMode(Player.REPEAT_MODE_ONE);
  1278 +
  1279 +// setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE);
  1280 + } else if (shuffleButton == view) {
  1281 + setShuffleModeEnabled(!player.getShuffleModeEnabled());
  1282 + }
  1283 + //横竖屏切换
  1284 + else if (switchScreenButton == view) {
  1285 + switchScreenDirection();
  1286 + }
  1287 + }
  1288 + }
  1289 +
  1290 + public void setShuffleModeEnabled(boolean isShuffleModeEnabled) {
  1291 + if (null == player) {
  1292 + return;
  1293 + }
  1294 + player.setShuffleModeEnabled(isShuffleModeEnabled);
  1295 +// controlDispatcher.dispatchSetShuffleModeEnabled(player, !player.getShuffleModeEnabled());
  1296 + }
  1297 +
  1298 + /**
  1299 + * 设置播放模式
  1300 + *
  1301 + * @param playerRepeatMode Player.REPEAT_MODE_ALL
  1302 + * Player.REPEAT_MODE_OFF
  1303 + * Player.REPEAT_MODE_ONE
  1304 + */
  1305 + public void setPlayerRepeatMode(int playerRepeatMode) {
  1306 +// controlDispatcher.dispatchSetRepeatMode(
  1307 +// player, playerRepeatMode);
  1308 + player.setRepeatMode(playerRepeatMode);
  1309 + }
  1310 +
  1311 +
  1312 + /**
  1313 + * 切换屏幕方向
  1314 + */
  1315 + public void switchScreenDirection() {
  1316 + if (null != activity && !activity.isFinishing()) {
  1317 + //获取当前屏幕朝向
  1318 + Configuration mConfiguration = activity.getResources().getConfiguration(); //获取设置的配置信息
  1319 + int ori = mConfiguration.orientation; //获取屏幕方向
  1320 + if (ori == mConfiguration.ORIENTATION_LANDSCAPE) {
  1321 + if (null != baseLandscapeView) {
  1322 + baseLandscapeView.setVisibility(GONE);
  1323 + }
  1324 + if (null != basePortraitView) {
  1325 + basePortraitView.setVisibility(VISIBLE);
  1326 + }
  1327 + //强制为竖屏
  1328 + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  1329 + //显示状态栏
  1330 + WindowManager.LayoutParams attr = activity.getWindow().getAttributes();
  1331 + attr.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
  1332 + activity.getWindow().setAttributes(attr);
  1333 + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
  1334 +
  1335 + } else if (ori == mConfiguration.ORIENTATION_PORTRAIT) {
  1336 + if (null == baseLandscapeView) {
  1337 + //切换布局
  1338 + baseLandscapeView = LayoutInflater.from(context).inflate(controllerLayoutId, /* root= */ null);
  1339 + }
  1340 + if (null != basePortraitView) {
  1341 + basePortraitView.setVisibility(GONE);
  1342 + }
  1343 + if (null != baseLandscapeView) {
  1344 + baseLandscapeView.setVisibility(VISIBLE);
  1345 + }
  1346 + //竖屏
  1347 + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//强制为横屏
  1348 + //隐藏状态栏
  1349 + WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
  1350 + lp.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
  1351 + activity.getWindow().setAttributes(lp);
  1352 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
  1353 + }
  1354 + }
  1355 + }
  1356 +
  1357 + /**
  1358 + * 设置播放进度条的颜色
  1359 + *
  1360 + * @param color
  1361 + */
  1362 + public void setPlayedColor(@ColorInt int color) {
  1363 + if (null != timeBar && timeBar instanceof BaseTimerBar) {
  1364 + BaseTimerBar baseTimerBar = (BaseTimerBar) timeBar;
  1365 + baseTimerBar.setPlayedColor(color);
  1366 + }
  1367 + }
  1368 +
  1369 + /**
  1370 + * 快进视频
  1371 + *
  1372 + * @param player
  1373 + */
  1374 + public void fastForward(VideoPlayer player) {
  1375 + if (player.isCurrentWindowSeekable() && fastForwardMs > 0) {
  1376 + seekToOffset(player, fastForwardMs);
  1377 + }
  1378 + }
  1379 +
  1380 + /**
  1381 + * 播放视频
  1382 + */
  1383 + public void playVideo() {
  1384 + if (null == player) {
  1385 + return;
  1386 + }
  1387 + player.play();
  1388 +// if (null == controlDispatcher || null == player) {
  1389 +// return;
  1390 +// }
  1391 +// controlDispatcher.dispatchSetPlayWhenReady(player, true);
  1392 + }
  1393 +
  1394 + /**
  1395 + * 暂停视频
  1396 + */
  1397 + public void pauseVideo() {
  1398 + if (null == player) {
  1399 + return;
  1400 + }
  1401 +// if (null == controlDispatcher || null == player) {
  1402 +// return;
  1403 +// }
  1404 + player.pause();
  1405 +// controlDispatcher.dispatchSetPlayWhenReady(player, false);
  1406 + }
  1407 +
  1408 + public boolean isPlaying() {
  1409 + return player == null ? false : player.isPlaying();
  1410 + }
  1411 +}
0 1412 \ No newline at end of file
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/CNLiveControlDispatcher.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +
  4 +import com.google.android.exoplayer2.Player;
  5 +
  6 +public interface CNLiveControlDispatcher {
  7 + /**
  8 + * Dispatches a {@link Player#setPlayWhenReady(boolean)} operation.
  9 + *
  10 + * @param player The {@link Player} to which the operation should be dispatched.
  11 + * @param playWhenReady Whether playback should proceed when ready.
  12 + * @return True if the operation was dispatched. False if suppressed.
  13 + */
  14 + boolean dispatchSetPlayWhenReady(VideoPlayer player, boolean playWhenReady);
  15 +
  16 + /**
  17 + * Dispatches a {@link Player#seekTo(int, long)} operation.
  18 + *
  19 + * @param player The {@link Player} to which the operation should be dispatched.
  20 + * @param windowIndex The index of the window.
  21 + * @param positionMs The seek position in the specified window, or {@link C#TIME_UNSET} to seek to
  22 + * the window's default position.
  23 + * @return True if the operation was dispatched. False if suppressed.
  24 + */
  25 + boolean dispatchSeekTo(VideoPlayer player, int windowIndex, long positionMs);
  26 +
  27 + /**
  28 + * Dispatches a {@link Player#setRepeatMode(int)} operation.
  29 + *
  30 + * @param player The {@link Player} to which the operation should be dispatched.
  31 + * @param repeatMode The repeat mode.
  32 + * @return True if the operation was dispatched. False if suppressed.
  33 + */
  34 + boolean dispatchSetRepeatMode(VideoPlayer player, @Player.RepeatMode int repeatMode);
  35 +
  36 + /**
  37 + * Dispatches a {@link Player#setShuffleModeEnabled(boolean)} operation.
  38 + *
  39 + * @param player The {@link Player} to which the operation should be dispatched.
  40 + * @param shuffleModeEnabled Whether shuffling is enabled.
  41 + * @return True if the operation was dispatched. False if suppressed.
  42 + */
  43 + boolean dispatchSetShuffleModeEnabled(VideoPlayer player, boolean shuffleModeEnabled);
  44 +
  45 + /**
  46 + * Dispatches a {@link Player#stop()} operation.
  47 + *
  48 + * @param player The {@link Player} to which the operation should be dispatched.
  49 + * @param reset Whether the player should be reset.
  50 + * @return True if the operation was dispatched. False if suppressed.
  51 + */
  52 + boolean dispatchStop(VideoPlayer player, boolean reset);
  53 +}
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/CNLiveDefaultControlDispatcher.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import com.google.android.exoplayer2.ControlDispatcher;
  4 +import com.google.android.exoplayer2.Player;
  5 +
  6 +public class CNLiveDefaultControlDispatcher implements CNLiveControlDispatcher {
  7 +
  8 + @Override
  9 + public boolean dispatchSetPlayWhenReady(VideoPlayer player, boolean playWhenReady) {
  10 +// player.setPlayWhenReady(playWhenReady);
  11 + return true;
  12 + }
  13 +
  14 + @Override
  15 + public boolean dispatchSeekTo(VideoPlayer player, int windowIndex, long positionMs) {
  16 + player.seekTo(windowIndex, positionMs);
  17 + return true;
  18 + }
  19 +
  20 + @Override
  21 + public boolean dispatchSetRepeatMode(VideoPlayer player, @Player.RepeatMode int repeatMode) {
  22 +// player.setRepeatMode(repeatMode);
  23 + return true;
  24 + }
  25 +
  26 + @Override
  27 + public boolean dispatchSetShuffleModeEnabled(VideoPlayer player, boolean shuffleModeEnabled) {
  28 +// player.setShuffleModeEnabled(shuffleModeEnabled);
  29 + return true;
  30 + }
  31 +
  32 + @Override
  33 + public boolean dispatchStop(VideoPlayer player, boolean reset) {
  34 +// player.stop(reset);
  35 + return true;
  36 + }
  37 +}
  38 +
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/CNLiveVideoView.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import android.annotation.SuppressLint;
  4 +import android.annotation.TargetApi;
  5 +import android.content.Context;
  6 +import android.content.res.Resources;
  7 +import android.content.res.TypedArray;
  8 +import android.graphics.Bitmap;
  9 +import android.graphics.BitmapFactory;
  10 +import android.graphics.Matrix;
  11 +import android.graphics.RectF;
  12 +import android.graphics.drawable.BitmapDrawable;
  13 +import android.graphics.drawable.Drawable;
  14 +import android.net.Uri;
  15 +import android.os.Looper;
  16 +import android.util.AttributeSet;
  17 +import android.view.KeyEvent;
  18 +import android.view.LayoutInflater;
  19 +import android.view.MotionEvent;
  20 +import android.view.SurfaceView;
  21 +import android.view.TextureView;
  22 +import android.view.View;
  23 +import android.view.ViewGroup;
  24 +import android.widget.FrameLayout;
  25 +import android.widget.ImageView;
  26 +import android.widget.TextView;
  27 +
  28 +import androidx.annotation.ColorInt;
  29 +import androidx.annotation.IntDef;
  30 +import androidx.annotation.Nullable;
  31 +import androidx.core.content.ContextCompat;
  32 +
  33 +import com.cnlive.cloud.video.R;
  34 +import com.google.android.exoplayer2.C;
  35 +import com.google.android.exoplayer2.ControlDispatcher;
  36 +import com.google.android.exoplayer2.DefaultControlDispatcher;
  37 +import com.google.android.exoplayer2.ExoPlaybackException;
  38 +import com.google.android.exoplayer2.PlaybackPreparer;
  39 +import com.google.android.exoplayer2.Player;
  40 +import com.google.android.exoplayer2.metadata.Metadata;
  41 +import com.google.android.exoplayer2.metadata.flac.PictureFrame;
  42 +import com.google.android.exoplayer2.metadata.id3.ApicFrame;
  43 +import com.google.android.exoplayer2.source.TrackGroupArray;
  44 +import com.google.android.exoplayer2.source.ads.AdsLoader;
  45 +import com.google.android.exoplayer2.text.Cue;
  46 +import com.google.android.exoplayer2.text.TextOutput;
  47 +import com.google.android.exoplayer2.trackselection.TrackSelection;
  48 +import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
  49 +import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
  50 +import com.google.android.exoplayer2.ui.PlayerView;
  51 +import com.google.android.exoplayer2.ui.SubtitleView;
  52 +import com.google.android.exoplayer2.ui.spherical.SingleTapListener;
  53 +import com.google.android.exoplayer2.ui.spherical.SphericalGLSurfaceView;
  54 +import com.google.android.exoplayer2.util.Assertions;
  55 +import com.google.android.exoplayer2.util.ErrorMessageProvider;
  56 +import com.google.android.exoplayer2.util.RepeatModeUtil;
  57 +import com.google.android.exoplayer2.util.Util;
  58 +import com.google.android.exoplayer2.video.VideoDecoderGLSurfaceView;
  59 +import com.google.android.exoplayer2.video.VideoListener;
  60 +
  61 +import java.lang.annotation.Documented;
  62 +import java.lang.annotation.Retention;
  63 +import java.lang.annotation.RetentionPolicy;
  64 +import java.util.ArrayList;
  65 +import java.util.List;
  66 +
  67 +public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProvider {
  68 + /**
  69 + * Determines when the buffering view is shown. One of {@link #SHOW_BUFFERING_NEVER}, {@link
  70 + * #SHOW_BUFFERING_WHEN_PLAYING} or {@link #SHOW_BUFFERING_ALWAYS}.
  71 + */
  72 + @Documented
  73 + @Retention(RetentionPolicy.SOURCE)
  74 + @IntDef({SHOW_BUFFERING_NEVER, SHOW_BUFFERING_WHEN_PLAYING, SHOW_BUFFERING_ALWAYS})
  75 + public @interface ShowBuffering {
  76 + }
  77 +
  78 + /**
  79 + * The buffering view is never shown.
  80 + */
  81 + public static final int SHOW_BUFFERING_NEVER = 0;
  82 + /**
  83 + * The buffering view is shown when the player is in the {@link Player#STATE_BUFFERING buffering}
  84 + * state and {@link Player#getPlayWhenReady() playWhenReady} is {@code true}.
  85 + */
  86 + public static final int SHOW_BUFFERING_WHEN_PLAYING = 1;
  87 + /**
  88 + * The buffering view is always shown when the player is in the {@link Player#STATE_BUFFERING
  89 + * buffering} state.
  90 + */
  91 + public static final int SHOW_BUFFERING_ALWAYS = 2;
  92 + // LINT.ThenChange(../../../../../../res/values/attrs.xml)
  93 +
  94 + // LINT.IfChange
  95 + private static final int SURFACE_TYPE_NONE = 0;
  96 + private static final int SURFACE_TYPE_SURFACE_VIEW = 1;
  97 + private static final int SURFACE_TYPE_TEXTURE_VIEW = 2;
  98 + private static final int SURFACE_TYPE_SPHERICAL_GL_SURFACE_VIEW = 3;
  99 + private static final int SURFACE_TYPE_VIDEO_DECODER_GL_SURFACE_VIEW = 4;
  100 + // LINT.ThenChange(../../../../../../res/values/attrs.xml)
  101 +
  102 + private final CNLiveVideoView.ComponentListener componentListener;
  103 + @Nullable
  104 + private final AspectRatioFrameLayout contentFrame;
  105 + @Nullable
  106 + private final View shutterView;
  107 + @Nullable
  108 + private final View surfaceView;
  109 + @Nullable
  110 + private final ImageView artworkView;
  111 + @Nullable
  112 + private final SubtitleView subtitleView;
  113 + @Nullable
  114 + private final View bufferingView;
  115 + @Nullable
  116 + private final TextView errorMessageView;
  117 + @Nullable
  118 + private CNLiveBaseVideoControlView controller;
  119 + @Nullable
  120 + private final FrameLayout adOverlayFrameLayout;
  121 + @Nullable
  122 + private final FrameLayout overlayFrameLayout;
  123 + private boolean useController;
  124 + @Nullable
  125 + private CNLiveBaseVideoControlView.VisibilityListener controllerVisibilityListener;
  126 + private boolean useArtwork;
  127 + @Nullable
  128 + private Drawable defaultArtwork;
  129 + private @PlayerView.ShowBuffering
  130 + int showBuffering;
  131 + private boolean keepContentOnPlayerReset;
  132 + @Nullable
  133 + private ErrorMessageProvider<? super ExoPlaybackException> errorMessageProvider;
  134 + @Nullable
  135 + private CharSequence customErrorMessage;
  136 + private int controllerShowTimeoutMs;
  137 + private boolean controllerAutoShow;
  138 + private boolean controllerHideDuringAds;
  139 + private boolean controllerHideOnTouch;
  140 + private int textureViewRotation;
  141 + private boolean isTouching;
  142 + private static final int PICTURE_TYPE_FRONT_COVER = 3;
  143 + private static final int PICTURE_TYPE_NOT_SET = -1;
  144 + private View controllerPlaceholder;
  145 + private VideoPlayer player;
  146 +
  147 + public CNLiveVideoView(Context context) {
  148 + this(context, /* attrs= */ null);
  149 + }
  150 +
  151 + public CNLiveVideoView(Context context, @Nullable AttributeSet attrs) {
  152 + this(context, attrs, /* defStyleAttr= */ 0);
  153 + }
  154 +
  155 + @SuppressWarnings({"nullness:argument.type.incompatible", "nullness:method.invocation.invalid"})
  156 + public CNLiveVideoView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  157 + super(context, attrs, defStyleAttr);
  158 + componentListener = new CNLiveVideoView.ComponentListener();
  159 + //初始化videoplayer
  160 + player = new VideoPlayer(context);
  161 + if (isInEditMode()) {
  162 + contentFrame = null;
  163 + shutterView = null;
  164 + surfaceView = null;
  165 + artworkView = null;
  166 + subtitleView = null;
  167 + bufferingView = null;
  168 + errorMessageView = null;
  169 + controller = null;
  170 + adOverlayFrameLayout = null;
  171 + overlayFrameLayout = null;
  172 + ImageView logo = new ImageView(context);
  173 + if (Util.SDK_INT >= 23) {
  174 + configureEditModeLogoV23(getResources(), logo);
  175 + } else {
  176 + configureEditModeLogo(getResources(), logo);
  177 + }
  178 + addView(logo);
  179 + return;
  180 + }
  181 +
  182 + boolean shutterColorSet = false;
  183 + int shutterColor = 0;
  184 + int playerLayoutId = R.layout.layout_base_viedo_view;
  185 + boolean useArtwork = true;
  186 + int defaultArtworkId = 0;
  187 + boolean useController = true;
  188 + int surfaceType = SURFACE_TYPE_SURFACE_VIEW;
  189 + int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
  190 + int controllerShowTimeoutMs = CNLiveBaseVideoControlView.DEFAULT_SHOW_TIMEOUT_MS;
  191 + boolean controllerHideOnTouch = true;
  192 + boolean controllerAutoShow = true;
  193 + boolean controllerHideDuringAds = true;
  194 + int showBuffering = SHOW_BUFFERING_NEVER;
  195 + if (attrs != null) {
  196 + TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PlayerView, 0, 0);
  197 + try {
  198 + shutterColorSet = typedArray.hasValue(R.styleable.PlayerView_shutter_background_color);
  199 + shutterColor = typedArray.getColor(R.styleable.PlayerView_shutter_background_color, shutterColor);
  200 + playerLayoutId = typedArray.getResourceId(R.styleable.PlayerView_player_layout_id, playerLayoutId);
  201 + useArtwork = typedArray.getBoolean(R.styleable.PlayerView_use_artwork, useArtwork);
  202 + defaultArtworkId =
  203 + typedArray.getResourceId(R.styleable.PlayerView_default_artwork, defaultArtworkId);
  204 + useController = typedArray.getBoolean(R.styleable.PlayerView_use_controller, useController);
  205 + surfaceType = typedArray.getInt(R.styleable.PlayerView_surface_type, surfaceType);
  206 + resizeMode = typedArray.getInt(R.styleable.PlayerView_resize_mode, resizeMode);
  207 + controllerShowTimeoutMs =
  208 + typedArray.getInt(R.styleable.PlayerView_show_timeout, controllerShowTimeoutMs);
  209 + controllerHideOnTouch =
  210 + typedArray.getBoolean(R.styleable.PlayerView_hide_on_touch, controllerHideOnTouch);
  211 + controllerAutoShow = typedArray.getBoolean(R.styleable.PlayerView_auto_show, controllerAutoShow);
  212 + showBuffering = typedArray.getInteger(R.styleable.PlayerView_show_buffering, showBuffering);
  213 + keepContentOnPlayerReset =
  214 + typedArray.getBoolean(
  215 + R.styleable.PlayerView_keep_content_on_player_reset, keepContentOnPlayerReset);
  216 + controllerHideDuringAds =
  217 + typedArray.getBoolean(R.styleable.PlayerView_hide_during_ads, controllerHideDuringAds);
  218 + } finally {
  219 + typedArray.recycle();
  220 + }
  221 + }
  222 +
  223 + LayoutInflater.from(context).inflate(playerLayoutId, this);
  224 + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
  225 +
  226 + // Content frame.
  227 + contentFrame = findViewById(R.id.cnlive_content_frame);
  228 + if (contentFrame != null) {
  229 + setResizeModeRaw(contentFrame, resizeMode);
  230 + }
  231 +
  232 + // Shutter view.
  233 + shutterView = findViewById(R.id.cnlive_shutter);
  234 + if (shutterView != null && shutterColorSet) {
  235 + shutterView.setBackgroundColor(shutterColor);
  236 + }
  237 +
  238 + // Create a surface view and insert it into the content frame, if there is one.
  239 + if (contentFrame != null && surfaceType != SURFACE_TYPE_NONE) {
  240 + ViewGroup.LayoutParams params =
  241 + new ViewGroup.LayoutParams(
  242 + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
  243 + switch (surfaceType) {
  244 + case SURFACE_TYPE_TEXTURE_VIEW:
  245 + surfaceView = new TextureView(context);
  246 + break;
  247 + case SURFACE_TYPE_SPHERICAL_GL_SURFACE_VIEW:
  248 + SphericalGLSurfaceView sphericalGLSurfaceView = new SphericalGLSurfaceView(context);
  249 + sphericalGLSurfaceView.setSingleTapListener(componentListener);
  250 + surfaceView = sphericalGLSurfaceView;
  251 + break;
  252 + case SURFACE_TYPE_VIDEO_DECODER_GL_SURFACE_VIEW:
  253 + surfaceView = new VideoDecoderGLSurfaceView(context);
  254 + break;
  255 + default:
  256 + surfaceView = new SurfaceView(context);
  257 + break;
  258 + }
  259 + surfaceView.setLayoutParams(params);
  260 + contentFrame.addView(surfaceView, 0);
  261 + } else {
  262 + surfaceView = null;
  263 + }
  264 +
  265 + // Ad overlay frame layout.
  266 + adOverlayFrameLayout = findViewById(R.id.cnlive_ad_overlay);
  267 +
  268 + // Overlay frame layout.
  269 + overlayFrameLayout = findViewById(R.id.cnlive_overlay);
  270 +
  271 + // Artwork view.
  272 + artworkView = findViewById(R.id.cnlive_artwork);
  273 + this.useArtwork = useArtwork && artworkView != null;
  274 + if (defaultArtworkId != 0) {
  275 + defaultArtwork = ContextCompat.getDrawable(getContext(), defaultArtworkId);
  276 + }
  277 +
  278 + // Subtitle view.
  279 + subtitleView = findViewById(R.id.cnlive_subtitles);
  280 + if (subtitleView != null) {
  281 + subtitleView.setUserDefaultStyle();
  282 + subtitleView.setUserDefaultTextSize();
  283 + }
  284 +
  285 + // Buffering view.
  286 + bufferingView = findViewById(R.id.cnlive_buffering);
  287 + if (bufferingView != null) {
  288 + bufferingView.setVisibility(View.GONE);
  289 + }
  290 + this.showBuffering = showBuffering;
  291 +
  292 + // Error message view.
  293 + errorMessageView = findViewById(R.id.cnlive_error_message);
  294 + if (errorMessageView != null) {
  295 + errorMessageView.setVisibility(View.GONE);
  296 + }
  297 +
  298 + // Playback control view.
  299 + CNLiveBaseVideoControlView customController = findViewById(R.id.cnlive_controller);
  300 + controllerPlaceholder = findViewById(R.id.cnlive_controller_placeholder);
  301 + if (customController != null) {
  302 + this.controller = customController;
  303 + } else if (controllerPlaceholder != null) {
  304 + // Propagate attrs as playbackAttrs so that PlayerControlView's custom attributes are
  305 + // transferred, but standard attributes (e.g. background) are not.
  306 + if (null == controller) {
  307 + this.controller = new CNLiveBaseVideoControlView(context, null, 0, attrs) {
  308 + @Override
  309 + protected void onViewClick(View view) {
  310 +
  311 + }
  312 +
  313 + @Override
  314 + protected void initPortraitView(View basePortraitView, ComponentListener onClickListener) {
  315 +
  316 + }
  317 +
  318 + };
  319 + controller.setId(R.id.cnlive_controller);
  320 + controller.setLayoutParams(controllerPlaceholder.getLayoutParams());
  321 + ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent());
  322 + int controllerIndex = parent.indexOfChild(controllerPlaceholder);
  323 +// parent.removeView(controllerPlaceholder);
  324 + parent.addView(controller, controllerIndex);
  325 + }
  326 + } else {
  327 + this.controller = null;
  328 + }
  329 + this.controllerShowTimeoutMs = controller != null ? controllerShowTimeoutMs : 0;
  330 + this.controllerHideOnTouch = controllerHideOnTouch;
  331 + this.controllerAutoShow = controllerAutoShow;
  332 + this.controllerHideDuringAds = controllerHideDuringAds;
  333 + this.useController = useController && controller != null;
  334 + hideController();
  335 + updateContentDescription();
  336 + if (controller != null) {
  337 + controller.addVisibilityListener(/* listener= */ componentListener);
  338 + }
  339 + Uri mp4VideoUri = Uri.parse("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8");
  340 + player.setDataSource(mp4VideoUri);
  341 + player.prepare(true);
  342 + setPlayer(player);
  343 + }
  344 +
  345 + public void setCustomControllView(CNLiveBaseVideoControlView controllerView) {
  346 + if (null != controllerPlaceholder && null != controllerView) {
  347 + controllerView.setPlayer(player);
  348 + controller.setId(R.id.cnlive_controller);
  349 + controller.setLayoutParams(controllerPlaceholder.getLayoutParams());
  350 + ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent());
  351 + if (null != parent) {
  352 + if (null!=controller){
  353 + parent.removeView(controller);
  354 + }
  355 + int controllerIndex = parent.indexOfChild(controllerPlaceholder);
  356 + this.controller = controllerView;
  357 + parent.addView(controller, controllerIndex);
  358 + parent.removeView(controllerPlaceholder);
  359 + }
  360 +
  361 + }
  362 + }
  363 +
  364 + /**
  365 + * Switches the view targeted by a given {@link Player}.
  366 + *
  367 + * @param player The player whose target view is being switched.
  368 + * @param oldPlayerView The old view to detach from the player.
  369 + * @param newPlayerView The new view to attach to the player.
  370 + */
  371 + public static void switchTargetView(
  372 + Player player, @Nullable PlayerView oldPlayerView, @Nullable PlayerView newPlayerView) {
  373 + if (oldPlayerView == newPlayerView) {
  374 + return;
  375 + }
  376 + // We attach the new view before detaching the old one because this ordering allows the player
  377 + // to swap directly from one surface to another, without transitioning through a state where no
  378 + // surface is attached. This is significantly more efficient and achieves a more seamless
  379 + // transition when using platform provided video decoders.
  380 + if (newPlayerView != null) {
  381 + newPlayerView.setPlayer(player);
  382 + }
  383 + if (oldPlayerView != null) {
  384 + oldPlayerView.setPlayer(null);
  385 + }
  386 + }
  387 +
  388 + /**
  389 + * Returns the player currently set on this view, or null if no player is set.
  390 + */
  391 + @Nullable
  392 + public VideoPlayer getPlayer() {
  393 + return player;
  394 + }
  395 +
  396 + /**
  397 + * Set the {@link Player} to use.
  398 + *
  399 + * <p>To transition a {@link Player} from targeting one view to another, it's recommended to use
  400 + * {@link #switchTargetView(Player, PlayerView, PlayerView)} rather than this method. If you do
  401 + * wish to use this method directly, be sure to attach the player to the new view <em>before</em>
  402 + * calling {@code setPlayer(null)} to detach it from the old one. This ordering is significantly
  403 + * more efficient and may allow for more seamless transitions.
  404 + *
  405 + * @param player The {@link Player} to use, or {@code null} to detach the current player. Only
  406 + * players which are accessed on the main thread are supported ({@code
  407 + * player.getApplicationLooper() == Looper.getMainLooper()}).
  408 + */
  409 + private void setPlayer(@Nullable VideoPlayer player) {
  410 + Assertions.checkState(Looper.myLooper() == Looper.getMainLooper());
  411 + Assertions.checkArgument(
  412 + player == null || player.getApplicationLooper() == Looper.getMainLooper());
  413 +// if (this.player == player) {
  414 +// return;
  415 +// }
  416 + @Nullable VideoPlayer oldPlayer = this.player;
  417 +
  418 + if (oldPlayer != null) {
  419 + oldPlayer.removeListener(componentListener);
  420 + @Nullable Player.VideoComponent oldVideoComponent = oldPlayer.getVideoComponent();
  421 + if (oldVideoComponent != null) {
  422 + oldVideoComponent.removeVideoListener(componentListener);
  423 + if (surfaceView instanceof TextureView) {
  424 + oldVideoComponent.clearVideoTextureView((TextureView) surfaceView);
  425 + } else if (surfaceView instanceof SphericalGLSurfaceView) {
  426 + ((SphericalGLSurfaceView) surfaceView).setVideoComponent(null);
  427 + } else if (surfaceView instanceof VideoDecoderGLSurfaceView) {
  428 + oldVideoComponent.setVideoDecoderOutputBufferRenderer(null);
  429 + } else if (surfaceView instanceof SurfaceView) {
  430 + oldVideoComponent.clearVideoSurfaceView((SurfaceView) surfaceView);
  431 + }
  432 + }
  433 + @Nullable Player.TextComponent oldTextComponent = oldPlayer.getTextComponent();
  434 + if (oldTextComponent != null) {
  435 + oldTextComponent.removeTextOutput(componentListener);
  436 + }
  437 + }
  438 + this.player = player;
  439 + if (useController()) {
  440 + controller.setPlayer(player);
  441 + }
  442 + if (subtitleView != null) {
  443 + subtitleView.setCues(null);
  444 + }
  445 + updateBuffering();
  446 + updateErrorMessage();
  447 + updateForCurrentTrackSelections(/* isNewPlayer= */ true);
  448 + if (player != null) {
  449 + @Nullable Player.VideoComponent newVideoComponent = player.getVideoComponent();
  450 + if (newVideoComponent != null) {
  451 + if (surfaceView instanceof TextureView) {
  452 + newVideoComponent.setVideoTextureView((TextureView) surfaceView);
  453 + } else if (surfaceView instanceof SphericalGLSurfaceView) {
  454 + ((SphericalGLSurfaceView) surfaceView).setVideoComponent(newVideoComponent);
  455 + } else if (surfaceView instanceof VideoDecoderGLSurfaceView) {
  456 + newVideoComponent.setVideoDecoderOutputBufferRenderer(
  457 + ((VideoDecoderGLSurfaceView) surfaceView).getVideoDecoderOutputBufferRenderer());
  458 + } else if (surfaceView instanceof SurfaceView) {
  459 + newVideoComponent.setVideoSurfaceView((SurfaceView) surfaceView);
  460 + }
  461 + newVideoComponent.addVideoListener(componentListener);
  462 + }
  463 + @Nullable Player.TextComponent newTextComponent = player.getTextComponent();
  464 + if (newTextComponent != null) {
  465 + newTextComponent.addTextOutput(componentListener);
  466 + }
  467 + player.addListener(componentListener);
  468 + maybeShowController(false);
  469 + } else {
  470 + hideController();
  471 + }
  472 + }
  473 +
  474 + @Override
  475 + public void setVisibility(int visibility) {
  476 + super.setVisibility(visibility);
  477 + if (surfaceView instanceof SurfaceView) {
  478 + // Work around https://github.com/google/ExoPlayer/issues/3160.
  479 + surfaceView.setVisibility(visibility);
  480 + }
  481 + }
  482 +
  483 + /**
  484 + * Sets the {@link AspectRatioFrameLayout.ResizeMode}.
  485 + *
  486 + * @param resizeMode The {@link AspectRatioFrameLayout.ResizeMode}.
  487 + */
  488 + public void setResizeMode(@AspectRatioFrameLayout.ResizeMode int resizeMode) {
  489 + Assertions.checkStateNotNull(contentFrame);
  490 + contentFrame.setResizeMode(resizeMode);
  491 + }
  492 +
  493 + /**
  494 + * Returns the {@link AspectRatioFrameLayout.ResizeMode}.
  495 + */
  496 + public @AspectRatioFrameLayout.ResizeMode
  497 + int getResizeMode() {
  498 + Assertions.checkStateNotNull(contentFrame);
  499 + return contentFrame.getResizeMode();
  500 + }
  501 +
  502 + /**
  503 + * Returns whether artwork is displayed if present in the media.
  504 + */
  505 + public boolean getUseArtwork() {
  506 + return useArtwork;
  507 + }
  508 +
  509 + /**
  510 + * Sets whether artwork is displayed if present in the media.
  511 + *
  512 + * @param useArtwork Whether artwork is displayed.
  513 + */
  514 + public void setUseArtwork(boolean useArtwork) {
  515 + Assertions.checkState(!useArtwork || artworkView != null);
  516 + if (this.useArtwork != useArtwork) {
  517 + this.useArtwork = useArtwork;
  518 + updateForCurrentTrackSelections(/* isNewPlayer= */ false);
  519 + }
  520 + }
  521 +
  522 + /**
  523 + * Returns the default artwork to display.
  524 + */
  525 + @Nullable
  526 + public Drawable getDefaultArtwork() {
  527 + return defaultArtwork;
  528 + }
  529 +
  530 + /**
  531 + * Sets the default artwork to display if {@code useArtwork} is {@code true} and no artwork is
  532 + * present in the media.
  533 + *
  534 + * @param defaultArtwork the default artwork to display.
  535 + * @deprecated use (@link {@link #setDefaultArtwork(Drawable)} instead.
  536 + */
  537 + @Deprecated
  538 + public void setDefaultArtwork(@Nullable Bitmap defaultArtwork) {
  539 + setDefaultArtwork(
  540 + defaultArtwork == null ? null : new BitmapDrawable(getResources(), defaultArtwork));
  541 + }
  542 +
  543 + /**
  544 + * Sets the default artwork to display if {@code useArtwork} is {@code true} and no artwork is
  545 + * present in the media.
  546 + *
  547 + * @param defaultArtwork the default artwork to display
  548 + */
  549 + public void setDefaultArtwork(@Nullable Drawable defaultArtwork) {
  550 + if (this.defaultArtwork != defaultArtwork) {
  551 + this.defaultArtwork = defaultArtwork;
  552 + updateForCurrentTrackSelections(/* isNewPlayer= */ false);
  553 + }
  554 + }
  555 +
  556 + /**
  557 + * Returns whether the playback controls can be shown.
  558 + */
  559 + public boolean getUseController() {
  560 + return useController;
  561 + }
  562 +
  563 + /**
  564 + * Sets whether the playback controls can be shown. If set to {@code false} the playback controls
  565 + * are never visible and are disconnected from the player.
  566 + *
  567 + * @param useController Whether the playback controls can be shown.
  568 + */
  569 + public void setUseController(boolean useController) {
  570 + Assertions.checkState(!useController || controller != null);
  571 + if (this.useController == useController) {
  572 + return;
  573 + }
  574 + this.useController = useController;
  575 + if (useController()) {
  576 + controller.setPlayer(player);
  577 + } else if (controller != null) {
  578 + controller.hide();
  579 + controller.setPlayer(/* player= */ null);
  580 + }
  581 + updateContentDescription();
  582 + }
  583 +
  584 + /**
  585 + * Sets the background color of the {@code exo_shutter} view.
  586 + *
  587 + * @param color The background color.
  588 + */
  589 + public void setShutterBackgroundColor(int color) {
  590 + if (shutterView != null) {
  591 + shutterView.setBackgroundColor(color);
  592 + }
  593 + }
  594 +
  595 + /**
  596 + * Sets whether the currently displayed video frame or media artwork is kept visible when the
  597 + * player is reset. A player reset is defined to mean the player being re-prepared with different
  598 + * media, the player transitioning to unprepared media, {@link Player#stop(boolean)} being called
  599 + * with {@code reset=true}, or the player being replaced or cleared by calling {@link
  600 + * #setPlayer(Player)}.
  601 + *
  602 + * <p>If enabled, the currently displayed video frame or media artwork will be kept visible until
  603 + * the player set on the view has been successfully prepared with new media and loaded enough of
  604 + * it to have determined the available tracks. Hence enabling this option allows transitioning
  605 + * from playing one piece of media to another, or from using one player instance to another,
  606 + * without clearing the view's content.
  607 + *
  608 + * <p>If disabled, the currently displayed video frame or media artwork will be hidden as soon as
  609 + * the player is reset. Note that the video frame is hidden by making {@code exo_shutter} visible.
  610 + * Hence the video frame will not be hidden if using a custom layout that omits this view.
  611 + *
  612 + * @param keepContentOnPlayerReset Whether the currently displayed video frame or media artwork is
  613 + * kept visible when the player is reset.
  614 + */
  615 + public void setKeepContentOnPlayerReset(boolean keepContentOnPlayerReset) {
  616 + if (this.keepContentOnPlayerReset != keepContentOnPlayerReset) {
  617 + this.keepContentOnPlayerReset = keepContentOnPlayerReset;
  618 + updateForCurrentTrackSelections(/* isNewPlayer= */ false);
  619 + }
  620 + }
  621 +
  622 + /**
  623 + * Sets whether a buffering spinner is displayed when the player is in the buffering state. The
  624 + * buffering spinner is not displayed by default.
  625 + *
  626 + * @param showBuffering Whether the buffering icon is displayed
  627 + * @deprecated Use {@link #setShowBuffering(int)}
  628 + */
  629 + @SuppressLint("WrongConstant")
  630 + @Deprecated
  631 + public void setShowBuffering(boolean showBuffering) {
  632 + setShowBuffering(showBuffering ? SHOW_BUFFERING_WHEN_PLAYING : SHOW_BUFFERING_NEVER);
  633 + }
  634 +
  635 + /**
  636 + * Sets whether a buffering spinner is displayed when the player is in the buffering state. The
  637 + * buffering spinner is not displayed by default.
  638 + *
  639 + * @param showBuffering The mode that defines when the buffering spinner is displayed. One of
  640 + * {@link #SHOW_BUFFERING_NEVER}, {@link #SHOW_BUFFERING_WHEN_PLAYING} and {@link
  641 + * #SHOW_BUFFERING_ALWAYS}.
  642 + */
  643 + public void setShowBuffering(@PlayerView.ShowBuffering int showBuffering) {
  644 + if (this.showBuffering != showBuffering) {
  645 + this.showBuffering = showBuffering;
  646 + updateBuffering();
  647 + }
  648 + }
  649 +
  650 + /**
  651 + * Sets the optional {@link ErrorMessageProvider}.
  652 + *
  653 + * @param errorMessageProvider The error message provider.
  654 + */
  655 + public void setErrorMessageProvider(
  656 + @Nullable ErrorMessageProvider<? super ExoPlaybackException> errorMessageProvider) {
  657 + if (this.errorMessageProvider != errorMessageProvider) {
  658 + this.errorMessageProvider = errorMessageProvider;
  659 + updateErrorMessage();
  660 + }
  661 + }
  662 +
  663 + /**
  664 + * Sets a custom error message to be displayed by the view. The error message will be displayed
  665 + * permanently, unless it is cleared by passing {@code null} to this method.
  666 + *
  667 + * @param message The message to display, or {@code null} to clear a previously set message.
  668 + */
  669 + public void setCustomErrorMessage(@Nullable CharSequence message) {
  670 + Assertions.checkState(errorMessageView != null);
  671 + customErrorMessage = message;
  672 + updateErrorMessage();
  673 + }
  674 +
  675 + @Override
  676 + public boolean dispatchKeyEvent(KeyEvent event) {
  677 + if (player != null && player.isPlayingAd()) {
  678 + return super.dispatchKeyEvent(event);
  679 + }
  680 +
  681 + boolean isDpadKey = isDpadKey(event.getKeyCode());
  682 + boolean handled = false;
  683 + if (isDpadKey && useController() && !controller.isVisible()) {
  684 + // Handle the key event by showing the controller.
  685 + maybeShowController(true);
  686 + handled = true;
  687 + } else if (dispatchMediaKeyEvent(event) || super.dispatchKeyEvent(event)) {
  688 + // The key event was handled as a media key or by the super class. We should also show the
  689 + // controller, or extend its show timeout if already visible.
  690 + maybeShowController(true);
  691 + handled = true;
  692 + } else if (isDpadKey && useController()) {
  693 + // The key event wasn't handled, but we should extend the controller's show timeout.
  694 + maybeShowController(true);
  695 + }
  696 + return handled;
  697 + }
  698 +
  699 + /**
  700 + * Called to process media key events. Any {@link KeyEvent} can be passed but only media key
  701 + * events will be handled. Does nothing if playback controls are disabled.
  702 + *
  703 + * @param event A key event.
  704 + * @return Whether the key event was handled.
  705 + */
  706 + public boolean dispatchMediaKeyEvent(KeyEvent event) {
  707 + return useController() && controller.dispatchMediaKeyEvent(event);
  708 + }
  709 +
  710 + /**
  711 + * Returns whether the controller is currently visible.
  712 + */
  713 + public boolean isControllerVisible() {
  714 + return controller != null && controller.isVisible();
  715 + }
  716 +
  717 + /**
  718 + * Shows the playback controls. Does nothing if playback controls are disabled.
  719 + *
  720 + * <p>The playback controls are automatically hidden during playback after {{@link
  721 + * #getControllerShowTimeoutMs()}}. They are shown indefinitely when playback has not started yet,
  722 + * is paused, has ended or failed.
  723 + */
  724 + public void showController() {
  725 + showController(shouldShowControllerIndefinitely());
  726 + }
  727 +
  728 + /**
  729 + * Hides the playback controls. Does nothing if playback controls are disabled.
  730 + */
  731 + public void hideController() {
  732 + if (controller != null) {
  733 + controller.hide();
  734 + }
  735 + }
  736 +
  737 + /**
  738 + * Returns the playback controls timeout. The playback controls are automatically hidden after
  739 + * this duration of time has elapsed without user input and with playback or buffering in
  740 + * progress.
  741 + *
  742 + * @return The timeout in milliseconds. A non-positive value will cause the controller to remain
  743 + * visible indefinitely.
  744 + */
  745 + public int getControllerShowTimeoutMs() {
  746 + return controllerShowTimeoutMs;
  747 + }
  748 +
  749 + /**
  750 + * Sets the playback controls timeout. The playback controls are automatically hidden after this
  751 + * duration of time has elapsed without user input and with playback or buffering in progress.
  752 + *
  753 + * @param controllerShowTimeoutMs The timeout in milliseconds. A non-positive value will cause the
  754 + * controller to remain visible indefinitely.
  755 + */
  756 + public void setControllerShowTimeoutMs(int controllerShowTimeoutMs) {
  757 + Assertions.checkStateNotNull(controller);
  758 + this.controllerShowTimeoutMs = controllerShowTimeoutMs;
  759 + if (controller.isVisible()) {
  760 + // Update the controller's timeout if necessary.
  761 + showController();
  762 + }
  763 + }
  764 +
  765 + /**
  766 + * Returns whether the playback controls are hidden by touch events.
  767 + */
  768 + public boolean getControllerHideOnTouch() {
  769 + return controllerHideOnTouch;
  770 + }
  771 +
  772 + /**
  773 + * Sets whether the playback controls are hidden by touch events.
  774 + *
  775 + * @param controllerHideOnTouch Whether the playback controls are hidden by touch events.
  776 + */
  777 + public void setControllerHideOnTouch(boolean controllerHideOnTouch) {
  778 + Assertions.checkStateNotNull(controller);
  779 + this.controllerHideOnTouch = controllerHideOnTouch;
  780 + updateContentDescription();
  781 + }
  782 +
  783 + /**
  784 + * Returns whether the playback controls are automatically shown when playback starts, pauses,
  785 + * ends, or fails. If set to false, the playback controls can be manually operated with {@link
  786 + * #showController()} and {@link #hideController()}.
  787 + */
  788 + public boolean getControllerAutoShow() {
  789 + return controllerAutoShow;
  790 + }
  791 +
  792 + /**
  793 + * Sets whether the playback controls are automatically shown when playback starts, pauses, ends,
  794 + * or fails. If set to false, the playback controls can be manually operated with {@link
  795 + * #showController()} and {@link #hideController()}.
  796 + *
  797 + * @param controllerAutoShow Whether the playback controls are allowed to show automatically.
  798 + */
  799 + public void setControllerAutoShow(boolean controllerAutoShow) {
  800 + this.controllerAutoShow = controllerAutoShow;
  801 + }
  802 +
  803 + /**
  804 + * Sets whether the playback controls are hidden when ads are playing. Controls are always shown
  805 + * during ads if they are enabled and the player is paused.
  806 + *
  807 + * @param controllerHideDuringAds Whether the playback controls are hidden when ads are playing.
  808 + */
  809 + public void setControllerHideDuringAds(boolean controllerHideDuringAds) {
  810 + this.controllerHideDuringAds = controllerHideDuringAds;
  811 + }
  812 +
  813 + /**
  814 + * Set the {@link CNLiveBaseVideoControlView.VisibilityListener}.
  815 + *
  816 + * @param listener The listener to be notified about visibility changes, or null to remove the
  817 + * current listener.
  818 + */
  819 + public void setControllerVisibilityListener(
  820 + @Nullable CNLiveBaseVideoControlView.VisibilityListener listener) {
  821 + Assertions.checkStateNotNull(controller);
  822 + if (this.controllerVisibilityListener == listener) {
  823 + return;
  824 + }
  825 + if (this.controllerVisibilityListener != null) {
  826 + controller.removeVisibilityListener(this.controllerVisibilityListener);
  827 + }
  828 + this.controllerVisibilityListener = listener;
  829 + if (listener != null) {
  830 + controller.addVisibilityListener(listener);
  831 + }
  832 + }
  833 +
  834 + /**
  835 + * Sets the {@link PlaybackPreparer}.
  836 + *
  837 + * @param playbackPreparer The {@link PlaybackPreparer}, or null to remove the current playback
  838 + * preparer.
  839 + */
  840 + public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
  841 + Assertions.checkStateNotNull(controller);
  842 + controller.setPlaybackPreparer(playbackPreparer);
  843 + }
  844 +
  845 + /**
  846 + * Sets the {@link ControlDispatcher}.
  847 + *
  848 + * @param controlDispatcher The {@link ControlDispatcher}, or null to use {@link
  849 + * DefaultControlDispatcher}.
  850 + */
  851 + public void setControlDispatcher(@Nullable CNLiveControlDispatcher controlDispatcher) {
  852 + Assertions.checkStateNotNull(controller);
  853 +// controller.setControlDispatcher(controlDispatcher);
  854 + }
  855 +
  856 + /**
  857 + * Sets the rewind increment in milliseconds.
  858 + *
  859 + * @param rewindMs The rewind increment in milliseconds. A non-positive value will cause the
  860 + * rewind button to be disabled.
  861 + */
  862 + public void setRewindIncrementMs(int rewindMs) {
  863 + Assertions.checkStateNotNull(controller);
  864 + controller.setRewindIncrementMs(rewindMs);
  865 + }
  866 +
  867 + /**
  868 + * Sets the fast forward increment in milliseconds.
  869 + *
  870 + * @param fastForwardMs The fast forward increment in milliseconds. A non-positive value will
  871 + * cause the fast forward button to be disabled.
  872 + */
  873 + public void setFastForwardIncrementMs(int fastForwardMs) {
  874 + Assertions.checkStateNotNull(controller);
  875 + controller.setFastForwardIncrementMs(fastForwardMs);
  876 + }
  877 +
  878 + /**
  879 + * Sets which repeat toggle modes are enabled.
  880 + *
  881 + * @param repeatToggleModes A set of {@link RepeatModeUtil.RepeatToggleModes}.
  882 + */
  883 + public void setRepeatToggleModes(@RepeatModeUtil.RepeatToggleModes int repeatToggleModes) {
  884 + Assertions.checkStateNotNull(controller);
  885 + controller.setRepeatToggleModes(repeatToggleModes);
  886 + }
  887 +
  888 + /**
  889 + * Sets whether the shuffle button is shown.
  890 + *
  891 + * @param showShuffleButton Whether the shuffle button is shown.
  892 + */
  893 + public void setShowShuffleButton(boolean showShuffleButton) {
  894 + Assertions.checkStateNotNull(controller);
  895 + controller.setShowShuffleButton(showShuffleButton);
  896 + }
  897 +
  898 + /**
  899 + * Sets whether the time bar should show all windows, as opposed to just the current one.
  900 + *
  901 + * @param showMultiWindowTimeBar Whether to show all windows.
  902 + */
  903 + public void setShowMultiWindowTimeBar(boolean showMultiWindowTimeBar) {
  904 + Assertions.checkStateNotNull(controller);
  905 + controller.setShowMultiWindowTimeBar(showMultiWindowTimeBar);
  906 + }
  907 +
  908 + /**
  909 + * Sets the millisecond positions of extra ad markers relative to the start of the window (or
  910 + * timeline, if in multi-window mode) and whether each extra ad has been played or not. The
  911 + * markers are shown in addition to any ad markers for ads in the player's timeline.
  912 + *
  913 + * @param extraAdGroupTimesMs The millisecond timestamps of the extra ad markers to show, or
  914 + * {@code null} to show no extra ad markers.
  915 + * @param extraPlayedAdGroups Whether each ad has been played, or {@code null} to show no extra ad
  916 + * markers.
  917 + */
  918 + public void setExtraAdGroupMarkers(
  919 + @Nullable long[] extraAdGroupTimesMs, @Nullable boolean[] extraPlayedAdGroups) {
  920 + Assertions.checkStateNotNull(controller);
  921 + controller.setExtraAdGroupMarkers(extraAdGroupTimesMs, extraPlayedAdGroups);
  922 + }
  923 +
  924 + /**
  925 + * Set the {@link AspectRatioFrameLayout.AspectRatioListener}.
  926 + *
  927 + * @param listener The listener to be notified about aspect ratios changes of the video content or
  928 + * the content frame.
  929 + */
  930 + public void setAspectRatioListener(
  931 + @Nullable AspectRatioFrameLayout.AspectRatioListener listener) {
  932 + Assertions.checkStateNotNull(contentFrame);
  933 + contentFrame.setAspectRatioListener(listener);
  934 + }
  935 +
  936 + /**
  937 + * Gets the view onto which video is rendered. This is a:
  938 + *
  939 + * <ul>
  940 + * <li>{@link SurfaceView} by default, or if the {@code surface_type} attribute is set to {@code
  941 + * surface_view}.
  942 + * <li>{@link TextureView} if {@code surface_type} is {@code texture_view}.
  943 + * <li>{@link SphericalGLSurfaceView} if {@code surface_type} is {@code
  944 + * spherical_gl_surface_view}.
  945 + * <li>{@link VideoDecoderGLSurfaceView} if {@code surface_type} is {@code
  946 + * video_decoder_gl_surface_view}.
  947 + * <li>{@code null} if {@code surface_type} is {@code none}.
  948 + * </ul>
  949 + *
  950 + * @return The {@link SurfaceView}, {@link TextureView}, {@link SphericalGLSurfaceView}, {@link
  951 + * VideoDecoderGLSurfaceView} or {@code null}.
  952 + */
  953 + @Nullable
  954 + public View getVideoSurfaceView() {
  955 + return surfaceView;
  956 + }
  957 +
  958 + /**
  959 + * Gets the overlay {@link FrameLayout}, which can be populated with UI elements to show on top of
  960 + * the player.
  961 + *
  962 + * @return The overlay {@link FrameLayout}, or {@code null} if the layout has been customized and
  963 + * the overlay is not present.
  964 + */
  965 + @Nullable
  966 + public FrameLayout getOverlayFrameLayout() {
  967 + return overlayFrameLayout;
  968 + }
  969 +
  970 + /**
  971 + * Gets the {@link SubtitleView}.
  972 + *
  973 + * @return The {@link SubtitleView}, or {@code null} if the layout has been customized and the
  974 + * subtitle view is not present.
  975 + */
  976 + @Nullable
  977 + public SubtitleView getSubtitleView() {
  978 + return subtitleView;
  979 + }
  980 +
  981 + @Override
  982 + public boolean onTouchEvent(MotionEvent event) {
  983 + if (!useController() || player == null) {
  984 + return false;
  985 + }
  986 + switch (event.getAction()) {
  987 + case MotionEvent.ACTION_DOWN:
  988 + isTouching = true;
  989 + return true;
  990 + case MotionEvent.ACTION_UP:
  991 + if (isTouching) {
  992 + isTouching = false;
  993 + performClick();
  994 + return true;
  995 + }
  996 + return false;
  997 + default:
  998 + return false;
  999 + }
  1000 + }
  1001 +
  1002 + @Override
  1003 + public boolean performClick() {
  1004 + super.performClick();
  1005 + return toggleControllerVisibility();
  1006 + }
  1007 +
  1008 + @Override
  1009 + public boolean onTrackballEvent(MotionEvent ev) {
  1010 + if (!useController() || player == null) {
  1011 + return false;
  1012 + }
  1013 + maybeShowController(true);
  1014 + return true;
  1015 + }
  1016 +
  1017 + /**
  1018 + * Should be called when the player is visible to the user and if {@code surface_type} is {@code
  1019 + * spherical_gl_surface_view}. It is the counterpart to {@link #onPause()}.
  1020 + *
  1021 + * <p>This method should typically be called in {@code Activity.onStart()}, or {@code
  1022 + * Activity.onResume()} for API versions &lt;= 23.
  1023 + */
  1024 + public void onResume() {
  1025 + if (surfaceView instanceof SphericalGLSurfaceView) {
  1026 + ((SphericalGLSurfaceView) surfaceView).onResume();
  1027 + }
  1028 + }
  1029 +
  1030 + /**
  1031 + * Should be called when the player is no longer visible to the user and if {@code surface_type}
  1032 + * is {@code spherical_gl_surface_view}. It is the counterpart to {@link #onResume()}.
  1033 + *
  1034 + * <p>This method should typically be called in {@code Activity.onStop()}, or {@code
  1035 + * Activity.onPause()} for API versions &lt;= 23.
  1036 + */
  1037 + public void onPause() {
  1038 + if (surfaceView instanceof SphericalGLSurfaceView) {
  1039 + ((SphericalGLSurfaceView) surfaceView).onPause();
  1040 + }
  1041 + }
  1042 +
  1043 + /**
  1044 + * Called when there's a change in the aspect ratio of the content being displayed. The default
  1045 + * implementation sets the aspect ratio of the content frame to that of the content, unless the
  1046 + * content view is a {@link SphericalGLSurfaceView} in which case the frame's aspect ratio is
  1047 + * cleared.
  1048 + *
  1049 + * @param contentAspectRatio The aspect ratio of the content.
  1050 + * @param contentFrame The content frame, or {@code null}.
  1051 + * @param contentView The view that holds the content being displayed, or {@code null}.
  1052 + */
  1053 + protected void onContentAspectRatioChanged(
  1054 + float contentAspectRatio,
  1055 + @Nullable AspectRatioFrameLayout contentFrame,
  1056 + @Nullable View contentView) {
  1057 + if (contentFrame != null) {
  1058 + contentFrame.setAspectRatio(
  1059 + contentView instanceof SphericalGLSurfaceView ? 0 : contentAspectRatio);
  1060 + }
  1061 + }
  1062 +
  1063 + // AdsLoader.AdViewProvider implementation.
  1064 +
  1065 + @Override
  1066 + public ViewGroup getAdViewGroup() {
  1067 + return Assertions.checkStateNotNull(
  1068 + adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback");
  1069 + }
  1070 +
  1071 + @Override
  1072 + public View[] getAdOverlayViews() {
  1073 + ArrayList<View> overlayViews = new ArrayList<>();
  1074 + if (overlayFrameLayout != null) {
  1075 + overlayViews.add(overlayFrameLayout);
  1076 + }
  1077 + if (controller != null) {
  1078 + overlayViews.add(controller);
  1079 + }
  1080 + return overlayViews.toArray(new View[0]);
  1081 + }
  1082 +
  1083 + // Internal methods.
  1084 +
  1085 + private boolean useController() {
  1086 + if (useController) {
  1087 + Assertions.checkStateNotNull(controller);
  1088 + return true;
  1089 + }
  1090 + return false;
  1091 + }
  1092 +
  1093 + private boolean useArtwork() {
  1094 + if (useArtwork) {
  1095 + Assertions.checkStateNotNull(artworkView);
  1096 + return true;
  1097 + }
  1098 + return false;
  1099 + }
  1100 +
  1101 + private boolean toggleControllerVisibility() {
  1102 + if (!useController() || player == null) {
  1103 + return false;
  1104 + }
  1105 + if (!controller.isVisible()) {
  1106 + maybeShowController(true);
  1107 + } else if (controllerHideOnTouch) {
  1108 + controller.hide();
  1109 + }
  1110 + return true;
  1111 + }
  1112 +
  1113 + /**
  1114 + * Shows the playback controls, but only if forced or shown indefinitely.
  1115 + */
  1116 + private void maybeShowController(boolean isForced) {
  1117 + if (isPlayingAd() && controllerHideDuringAds) {
  1118 + return;
  1119 + }
  1120 + if (useController()) {
  1121 + boolean wasShowingIndefinitely = controller.isVisible() && controller.getShowTimeoutMs() <= 0;
  1122 + boolean shouldShowIndefinitely = shouldShowControllerIndefinitely();
  1123 + if (isForced || wasShowingIndefinitely || shouldShowIndefinitely) {
  1124 + showController(shouldShowIndefinitely);
  1125 + }
  1126 + }
  1127 + }
  1128 +
  1129 + private boolean shouldShowControllerIndefinitely() {
  1130 + if (player == null) {
  1131 + return true;
  1132 + }
  1133 + int playbackState = player.getPlaybackState();
  1134 + return controllerAutoShow
  1135 + && (playbackState == Player.STATE_IDLE
  1136 + || playbackState == Player.STATE_ENDED
  1137 + || !player.getPlayWhenReady());
  1138 + }
  1139 +
  1140 + private void showController(boolean showIndefinitely) {
  1141 + if (!useController()) {
  1142 + return;
  1143 + }
  1144 + controller.setShowTimeoutMs(showIndefinitely ? 0 : controllerShowTimeoutMs);
  1145 + controller.show();
  1146 + }
  1147 +
  1148 + private boolean isPlayingAd() {
  1149 + return player != null && player.isPlayingAd() && player.getPlayWhenReady();
  1150 + }
  1151 +
  1152 + private void updateForCurrentTrackSelections(boolean isNewPlayer) {
  1153 + @Nullable VideoPlayer player = this.player;
  1154 + if (player == null || player.getCurrentTrackGroups().isEmpty()) {
  1155 + if (!keepContentOnPlayerReset) {
  1156 + hideArtwork();
  1157 + closeShutter();
  1158 + }
  1159 + return;
  1160 + }
  1161 +
  1162 + if (isNewPlayer && !keepContentOnPlayerReset) {
  1163 + // Hide any video from the previous player.
  1164 + closeShutter();
  1165 + }
  1166 +
  1167 + TrackSelectionArray selections = player.getCurrentTrackSelections();
  1168 + for (int i = 0; i < selections.length; i++) {
  1169 + if (player.getRendererType(i) == C.TRACK_TYPE_VIDEO && selections.get(i) != null) {
  1170 + // Video enabled so artwork must be hidden. If the shutter is closed, it will be opened in
  1171 + // onRenderedFirstFrame().
  1172 + hideArtwork();
  1173 + return;
  1174 + }
  1175 + }
  1176 +
  1177 + // Video disabled so the shutter must be closed.
  1178 + closeShutter();
  1179 + // Display artwork if enabled and available, else hide it.
  1180 + if (useArtwork()) {
  1181 + for (int i = 0; i < selections.length; i++) {
  1182 + @Nullable TrackSelection selection = selections.get(i);
  1183 + if (selection != null) {
  1184 + for (int j = 0; j < selection.length(); j++) {
  1185 + @Nullable Metadata metadata = selection.getFormat(j).metadata;
  1186 + if (metadata != null && setArtworkFromMetadata(metadata)) {
  1187 + return;
  1188 + }
  1189 + }
  1190 + }
  1191 + }
  1192 + if (setDrawableArtwork(defaultArtwork)) {
  1193 + return;
  1194 + }
  1195 + }
  1196 + // Artwork disabled or unavailable.
  1197 + hideArtwork();
  1198 + }
  1199 +
  1200 + private boolean setArtworkFromMetadata(Metadata metadata) {
  1201 + boolean isArtworkSet = false;
  1202 + int currentPictureType = PICTURE_TYPE_NOT_SET;
  1203 + for (int i = 0; i < metadata.length(); i++) {
  1204 + Metadata.Entry metadataEntry = metadata.get(i);
  1205 + int pictureType;
  1206 + byte[] bitmapData;
  1207 + if (metadataEntry instanceof ApicFrame) {
  1208 + bitmapData = ((ApicFrame) metadataEntry).pictureData;
  1209 + pictureType = ((ApicFrame) metadataEntry).pictureType;
  1210 + } else if (metadataEntry instanceof PictureFrame) {
  1211 + bitmapData = ((PictureFrame) metadataEntry).pictureData;
  1212 + pictureType = ((PictureFrame) metadataEntry).pictureType;
  1213 + } else {
  1214 + continue;
  1215 + }
  1216 + // Prefer the first front cover picture. If there aren't any, prefer the first picture.
  1217 + if (currentPictureType == PICTURE_TYPE_NOT_SET || pictureType == PICTURE_TYPE_FRONT_COVER) {
  1218 + Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length);
  1219 + isArtworkSet = setDrawableArtwork(new BitmapDrawable(getResources(), bitmap));
  1220 + currentPictureType = pictureType;
  1221 + if (currentPictureType == PICTURE_TYPE_FRONT_COVER) {
  1222 + break;
  1223 + }
  1224 + }
  1225 + }
  1226 + return isArtworkSet;
  1227 + }
  1228 +
  1229 + private boolean setDrawableArtwork(@Nullable Drawable drawable) {
  1230 + if (drawable != null) {
  1231 + int drawableWidth = drawable.getIntrinsicWidth();
  1232 + int drawableHeight = drawable.getIntrinsicHeight();
  1233 + if (drawableWidth > 0 && drawableHeight > 0) {
  1234 + float artworkAspectRatio = (float) drawableWidth / drawableHeight;
  1235 + onContentAspectRatioChanged(artworkAspectRatio, contentFrame, artworkView);
  1236 + artworkView.setImageDrawable(drawable);
  1237 + artworkView.setVisibility(VISIBLE);
  1238 + return true;
  1239 + }
  1240 + }
  1241 + return false;
  1242 + }
  1243 +
  1244 + private void hideArtwork() {
  1245 + if (artworkView != null) {
  1246 + artworkView.setImageResource(android.R.color.transparent); // Clears any bitmap reference.
  1247 + artworkView.setVisibility(INVISIBLE);
  1248 + }
  1249 + }
  1250 +
  1251 + private void closeShutter() {
  1252 + if (shutterView != null) {
  1253 + shutterView.setVisibility(View.VISIBLE);
  1254 + }
  1255 + }
  1256 +
  1257 + private void updateBuffering() {
  1258 + if (bufferingView != null) {
  1259 + boolean showBufferingSpinner =
  1260 + player != null
  1261 + && player.getPlaybackState() == Player.STATE_BUFFERING
  1262 + && (showBuffering == SHOW_BUFFERING_ALWAYS
  1263 + || (showBuffering == SHOW_BUFFERING_WHEN_PLAYING && player.getPlayWhenReady()));
  1264 + bufferingView.setVisibility(showBufferingSpinner ? View.VISIBLE : View.GONE);
  1265 + }
  1266 + }
  1267 +
  1268 + private void updateErrorMessage() {
  1269 + if (errorMessageView != null) {
  1270 + if (customErrorMessage != null) {
  1271 + errorMessageView.setText(customErrorMessage);
  1272 + errorMessageView.setVisibility(View.VISIBLE);
  1273 + return;
  1274 + }
  1275 + @Nullable ExoPlaybackException error = player != null ? player.getPlaybackError() : null;
  1276 + if (error != null && errorMessageProvider != null) {
  1277 + CharSequence errorMessage = errorMessageProvider.getErrorMessage(error).second;
  1278 + errorMessageView.setText(errorMessage);
  1279 + errorMessageView.setVisibility(View.VISIBLE);
  1280 + } else {
  1281 + errorMessageView.setVisibility(View.GONE);
  1282 + }
  1283 + }
  1284 + }
  1285 +
  1286 + private void updateContentDescription() {
  1287 + if (controller == null || !useController) {
  1288 + setContentDescription(/* contentDescription= */ null);
  1289 + } else if (controller.getVisibility() == View.VISIBLE) {
  1290 + setContentDescription(
  1291 + /* contentDescription= */ controllerHideOnTouch
  1292 + ? getResources().getString(R.string.exo_controls_hide)
  1293 + : null);
  1294 + } else {
  1295 + setContentDescription(
  1296 + /* contentDescription= */ getResources().getString(R.string.exo_controls_show));
  1297 + }
  1298 + }
  1299 +
  1300 + @TargetApi(23)
  1301 + private static void configureEditModeLogoV23(Resources resources, ImageView logo) {
  1302 + logo.setImageDrawable(resources.getDrawable(R.drawable.exo_edit_mode_logo, null));
  1303 + logo.setBackgroundColor(resources.getColor(R.color.exo_edit_mode_background_color, null));
  1304 + }
  1305 +
  1306 + private static void configureEditModeLogo(Resources resources, ImageView logo) {
  1307 + logo.setImageDrawable(resources.getDrawable(R.drawable.exo_edit_mode_logo));
  1308 + logo.setBackgroundColor(resources.getColor(R.color.exo_edit_mode_background_color));
  1309 + }
  1310 +
  1311 + @SuppressWarnings("ResourceType")
  1312 + private static void setResizeModeRaw(AspectRatioFrameLayout aspectRatioFrame, int resizeMode) {
  1313 + aspectRatioFrame.setResizeMode(resizeMode);
  1314 + }
  1315 +
  1316 + /**
  1317 + * Applies a texture rotation to a {@link TextureView}.
  1318 + */
  1319 + private static void applyTextureViewRotation(TextureView textureView, int textureViewRotation) {
  1320 + Matrix transformMatrix = new Matrix();
  1321 + float textureViewWidth = textureView.getWidth();
  1322 + float textureViewHeight = textureView.getHeight();
  1323 + if (textureViewWidth != 0 && textureViewHeight != 0 && textureViewRotation != 0) {
  1324 + float pivotX = textureViewWidth / 2;
  1325 + float pivotY = textureViewHeight / 2;
  1326 + transformMatrix.postRotate(textureViewRotation, pivotX, pivotY);
  1327 +
  1328 + // After rotation, scale the rotated texture to fit the TextureView size.
  1329 + RectF originalTextureRect = new RectF(0, 0, textureViewWidth, textureViewHeight);
  1330 + RectF rotatedTextureRect = new RectF();
  1331 + transformMatrix.mapRect(rotatedTextureRect, originalTextureRect);
  1332 + transformMatrix.postScale(
  1333 + textureViewWidth / rotatedTextureRect.width(),
  1334 + textureViewHeight / rotatedTextureRect.height(),
  1335 + pivotX,
  1336 + pivotY);
  1337 + }
  1338 + textureView.setTransform(transformMatrix);
  1339 + }
  1340 +
  1341 + @SuppressLint("InlinedApi")
  1342 + private boolean isDpadKey(int keyCode) {
  1343 + return keyCode == KeyEvent.KEYCODE_DPAD_UP
  1344 + || keyCode == KeyEvent.KEYCODE_DPAD_UP_RIGHT
  1345 + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
  1346 + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN_RIGHT
  1347 + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN
  1348 + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN_LEFT
  1349 + || keyCode == KeyEvent.KEYCODE_DPAD_LEFT
  1350 + || keyCode == KeyEvent.KEYCODE_DPAD_UP_LEFT
  1351 + || keyCode == KeyEvent.KEYCODE_DPAD_CENTER;
  1352 + }
  1353 +
  1354 + private final class ComponentListener
  1355 + implements Player.EventListener,
  1356 + TextOutput,
  1357 + VideoListener,
  1358 + OnLayoutChangeListener,
  1359 + SingleTapListener,
  1360 + CNLiveBaseVideoControlView.VisibilityListener {
  1361 +
  1362 + // TextOutput implementation
  1363 +
  1364 + @Override
  1365 + public void onCues(List<Cue> cues) {
  1366 + if (subtitleView != null) {
  1367 + subtitleView.onCues(cues);
  1368 + }
  1369 + }
  1370 +
  1371 + // VideoListener implementation
  1372 +
  1373 + @Override
  1374 + public void onVideoSizeChanged(
  1375 + int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
  1376 + float videoAspectRatio =
  1377 + (height == 0 || width == 0) ? 1 : (width * pixelWidthHeightRatio) / height;
  1378 +
  1379 + if (surfaceView instanceof TextureView) {
  1380 + // Try to apply rotation transformation when our surface is a TextureView.
  1381 + if (unappliedRotationDegrees == 90 || unappliedRotationDegrees == 270) {
  1382 + // We will apply a rotation 90/270 degree to the output texture of the TextureView.
  1383 + // In this case, the output video's width and height will be swapped.
  1384 + videoAspectRatio = 1 / videoAspectRatio;
  1385 + }
  1386 + if (textureViewRotation != 0) {
  1387 + surfaceView.removeOnLayoutChangeListener(this);
  1388 + }
  1389 + textureViewRotation = unappliedRotationDegrees;
  1390 + if (textureViewRotation != 0) {
  1391 + // The texture view's dimensions might be changed after layout step.
  1392 + // So add an OnLayoutChangeListener to apply rotation after layout step.
  1393 + surfaceView.addOnLayoutChangeListener(this);
  1394 + }
  1395 + applyTextureViewRotation((TextureView) surfaceView, textureViewRotation);
  1396 + }
  1397 +
  1398 + onContentAspectRatioChanged(videoAspectRatio, contentFrame, surfaceView);
  1399 + }
  1400 +
  1401 + @Override
  1402 + public void onRenderedFirstFrame() {
  1403 + if (shutterView != null) {
  1404 + shutterView.setVisibility(INVISIBLE);
  1405 + }
  1406 + }
  1407 +
  1408 + @Override
  1409 + public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) {
  1410 + updateForCurrentTrackSelections(/* isNewPlayer= */ false);
  1411 + }
  1412 +
  1413 + // Player.EventListener implementation
  1414 +
  1415 + @Override
  1416 + public void onPlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) {
  1417 + updateBuffering();
  1418 + updateErrorMessage();
  1419 + if (isPlayingAd() && controllerHideDuringAds) {
  1420 + hideController();
  1421 + } else {
  1422 + maybeShowController(false);
  1423 + }
  1424 + }
  1425 +
  1426 + @Override
  1427 + public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) {
  1428 + if (isPlayingAd() && controllerHideDuringAds) {
  1429 + hideController();
  1430 + }
  1431 + }
  1432 +
  1433 + // OnLayoutChangeListener implementation
  1434 +
  1435 + @Override
  1436 + public void onLayoutChange(
  1437 + View view,
  1438 + int left,
  1439 + int top,
  1440 + int right,
  1441 + int bottom,
  1442 + int oldLeft,
  1443 + int oldTop,
  1444 + int oldRight,
  1445 + int oldBottom) {
  1446 + applyTextureViewRotation((TextureView) view, textureViewRotation);
  1447 + }
  1448 +
  1449 + // SingleTapListener implementation
  1450 +
  1451 + @Override
  1452 + public boolean onSingleTapUp(MotionEvent e) {
  1453 + return toggleControllerVisibility();
  1454 + }
  1455 +
  1456 + // PlayerControlView.VisibilityListener implementation
  1457 +
  1458 + @Override
  1459 + public void onVisibilityChange(int visibility) {
  1460 + updateContentDescription();
  1461 + }
  1462 + }
  1463 +
  1464 +
  1465 + /**
  1466 + * 设置播放进度条的颜色
  1467 + *
  1468 + * @param color
  1469 + */
  1470 + public void setPlayedProgressColor(@ColorInt int color) {
  1471 + if (null != controller && controller instanceof CNLiveBaseVideoControlView) {
  1472 + CNLiveBaseVideoControlView controlView = controller;
  1473 + controller.setPlayedColor(color);
  1474 + }
  1475 + }
  1476 +
  1477 +}
  1478 +
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/CacheUtil.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import android.content.ContentValues;
  4 +import android.content.Context;
  5 +import android.database.Cursor;
  6 +import android.database.SQLException;
  7 +import android.database.sqlite.SQLiteDatabase;
  8 +import android.database.sqlite.SQLiteOpenHelper;
  9 +import android.text.TextUtils;
  10 +import android.util.Log;
  11 +
  12 +import com.libs.video.cache.utils.DesUtil;
  13 +import com.libs.video.util.Config;
  14 +
  15 +import java.security.InvalidKeyException;
  16 +import java.security.spec.InvalidKeySpecException;
  17 +import java.util.UUID;
  18 +
  19 +import javax.crypto.BadPaddingException;
  20 +import javax.crypto.IllegalBlockSizeException;
  21 +import javax.crypto.NoSuchPaddingException;
  22 +
  23 +public class CacheUtil {
  24 + private static final String TAG = "CacheUtil";
  25 + private final String encodeText=".cnlive.video.";
  26 + /**
  27 + * Database Name
  28 + */
  29 + private static final String DATABASE_NAME = "cnlive_video_data";
  30 +
  31 + /**
  32 + * Database Version
  33 + */
  34 + private static final int DATABASE_VERSION = 1;
  35 +
  36 + /**
  37 + * Table Name
  38 + */
  39 + private static final String DATABASE_TABLE = "tb_video_data";
  40 +
  41 + /**
  42 + * Table columns
  43 + */
  44 + public static final String KEY_NAME = "video_id";
  45 + public static final String KEY_GRADE = "video_url";
  46 + public static final String KEY_ROWID = "id";
  47 +
  48 + /**
  49 + * Database creation sql statement
  50 + */
  51 + private static final String CREATE_TABLE =
  52 + "create table " + DATABASE_TABLE + " (" + KEY_ROWID + " text primary key,"
  53 + + KEY_NAME + " text not null, " + KEY_GRADE + " text not null);";
  54 +
  55 + /**
  56 + * Context
  57 + */
  58 + private final Context mCtx;
  59 + private DatabaseHelper mDbHelper;
  60 + private SQLiteDatabase mDb;
  61 +
  62 + /**
  63 + * Inner private class. Database Helper class for creating and updating database.
  64 + */
  65 + private static class DatabaseHelper extends SQLiteOpenHelper {
  66 + DatabaseHelper(Context context) {
  67 + super(context, DATABASE_NAME, null, DATABASE_VERSION);
  68 + }
  69 +
  70 + /**
  71 + * onCreate method is called for the 1st time when database doesn't exists.
  72 + */
  73 + @Override
  74 + public void onCreate(SQLiteDatabase db) {
  75 + Log.i(TAG, "Creating DataBase: " + CREATE_TABLE);
  76 + db.execSQL(CREATE_TABLE);
  77 + }
  78 +
  79 + /**
  80 + * onUpgrade method is called when database version changes.
  81 + */
  82 + @Override
  83 + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  84 + Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
  85 + + newVersion);
  86 + }
  87 + }
  88 +
  89 + /**
  90 + * Constructor - takes the context to allow the database to be
  91 + * opened/created
  92 + *
  93 + * @param ctx the Context within which to work
  94 + */
  95 + public CacheUtil(Context ctx) {
  96 + this.mCtx = ctx;
  97 + open();
  98 + }
  99 +
  100 + /**
  101 + * This method is used for creating/opening connection
  102 + *
  103 + * @return instance of CacheUtil
  104 + * @throws SQLException
  105 + */
  106 + private CacheUtil open() throws SQLException {
  107 + if (null == mDbHelper) {
  108 + mDbHelper = new DatabaseHelper(mCtx);
  109 + mDb = mDbHelper.getWritableDatabase();
  110 + }
  111 + return this;
  112 + }
  113 +
  114 + /**
  115 + * This method is used for closing the connection.
  116 + */
  117 + public void close() {
  118 + mDbHelper.close();
  119 + }
  120 +
  121 + /**
  122 + * This method is used to create/insert new record record.
  123 + *
  124 + * @param videoId
  125 + * @param videoUrl
  126 + * @return long
  127 + */
  128 + public long add(String videoId, String videoUrl) throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, InvalidKeySpecException, InvalidKeyException {
  129 + ContentValues initialValues = new ContentValues();
  130 + initialValues.put(KEY_ROWID, UUID.randomUUID().toString().replace("-", ""));
  131 + initialValues.put(KEY_NAME, videoId);
  132 + String enVideoUrl= DesUtil.enCrypto(videoUrl, encodeText);
  133 + initialValues.put(KEY_GRADE, enVideoUrl);
  134 + return mDb.insert(DATABASE_TABLE, null, initialValues);
  135 + }
  136 +
  137 + /**
  138 + * This method will delete record.
  139 + *
  140 + * @param rowId
  141 + * @return boolean
  142 + */
  143 + public boolean delete(long rowId) {
  144 + return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
  145 + }
  146 +
  147 + /**
  148 + * This method will deleteAll record.
  149 + *
  150 + * @return
  151 + */
  152 + public boolean deleteAll() {
  153 + return mDb.delete(DATABASE_TABLE, " 1 ", null) > 0;
  154 + }
  155 +
  156 + /**
  157 + * This method will return Cursor holding all the records.
  158 + *
  159 + * @return Cursor
  160 + */
  161 + public Cursor fetchAll() {
  162 + return mDb.query(DATABASE_TABLE, new String[]{KEY_ROWID, KEY_NAME,
  163 + KEY_GRADE}, null, null, null, null, null);
  164 + }
  165 +
  166 +// /**
  167 +// * This method will return Cursor holding the specific record.
  168 +// *
  169 +// * @param id
  170 +// * @return Cursor
  171 +// * @throws SQLException
  172 +// */
  173 +// public Cursor fetch(long id) throws SQLException {
  174 +// Cursor mCursor =
  175 +// mDb.query(true, DATABASE_TABLE, new String[]{KEY_ROWID,
  176 +// KEY_NAME, KEY_GRADE}, KEY_ROWID + "=" + id, null,
  177 +// null, null, null, null);
  178 +// if (mCursor != null) {
  179 +// mCursor.moveToFirst();
  180 +// }
  181 +// return mCursor;
  182 +// }
  183 +
  184 + public String getVideoUri(String videoUid) throws SQLException, IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, InvalidKeySpecException, InvalidKeyException {
  185 + String result = "";
  186 + Cursor mCursor =
  187 + mDb.query(true, DATABASE_TABLE, new String[]{KEY_ROWID,
  188 + KEY_NAME, KEY_GRADE}, KEY_NAME + "=" + videoUid, null,
  189 + null, null, null, null);
  190 + if (mCursor != null && mCursor.getCount() > 0) {
  191 + while (mCursor.moveToNext()) {
  192 + if (mCursor.getColumnIndex(KEY_GRADE) > 0) {
  193 + result = mCursor.getString(mCursor.getColumnIndex(KEY_GRADE));
  194 + if (!TextUtils.isEmpty(result)) {
  195 + mCursor.close();
  196 + result=DesUtil.deCrypto(result, encodeText);
  197 + break;
  198 + }
  199 + }
  200 + }
  201 + }
  202 + return result;
  203 + }
  204 +
  205 + /**
  206 + * This method will update record.
  207 + *
  208 + * @param id
  209 + * @param name
  210 + * @param standard
  211 + * @return boolean
  212 + */
  213 + public boolean update(int id, String name, String standard) {
  214 + ContentValues args = new ContentValues();
  215 + args.put(KEY_NAME, name);
  216 + args.put(KEY_GRADE, standard);
  217 + return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + id, null) > 0;
  218 + }
  219 +}
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/IVideoDataSource.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import android.net.Uri;
  4 +
  5 +public interface IVideoDataSource {
  6 + String getDataSourceId();
  7 +
  8 + String getDataSourceType();
  9 +
  10 + Uri getPlayUri();
  11 +}
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/IVideoPlayer.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import android.net.Uri;
  4 +import android.os.Looper;
  5 +
  6 +import androidx.annotation.Nullable;
  7 +
  8 +import com.google.android.exoplayer2.C;
  9 +import com.google.android.exoplayer2.ExoPlaybackException;
  10 +import com.google.android.exoplayer2.PlaybackParameters;
  11 +import com.google.android.exoplayer2.Player;
  12 +import com.google.android.exoplayer2.Renderer;
  13 +import com.google.android.exoplayer2.Timeline;
  14 +import com.google.android.exoplayer2.source.MediaSource;
  15 +import com.google.android.exoplayer2.source.TrackGroupArray;
  16 +import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
  17 +import com.google.android.exoplayer2.ui.PlayerView;
  18 +import com.libs.video.video.base.IDataSource;
  19 +import com.libs.video.video.base.IMediaPlayer;
  20 +
  21 +public interface IVideoPlayer {
  22 + // /**
  23 +// * 设置播放资源
  24 +// *
  25 +// * @param data
  26 +// */
  27 + void setDataSource(IVideoDataSource data);
  28 +
  29 + void setDataSource(Uri data);
  30 +
  31 + void setPlayView(CNLiveVideoView playView);
  32 +
  33 + void play() throws IllegalStateException;
  34 +
  35 + void stop() throws IllegalStateException;
  36 +
  37 + void pause();
  38 +
  39 + void prepare();
  40 +
  41 + void prepare(boolean isPlay);
  42 +
  43 + void setForegroundMode(boolean foregroundMode);
  44 +
  45 + //默认的
  46 + Looper getApplicationLooper();
  47 +
  48 + void removeListener(Player.EventListener listener);
  49 +
  50 + @Nullable
  51 + Player.VideoComponent getVideoComponent();
  52 +
  53 + @Nullable
  54 + Player.TextComponent getTextComponent();
  55 +
  56 + void addListener(Player.EventListener listener);
  57 +
  58 + @Player.RepeatMode
  59 + int getRepeatMode();
  60 +
  61 + Timeline getCurrentTimeline();
  62 + /**
  63 + * Returns whether the player is currently playing an ad.
  64 + */
  65 + boolean isPlayingAd();
  66 + /**
  67 + * Returns whether a previous window exists, which may depend on the current repeat mode and
  68 + * whether shuffle mode is enabled.
  69 + */
  70 + boolean hasPrevious();
  71 + /**
  72 + * Returns whether a next window exists, which may depend on the current repeat mode and whether
  73 + * shuffle mode is enabled.
  74 + */
  75 + boolean hasNext();
  76 + /**
  77 + * If {@link #isPlayingAd()} returns {@code true}, returns the content position that will be
  78 + * played once all ads in the ad group have finished playing, in milliseconds. If there is no ad
  79 + * playing, the returned position is the same as that returned by {@link #getCurrentPosition()}.
  80 + */
  81 + long getContentPosition();
  82 +
  83 + /**
  84 + * If {@link #isPlayingAd()} returns {@code true}, returns an estimate of the content position in
  85 + * the current content window up to which data is buffered, in milliseconds. If there is no ad
  86 + * playing, the returned position is the same as that returned by {@link #getBufferedPosition()}.
  87 + */
  88 + long getContentBufferedPosition();
  89 + int getPlaybackState();
  90 + /**
  91 + * Returns the currently active playback parameters.
  92 + *
  93 + * @see Player.EventListener#onPlaybackParametersChanged(PlaybackParameters)
  94 + */
  95 + PlaybackParameters getPlaybackParameters();
  96 +
  97 + /**
  98 + * Returns whether shuffling of windows is enabled.
  99 + */
  100 + boolean getShuffleModeEnabled();
  101 + /**
  102 + * Whether playback will proceed when {@link #getPlaybackState()} == {@link #STATE_READY}.
  103 + *
  104 + * @return Whether playback will proceed when ready.
  105 + */
  106 + boolean getPlayWhenReady();
  107 + /**
  108 + * Returns whether the current window is seekable, or {@code false} if the {@link Timeline} is
  109 + * empty.
  110 + *
  111 + * @see Timeline.Window#isSeekable
  112 + */
  113 + boolean isCurrentWindowSeekable();
  114 + int getPreviousWindowIndex();
  115 + /** Returns the playback position in the current content window or ad, in milliseconds. */
  116 + long getCurrentPosition();
  117 + /**
  118 + * Returns the index of the next timeline window to be played, which may depend on the current
  119 + * repeat mode and whether shuffle mode is enabled. Returns {@link C#INDEX_UNSET} if the window
  120 + * currently being played is the last window.
  121 + */
  122 + int getNextWindowIndex();
  123 + /**
  124 + * 释放资源
  125 + */
  126 + void release();
  127 +
  128 + /**
  129 + * 移动资源
  130 + *
  131 + * @param windowIndex 当前位置
  132 + * @param positionMs 毫秒
  133 + */
  134 + void seekTo(int windowIndex, long positionMs);
  135 +
  136 + /**
  137 + * 获取当前进度
  138 + *
  139 + * @return
  140 + */
  141 + int getCurrentWindowIndex();
  142 +
  143 + /**
  144 + * 获取总时长
  145 + *
  146 + * @return
  147 + */
  148 + long getDuration();
  149 +
  150 + /**
  151 + * Returns the available track groups.
  152 + */
  153 + TrackGroupArray getCurrentTrackGroups();
  154 +
  155 + /**
  156 + * Returns the current track selections for each renderer.
  157 + */
  158 + TrackSelectionArray getCurrentTrackSelections();
  159 +
  160 + /**
  161 + * Returns the track type that the renderer at a given index handles.
  162 + *
  163 + * @see Renderer#getTrackType()
  164 + * @param index The index of the renderer.
  165 + * @return One of the {@code TRACK_TYPE_*} constants defined in {@link C}.
  166 + */
  167 + int getRendererType(int index);
  168 + /**
  169 + * Returns whether the player is playing, i.e. {@link #getContentPosition()} is advancing.
  170 + *
  171 + * <p>If {@code false}, then at least one of the following is true:
  172 + *
  173 + * <ul>
  174 + * <li>The {@link #getPlaybackState() playback state} is not {@link #STATE_READY ready}.
  175 + * <li>There is no {@link #getPlayWhenReady() intention to play}.
  176 + * <li>Playback is {@link #getPlaybackSuppressionReason() suppressed for other reasons}.
  177 + * </ul>
  178 + *
  179 + * @return Whether the player is playing.
  180 + */
  181 + boolean isPlaying();
  182 + /**
  183 + * Sets the {@link Player.RepeatMode} to be used for playback.
  184 + *
  185 + * @param repeatMode The repeat mode.
  186 + */
  187 + void setRepeatMode(@Player.RepeatMode int repeatMode);
  188 + /**
  189 + * Sets whether shuffling of windows is enabled.
  190 + *
  191 + * @param shuffleModeEnabled Whether shuffling is enabled.
  192 + */
  193 + void setShuffleModeEnabled(boolean shuffleModeEnabled);
  194 + @Nullable
  195 + ExoPlaybackException getPlaybackError();
  196 + interface OnPreparedListener {
  197 + void onPrepared(IVideoPlayer videoPlayer);
  198 + }
  199 +}
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/MyTestControlView.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import android.content.Context;
  4 +import android.util.AttributeSet;
  5 +import android.view.View;
  6 +
  7 +import androidx.annotation.Nullable;
  8 +
  9 +import com.cnlive.cloud.video.R;
  10 +
  11 +public class MyTestControlView extends CNLiveBaseVideoControlView {
  12 + private View playButton;
  13 +
  14 + public MyTestControlView(Context context) {
  15 + super(context);
  16 + }
  17 +
  18 + public MyTestControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, @Nullable AttributeSet playbackAttrs) {
  19 + super(context, attrs, defStyleAttr, playbackAttrs);
  20 + }
  21 +
  22 + @Override
  23 + protected void initPortraitView(View basePortraitView, ComponentListener onClickListener) {
  24 + playButton = basePortraitView.findViewById(R.id.btn_start);
  25 + playButton.setOnClickListener(onClickListener);
  26 + }
  27 +
  28 +
  29 + @Override
  30 + protected int getControllViewId() {
  31 + return R.layout.test_view;
  32 + }
  33 +
  34 + @Override
  35 + protected void onViewClick(View view) {
  36 + if (view.getId()==R.id.btn_start){
  37 +// if (isPlaying()){
  38 +// pauseVideo();
  39 +// }else {
  40 +// playVideo();
  41 +// }
  42 + switchScreenDirection();
  43 +
  44 + }
  45 + }
  46 +}
... ...
cloud/video/src/main/java/com/libs/video/video/exoplayer/VideoPlayer.java 0 → 100644
  1 +package com.libs.video.video.exoplayer;
  2 +
  3 +import android.content.Context;
  4 +import android.net.Uri;
  5 +import android.os.Looper;
  6 +import android.text.TextUtils;
  7 +import android.util.Log;
  8 +
  9 +import androidx.annotation.Nullable;
  10 +
  11 +import com.google.android.exoplayer2.ExoPlaybackException;
  12 +import com.google.android.exoplayer2.PlaybackParameters;
  13 +import com.google.android.exoplayer2.Player;
  14 +import com.google.android.exoplayer2.SimpleExoPlayer;
  15 +import com.google.android.exoplayer2.Timeline;
  16 +import com.google.android.exoplayer2.ext.rtmp.RtmpDataSourceFactory;
  17 +import com.google.android.exoplayer2.source.MediaSource;
  18 +import com.google.android.exoplayer2.source.ProgressiveMediaSource;
  19 +import com.google.android.exoplayer2.source.TrackGroupArray;
  20 +import com.google.android.exoplayer2.source.dash.DashMediaSource;
  21 +import com.google.android.exoplayer2.source.hls.HlsMediaSource;
  22 +import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
  23 +import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
  24 +import com.google.android.exoplayer2.ui.PlayerView;
  25 +import com.google.android.exoplayer2.upstream.DataSource;
  26 +import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
  27 +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
  28 +import com.google.android.exoplayer2.util.Util;
  29 +import com.libs.video.data.network.Callback;
  30 +import com.libs.video.data.network.DataLoader;
  31 +import com.libs.video.util.AuthUtil;
  32 +import com.libs.video.util.Config;
  33 +import com.libs.video.video.base.IActivityDataSource;
  34 +import com.libs.video.video.base.IChannelDataSource;
  35 +import com.libs.video.video.base.IDataSource;
  36 +import com.umeng.analytics.MobclickAgent;
  37 +
  38 +import java.security.InvalidKeyException;
  39 +import java.security.spec.InvalidKeySpecException;
  40 +import java.util.HashMap;
  41 +import java.util.Map;
  42 +
  43 +import javax.crypto.BadPaddingException;
  44 +import javax.crypto.IllegalBlockSizeException;
  45 +import javax.crypto.NoSuchPaddingException;
  46 +
  47 +public class VideoPlayer implements IVideoPlayer {
  48 + private static final String TAG = VideoPlayer.class.getName();
  49 + public SimpleExoPlayer player;//exo播放器
  50 + // private Player.EventListener eventListener;//播放器监听器
  51 + //init统计使用,onPrepare后当前的对比值和之前的对比值进行对比,不一样就进行统计
  52 + //对比值对于使用视讯云播放的就是播放id,对于使用金山播放器的就是dataSource
  53 + private String probePreCompareStr;
  54 + private String probeCurCompareStr;
  55 + //记录当前是否是镜像播放
  56 + private boolean mMirror = false;
  57 + private Context mContext;
  58 + //测量播放过程中的带宽。 如果不需要,可以为null。
  59 + private DefaultBandwidthMeter bandwidthMeter;
  60 + private IVideoDataSource mDataSource, mProbeSource;//播放资源
  61 + //资源类型
  62 + private MediaSource currentMediaSource;//当前正在播放的资源类型
  63 + private HlsMediaSource hlsMediaSource;//HLS媒体资源
  64 + private SsMediaSource ssMediaSource;//SmoothStreming媒体资源
  65 + private DashMediaSource dashMediaSource;//dash媒体资源
  66 + private ProgressiveMediaSource normalSource;//常规媒体文件
  67 + //资源工厂类型
  68 + private RtmpDataSourceFactory rtmpDataSourceFactory;//rtmp直播
  69 + // Create a data source factory.
  70 + private DataSource.Factory httpDataSourceFactory;
  71 + private final String VIDEO_TYPE_RTMP = "rtmp://";//是直播
  72 + private final String VIDEO_TYPE_MU3U8 = ".m3u8";//是点播
  73 + public static final int SCALE_FULL = 0;//铺满屏幕
  74 + public static final int SCALE_FIT = 1;//按比例
  75 + private DataLoader mDataLoader;//数据加载
  76 + private CacheUtil cacheUtil;//缓存工具
  77 + private OnPreparedListener onPreparedListener;
  78 +
  79 + public VideoPlayer(Context context) {
  80 + cacheUtil = new CacheUtil(context);
  81 + //初始化exoplayer
  82 + //step2. 创建播放器
  83 + player = new SimpleExoPlayer.Builder(context).build();
  84 + mContext = context;
  85 + bandwidthMeter = DefaultBandwidthMeter.getSingletonInstance(context);
  86 + //创建资源工厂
  87 + rtmpDataSourceFactory = new RtmpDataSourceFactory();
  88 + httpDataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(context, "app-name"));
  89 + //数据加载
  90 + mDataLoader = new DataLoader();
  91 + //添加行为监听器
  92 + player.addListener(new Player.EventListener() {
  93 + @Override
  94 + public void onTimelineChanged(Timeline timeline, int reason) {
  95 +
  96 + }
  97 +
  98 + @Override
  99 + public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) {
  100 +
  101 + }
  102 +
  103 + @Override
  104 + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
  105 +
  106 + }
  107 +
  108 + @Override
  109 + public void onLoadingChanged(boolean isLoading) {
  110 +
  111 + }
  112 +
  113 + @Override
  114 + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
  115 +//状态改变监听
  116 + switch (playbackState) {
  117 + //这是初始状态,播放机停止时的状态,以及播放失败时的状态。
  118 + case Player.STATE_IDLE:
  119 + Log.d(TAG, "onPlayerStateChanged: STATE_IDLE");
  120 + break;
  121 + // 播放器无法立即从其当前位置进行播放。这主要是因为需要加载更多的数据。
  122 + case Player.STATE_BUFFERING:
  123 + Log.d(TAG, "onPlayerStateChanged: STATE_BUFFERING");
  124 + break;
  125 + //播放器可以立即从其当前位置进行播放。
  126 + case Player.STATE_READY:
  127 + Log.d(TAG, "onPlayerStateChanged: STATE_READY");
  128 + break;
  129 + //播放器完成了所有媒体的播放。
  130 + case Player.STATE_ENDED:
  131 + Log.d(TAG, "onPlayerStateChanged: STATE_ENDED");
  132 + break;
  133 + }
  134 + }
  135 +
  136 + @Override
  137 + public void onPlaybackSuppressionReasonChanged(int playbackSuppressionReason) {
  138 +
  139 + }
  140 +
  141 + @Override
  142 + public void onIsPlayingChanged(boolean isPlaying) {
  143 +
  144 + }
  145 +
  146 + @Override
  147 + public void onRepeatModeChanged(int repeatMode) {
  148 +
  149 + }
  150 +
  151 + @Override
  152 + public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
  153 +
  154 + }
  155 +
  156 + @Override
  157 + public void onPlayerError(ExoPlaybackException error) {
  158 +
  159 + }
  160 +
  161 + @Override
  162 + public void onPositionDiscontinuity(int reason) {
  163 +
  164 + }
  165 +
  166 + @Override
  167 + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
  168 +
  169 + }
  170 +
  171 + @Override
  172 + public void onSeekProcessed() {
  173 +
  174 + }
  175 + });
  176 + }
  177 +
  178 + /**
  179 + * 获取当前版本
  180 + *
  181 + * @return
  182 + */
  183 + public static String getVersion() {
  184 + return Config.SDK_PLAYER_VERSION;
  185 + }
  186 +
  187 + /**
  188 + * 获取版本号
  189 + *
  190 + * @return
  191 + */
  192 + public static String getVersionCode() {
  193 + StringBuilder buffer = new StringBuilder();
  194 + String[] strArray = getVersion().split("\\.");
  195 +
  196 + for (String aStrArray : strArray) {
  197 + buffer.append(aStrArray);
  198 + }
  199 +
  200 + return buffer.toString();
  201 + }
  202 +
  203 + @Override
  204 + public void setDataSource(IVideoDataSource data) {
  205 + mDataSource = null;
  206 + mDataSource = data;
  207 + Map<String, String> probeData = new HashMap<>();
  208 + probeData.put("mediaId", mDataSource == null ? "" : mDataSource.getDataSourceId());
  209 +// probeData.put("mediaRate", mDataSource == null ? "" : mDataSource.getDataSourceRate());
  210 + probeData.put("mediaType", mDataSource == null ? "" : mDataSource.getDataSourceType());
  211 + probeData.put("mediaUrl", "");
  212 + MobclickAgent.onEvent(mContext, "media_init", probeData);
  213 + //判断该条数据是否在缓存中
  214 + if (null != mDataSource && !TextUtils.isEmpty(mDataSource.getDataSourceId())) {
  215 + //获取缓存的url
  216 + String cachVideoUrl = null;
  217 + try {
  218 + cachVideoUrl = cacheUtil.getVideoUri(mDataSource.getDataSourceId());
  219 + } catch (IllegalBlockSizeException e) {
  220 + e.printStackTrace();
  221 + } catch (NoSuchPaddingException e) {
  222 + e.printStackTrace();
  223 + } catch (BadPaddingException e) {
  224 + e.printStackTrace();
  225 + } catch (InvalidKeySpecException e) {
  226 + e.printStackTrace();
  227 + } catch (InvalidKeyException e) {
  228 + e.printStackTrace();
  229 + }
  230 + //判断是否有缓存数据
  231 + if (!TextUtils.isEmpty(cachVideoUrl)) {
  232 + currentMediaSource = getPlayerMediaSource(cachVideoUrl);
  233 + }
  234 + }
  235 + if (!AuthUtil.isValid()) {
  236 + AuthUtil.auth(new Callback() {
  237 + @Override
  238 + public void onState(int what, String extra, Object obj) {
  239 + if (what > 0) {
  240 +// if (mCNOnInfoListener != null)
  241 +// mCNOnInfoListener.onInfo(MediaPlayer.this, what, 0);
  242 +// loadDataSource(data, curSource.equals(preSource));
  243 + } else if (what < 0) {
  244 +// if (mCNOnErrorListener != null)
  245 +// mCNOnErrorListener.onError(MediaPlayer.this, what, 0);
  246 + }
  247 + }
  248 +
  249 + @Override
  250 + public void onError(String errCode, Exception e) {
  251 +
  252 + }
  253 + });
  254 + } else {
  255 +// loadDataSource(data, curSource.equals(preSource));
  256 + }
  257 + }
  258 +
  259 + @Override
  260 + public void setDataSource(Uri data) {
  261 + currentMediaSource = getPlayerMediaSource(data.toString());
  262 + }
  263 +
  264 + @Override
  265 + public void setPlayView(CNLiveVideoView playView) {
  266 + }
  267 +
  268 + private void loadDataSource(final IDataSource data, boolean retry) {
  269 + String activityId = data instanceof IActivityDataSource ? ((IActivityDataSource) data).getDataActivityId() : "";
  270 + String tag = data instanceof IChannelDataSource ? ((IChannelDataSource) data).getDataTag() : "";
  271 + String channelName = data instanceof IChannelDataSource ? ((IChannelDataSource) data).getChannelName() : "";
  272 + mDataLoader.loadDataSource(data.getDataSourceId(), data.getDataSourceRate(), data.getDataSourceType(), activityId, tag, channelName, retry, new Callback() {
  273 + @Override
  274 + public void onState(int what, String extra, Object obj) {
  275 + if (what > 0) {
  276 + String playerUrl;
  277 + if (data.getDataSourceType() == Config.TYPE_VOD) {
  278 + //缓存数据
  279 + try {
  280 + //暂时只有点播才能缓存
  281 + cacheUtil.add(data.getDataSourceId(), extra);
  282 + } catch (IllegalBlockSizeException e) {
  283 + e.printStackTrace();
  284 + } catch (NoSuchPaddingException e) {
  285 + e.printStackTrace();
  286 + } catch (BadPaddingException e) {
  287 + e.printStackTrace();
  288 + } catch (InvalidKeySpecException e) {
  289 + e.printStackTrace();
  290 + } catch (InvalidKeyException e) {
  291 + e.printStackTrace();
  292 + }
  293 + }
  294 + playerUrl = extra;
  295 + currentMediaSource = getPlayerMediaSource(playerUrl);
  296 +// if (mCNOnInfoListener != null)
  297 +// mCNOnInfoListener.onInfo(MediaPlayer.this, MEDIA_INFO_GET_PLAYURL_SUCCESS, 0);
  298 + } else if (what < 0) {
  299 +// if (mCNOnErrorListener != null)
  300 +// mCNOnErrorListener.onError(MediaPlayer.this, what, 0);
  301 + }
  302 +//
  303 + }
  304 +
  305 + @Override
  306 + public void onError(String errCode, Exception e) {
  307 +
  308 + }
  309 + });
  310 + }
  311 +
  312 + @Override
  313 + public void play() throws IllegalStateException {
  314 + if (null != player) {
  315 + //准备好就开始播放
  316 + player.setPlayWhenReady(true);
  317 + }
  318 + }
  319 +
  320 + @Override
  321 + public void stop() throws IllegalStateException {
  322 + if (null != player) {
  323 + player.stop();
  324 + }
  325 + }
  326 +
  327 + @Override
  328 + public void pause() {
  329 + if (null != player) {
  330 + player.setPlayWhenReady(false);
  331 + }
  332 + }
  333 +
  334 + /**
  335 + * 加载视频
  336 + */
  337 + @Override
  338 + public void prepare() {
  339 + if (null != currentMediaSource && null != player) {
  340 + player.prepare(currentMediaSource);
  341 + }
  342 + }
  343 +
  344 + @Override
  345 + public void prepare(boolean isPlay) {
  346 + if (null != currentMediaSource && null != player) {
  347 + player.prepare(currentMediaSource);
  348 + player.setPlayWhenReady(isPlay);
  349 + }
  350 + }
  351 +
  352 + private MediaSource getPlayerMediaSource(String playerUrl) {
  353 + if (TextUtils.isEmpty(playerUrl)) {
  354 + return null;
  355 + }
  356 +
  357 + Uri uri = Uri.parse(playerUrl);
  358 + int type = Util.inferContentType(!TextUtils.isEmpty(playerUrl) ?
  359 + "." + playerUrl
  360 + : uri.getLastPathSegment());
  361 +// case C.TYPE_DASH:
  362 + if (playerUrl.indexOf(VIDEO_TYPE_MU3U8) != -1) {
  363 + //点播
  364 + hlsMediaSource = new HlsMediaSource.Factory(httpDataSourceFactory).createMediaSource(uri);
  365 + return hlsMediaSource;
  366 + } else if (playerUrl.indexOf(VIDEO_TYPE_RTMP) != -1) {
  367 + normalSource = new ProgressiveMediaSource.Factory(rtmpDataSourceFactory)
  368 + .createMediaSource(uri);
  369 + return normalSource;
  370 + } else {
  371 + normalSource = new ProgressiveMediaSource.Factory(httpDataSourceFactory)
  372 + .createMediaSource(uri);
  373 + return normalSource;
  374 + }
  375 + }
  376 +
  377 +
  378 + @Override
  379 + public void setForegroundMode(boolean foregroundMode) {
  380 + if (null != player) {
  381 + player.setForegroundMode(foregroundMode);
  382 + }
  383 + }
  384 +
  385 + @Override
  386 + public Looper getApplicationLooper() {
  387 + return player.getApplicationLooper();
  388 + }
  389 +
  390 + @Override
  391 + public void removeListener(Player.EventListener listener) {
  392 + player.removeListener(listener);
  393 + }
  394 +
  395 + @Nullable
  396 + @Override
  397 + public Player.VideoComponent getVideoComponent() {
  398 + return player.getVideoComponent();
  399 + }
  400 +
  401 + @Nullable
  402 + @Override
  403 + public Player.TextComponent getTextComponent() {
  404 + return player.getTextComponent();
  405 + }
  406 +
  407 + @Override
  408 + public void addListener(Player.EventListener listener) {
  409 + player.addListener(listener);
  410 + }
  411 +
  412 + @Override
  413 + public int getRepeatMode() {
  414 + return player.getRepeatMode();
  415 + }
  416 +
  417 + @Override
  418 + public Timeline getCurrentTimeline() {
  419 + return player.getCurrentTimeline();
  420 + }
  421 +
  422 + @Override
  423 + public boolean isPlayingAd() {
  424 + return player.isPlayingAd();
  425 + }
  426 +
  427 + @Override
  428 + public boolean hasPrevious() {
  429 + return player.hasPrevious();
  430 + }
  431 +
  432 + @Override
  433 + public boolean hasNext() {
  434 + return player.hasNext();
  435 + }
  436 +
  437 + @Override
  438 + public long getContentPosition() {
  439 + return player.getContentPosition();
  440 + }
  441 +
  442 + @Override
  443 + public long getContentBufferedPosition() {
  444 + return player.getContentBufferedPosition();
  445 + }
  446 +
  447 + @Override
  448 + public int getPlaybackState() {
  449 + return player.getPlaybackState();
  450 + }
  451 +
  452 + @Override
  453 + public PlaybackParameters getPlaybackParameters() {
  454 + return player.getPlaybackParameters();
  455 + }
  456 +
  457 + @Override
  458 + public boolean getShuffleModeEnabled() {
  459 + return player.getShuffleModeEnabled();
  460 + }
  461 +
  462 + @Override
  463 + public boolean getPlayWhenReady() {
  464 + return player.getPlayWhenReady();
  465 + }
  466 +
  467 + @Override
  468 + public boolean isCurrentWindowSeekable() {
  469 + return player.isCurrentWindowSeekable();
  470 + }
  471 +
  472 + @Override
  473 + public int getPreviousWindowIndex() {
  474 + return player.getPreviousWindowIndex();
  475 + }
  476 +
  477 + @Override
  478 + public long getCurrentPosition() {
  479 + return player.getCurrentPosition();
  480 + }
  481 +
  482 + @Override
  483 + public int getNextWindowIndex() {
  484 + return player.getNextWindowIndex();
  485 + }
  486 +
  487 + @Override
  488 + public void release() {
  489 + //释放资源
  490 + if (null != player) {
  491 + player.release();
  492 + }
  493 + }
  494 +
  495 + /**
  496 + * 移动位置
  497 + *
  498 + * @param windowIndex 当前位置
  499 + * @param positionMs 毫秒
  500 + */
  501 + @Override
  502 + public void seekTo(int windowIndex, long positionMs) {
  503 + if (null != player) {
  504 + player.seekTo(windowIndex, positionMs);
  505 + }
  506 + }
  507 +
  508 + /**
  509 + * 获取当前位置
  510 + *
  511 + * @return
  512 + */
  513 + @Override
  514 + public int getCurrentWindowIndex() {
  515 + if (null != player) {
  516 + player.getCurrentWindowIndex();
  517 + }
  518 + return 0;
  519 + }
  520 +
  521 + @Override
  522 + public long getDuration() {
  523 + if (null != player) {
  524 + player.getDuration();
  525 + }
  526 + return 0;
  527 + }
  528 +
  529 + @Override
  530 + public TrackGroupArray getCurrentTrackGroups() {
  531 + return player.getCurrentTrackGroups();
  532 + }
  533 +
  534 + @Override
  535 + public TrackSelectionArray getCurrentTrackSelections() {
  536 + return player.getCurrentTrackSelections();
  537 + }
  538 +
  539 + @Override
  540 + public int getRendererType(int index) {
  541 + return player.getRendererType(index);
  542 + }
  543 +
  544 + @Override
  545 + public boolean isPlaying() {
  546 + return player.isPlaying();
  547 + }
  548 +
  549 + @Override
  550 + public void setRepeatMode(int repeatMode) {
  551 + player.setRepeatMode(repeatMode);
  552 + }
  553 +
  554 + @Override
  555 + public void setShuffleModeEnabled(boolean shuffleModeEnabled) {
  556 + player.setShuffleModeEnabled(shuffleModeEnabled);
  557 + }
  558 +
  559 + @Nullable
  560 + @Override
  561 + public ExoPlaybackException getPlaybackError() {
  562 + return player.getPlaybackError();
  563 + }
  564 +
  565 +}
... ...
cloud/video/src/main/res/drawable/shape_video_top_bg.xml 0 → 100644
  1 +<shape xmlns:android="http://schemas.android.com/apk/res/android">
  2 + <!--
  3 + android:startColor="#aa000000" 渐变起始色值
  4 + android:centerColor="" 渐变中间色值
  5 + android:endColor="#ffffffff" 渐变结束颜色
  6 + android:angle="45" 渐变的方向 默认为0 从做向右 ,90时从下向上 必须为45的整数倍
  7 + android:type="radial" 渐变类型 有三种 线性linear 放射渐变radial 扫描线性渐变sweep
  8 + android:centerX="0.5" 渐变中心相对X坐标只有渐变类型为放射渐变时有效
  9 + android:centerY="0.5" 渐变中心相对Y坐标只有渐变类型为放射渐变时有效
  10 + android:gradientRadius="100" 渐变半径 非线性放射有效
  11 + -->
  12 + <gradient
  13 + android:angle="90"
  14 + android:endColor="#1A000000"
  15 + android:startColor="#80000000"
  16 + android:type="linear" />
  17 +
  18 +</shape>
0 19 \ No newline at end of file
... ...
cloud/video/src/main/res/layout/layout_base_viedo_view.xml 0 → 100644
  1 +<merge xmlns:android="http://schemas.android.com/apk/res/android">
  2 +
  3 + <com.google.android.exoplayer2.ui.AspectRatioFrameLayout
  4 + android:id="@id/cnlive_content_frame"
  5 + android:layout_width="match_parent"
  6 + android:layout_height="match_parent"
  7 + android:layout_gravity="center">
  8 +
  9 + <!-- Video surface will be inserted as the first child of the content frame. -->
  10 +
  11 + <View
  12 + android:id="@id/cnlive_shutter"
  13 + android:layout_width="match_parent"
  14 + android:layout_height="match_parent"
  15 + android:background="#80000000" />
  16 +
  17 + <ImageView
  18 + android:id="@id/cnlive_artwork"
  19 + android:layout_width="match_parent"
  20 + android:layout_height="match_parent"
  21 + android:scaleType="fitXY" />
  22 +
  23 + <com.google.android.exoplayer2.ui.SubtitleView
  24 + android:id="@id/cnlive_subtitles"
  25 + android:layout_width="match_parent"
  26 + android:layout_height="match_parent" />
  27 +
  28 + <ProgressBar
  29 + android:id="@id/cnlive_buffering"
  30 + android:layout_width="wrap_content"
  31 + android:layout_height="wrap_content"
  32 + android:layout_gravity="center"
  33 + android:indeterminate="true" />
  34 +
  35 + <TextView
  36 + android:id="@id/cnlive_error_message"
  37 + android:layout_width="match_parent"
  38 + android:layout_height="match_parent"
  39 + android:layout_gravity="center"
  40 + android:background="@color/exo_error_message_background_color"
  41 + android:gravity="center"
  42 + android:padding="16dp" />
  43 +
  44 + </com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
  45 +
  46 + <FrameLayout
  47 + android:id="@id/cnlive_ad_overlay"
  48 + android:layout_width="match_parent"
  49 + android:layout_height="match_parent" />
  50 +
  51 + <FrameLayout
  52 + android:id="@id/cnlive_overlay"
  53 + android:layout_width="match_parent"
  54 + android:layout_height="match_parent" />
  55 +
  56 + <View
  57 + android:id="@id/cnlive_controller_placeholder"
  58 + android:layout_width="match_parent"
  59 + android:layout_height="match_parent" />
  60 +
  61 +</merge>
0 62 \ No newline at end of file
... ...
cloud/video/src/main/res/layout/layout_cnlive_control_view.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2016 The Android Open Source Project
  2 +
  3 + Licensed under the Apache License, Version 2.0 (the "License");
  4 + you may not use this file except in compliance with the License.
  5 + You may obtain a copy of the License at
  6 +
  7 + http://www.apache.org/licenses/LICENSE-2.0
  8 +
  9 + Unless required by applicable law or agreed to in writing, software
  10 + distributed under the License is distributed on an "AS IS" BASIS,
  11 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 + See the License for the specific language governing permissions and
  13 + limitations under the License.
  14 +-->
  15 +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  16 + xmlns:tools="http://schemas.android.com/tools"
  17 + android:layout_width="match_parent"
  18 + android:layout_height="match_parent"
  19 + android:orientation="vertical"
  20 + tools:targetApi="28">
  21 +
  22 +
  23 + <!-- 控制视图-->
  24 + <LinearLayout
  25 + android:layout_width="match_parent"
  26 + android:layout_height="wrap_content"
  27 + android:layout_gravity="bottom"
  28 + android:background="#80000000"
  29 + android:orientation="vertical">
  30 + <!-- 进度条-->
  31 + <LinearLayout
  32 + android:layout_width="match_parent"
  33 + android:layout_height="wrap_content"
  34 + android:layout_marginTop="4dp"
  35 + android:gravity="center_vertical"
  36 + android:orientation="horizontal">
  37 +
  38 + <TextView
  39 + android:id="@id/cnlive_position"
  40 + android:layout_width="wrap_content"
  41 + android:layout_height="wrap_content"
  42 + android:includeFontPadding="false"
  43 + android:paddingLeft="4dp"
  44 + android:paddingRight="4dp"
  45 + android:textColor="#FFBEBEBE"
  46 + android:textSize="14sp"
  47 + android:textStyle="bold" />
  48 +
  49 + <View
  50 + android:id="@id/cnlive_progress_placeholder"
  51 + android:layout_width="0dp"
  52 + android:layout_height="26dp"
  53 + android:layout_weight="1" />
  54 +
  55 + <TextView
  56 + android:id="@id/cnlive_duration"
  57 + android:layout_width="wrap_content"
  58 + android:layout_height="wrap_content"
  59 + android:includeFontPadding="false"
  60 + android:paddingLeft="4dp"
  61 + android:paddingRight="4dp"
  62 + android:textColor="#FFBEBEBE"
  63 + android:textSize="14sp"
  64 + android:textStyle="bold" />
  65 +
  66 + </LinearLayout>
  67 + <!---->
  68 + <LinearLayout
  69 + android:layout_width="match_parent"
  70 + android:layout_height="wrap_content"
  71 + android:layout_gravity="bottom"
  72 + android:gravity="center"
  73 + android:layoutDirection="ltr"
  74 + android:orientation="horizontal"
  75 + android:paddingTop="4dp">
  76 +
  77 + <ImageButton
  78 + android:id="@id/cnlive_prev"
  79 + style="@style/ExoMediaButton.Previous" />
  80 +
  81 + <ImageButton
  82 + android:id="@id/cnlive_rew"
  83 + style="@style/ExoMediaButton.Rewind" />
  84 +
  85 + <ImageButton
  86 + android:id="@id/cnlive_shuffle"
  87 + style="@style/ExoMediaButton" />
  88 +
  89 + <ImageButton
  90 + android:id="@id/cnlive_repeat_toggle"
  91 + android:layout_width="30dp"
  92 + android:layout_height="30dp"
  93 + android:src="@drawable/wufalianjie" />
  94 +
  95 + <ImageButton
  96 + android:id="@id/cnlive_play"
  97 + style="@style/ExoMediaButton.Play" />
  98 +
  99 + <ImageButton
  100 + android:id="@id/cnlive_pause"
  101 + style="@style/ExoMediaButton.Pause" />
  102 +
  103 + <ImageButton
  104 + android:id="@id/cnlive_switch_screen"
  105 + style="@style/ExoMediaButton.FastForward" />
  106 +
  107 + <ImageButton
  108 + android:id="@id/cnlive_next"
  109 + style="@style/ExoMediaButton.Next" />
  110 +
  111 + <ImageButton
  112 + android:id="@id/cnlive_vr"
  113 + style="@style/ExoMediaButton.VR" />
  114 +
  115 + </LinearLayout>
  116 +
  117 +
  118 + </LinearLayout>
  119 + <!--顶部视图-->
  120 + <RelativeLayout
  121 + android:layout_width="match_parent"
  122 + android:layout_height="40dp"
  123 + android:background="#80000000">
  124 +
  125 + <TextView
  126 + android:id="@id/cnlive_top_title"
  127 + android:layout_width="wrap_content"
  128 + android:layout_height="wrap_content"
  129 + android:layout_centerVertical="true"
  130 + android:layout_marginLeft="10dp"
  131 + android:textColor="@color/white"
  132 + android:textSize="15sp"
  133 + android:textStyle="bold" />
  134 +
  135 + <ImageButton
  136 + android:id="@id/cnlive_play"
  137 + style="@style/ExoMediaButton.VR"
  138 + android:layout_alignParentRight="true"
  139 + android:layout_centerVertical="true" />
  140 + </RelativeLayout>
  141 +
  142 +</FrameLayout>
... ...
cloud/video/src/main/res/layout/layout_test.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent"
  5 + android:orientation="vertical">
  6 +
  7 + <SurfaceView
  8 + android:id="@+id/sv"
  9 + android:layout_width="match_parent"
  10 + android:layout_height="match_parent"></SurfaceView>
  11 +</LinearLayout>
0 12 \ No newline at end of file
... ...
cloud/video/src/main/res/layout/test_view.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="wrap_content"
  5 + android:layout_gravity="bottom"
  6 + android:background="@color/color_282828"
  7 + android:orientation="vertical">
  8 +
  9 + <Button
  10 + android:id="@+id/btn_start"
  11 + android:layout_width="50dp"
  12 + android:layout_height="50dp" />
  13 +</LinearLayout>
0 14 \ No newline at end of file
... ...
cloud/video/src/main/res/values/values.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <item name="cnlive_ad_overlay" type="id" />
  4 + <item name="cnlive_artwork" type="id" />
  5 + <item name="cnlive_buffering" type="id" />
  6 + <item name="cnlive_content_frame" type="id" />
  7 + <item name="cnlive_controller" type="id" />
  8 + <item name="cnlive_controller_placeholder" type="id" />
  9 + <item name="cnlive_duration" type="id" />
  10 + <item name="cnlive_error_message" type="id" />
  11 + <item name="cnlive_ffwd" type="id" />
  12 + <item name="cnlive_next" type="id" />
  13 + <item name="cnlive_overlay" type="id" />
  14 + <item name="cnlive_pause" type="id" />
  15 + <item name="cnlive_play" type="id" />
  16 + <item name="cnlive_position" type="id" />
  17 + <item name="cnlive_prev" type="id" />
  18 + <item name="cnlive_progress" type="id" />
  19 + <item name="cnlive_progress_placeholder" type="id" />
  20 + <item name="cnlive_repeat_toggle" type="id" />
  21 + <item name="cnlive_rew" type="id" />
  22 + <item name="cnlive_shuffle" type="id" />
  23 + <item name="cnlive_shutter" type="id" />
  24 + <item name="cnlive_subtitles" type="id" />
  25 + <item name="cnlive_vr" type="id" />
  26 + <!-- 横竖屏切换-->
  27 + <item name="cnlive_switch_screen" type="id" />
  28 +
  29 +
  30 + <!--杨帅自定义视图-->
  31 + <!--顶部视图根布局-->
  32 + <item name="cnlive_top_root_view" type="id" />
  33 + <!--顶部视图标题-->
  34 + <item name="cnlive_top_title" type="id" />
  35 +
  36 + <!--右视图-->
  37 +
  38 +</resources>
0 39 \ No newline at end of file
... ...
core/base/src/main/AndroidManifest.xml
... ... @@ -28,6 +28,9 @@
28 28 <!-- android:name="android.support.FILE_PROVIDER_PATHS" -->
29 29 <!-- android:resource="@xml/file_paths" /> -->
30 30 <!-- </provider> -->
31   -
  31 + <activity
  32 + android:name="com.cnlive.core.libs.base.frame.activity.BaseActivity"
  33 + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
  34 + android:screenOrientation="portrait" />
32 35 </application>
33 36 </manifest>
... ...
core/base/src/main/java/com/cnlive/core/libs/base/application/MyAppGlideModule.java
1 1 package com.cnlive.core.libs.base.application;
2 2  
  3 +import android.content.Context;
  4 +
  5 +import androidx.annotation.NonNull;
  6 +
  7 +import com.bumptech.glide.GlideBuilder;
3 8 import com.bumptech.glide.annotation.GlideModule;
4 9 import com.bumptech.glide.module.AppGlideModule;
5 10  
... ... @@ -9,5 +14,8 @@ import com.bumptech.glide.module.AppGlideModule;
9 14  
10 15 @GlideModule
11 16 public final class MyAppGlideModule extends AppGlideModule {
12   -
  17 + @Override
  18 + public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
  19 + super.applyOptions(context, builder);
  20 + }
13 21 }
... ...
core/base/src/main/java/com/cnlive/core/libs/base/arouter/RouterConst.java
1 1 package com.cnlive.core.libs.base.arouter;
2 2  
3 3 public class RouterConst {
  4 + //获取用户信息
4 5 public static final String OPEN_USER_SERVICE_PATH = "/Common/UserInfoService";
5 6 public static final String OPEN_USER_SERVICE_NAME = "UserInfoService";
6 7  
... ...
core/base/src/main/java/com/cnlive/core/libs/base/arouter/user/CNLiveUserService.java
... ... @@ -8,9 +8,6 @@ import com.alibaba.android.arouter.facade.template.IProvider;
8 8 * 给上海电商 用户库
9 9 */
10 10 public interface CNLiveUserService extends IProvider {
11   - public static final String PATH = "/openService/CNLiveUserService";
12   - public static final String NAME = "开放用户信息的服务接口";
13   -
14 11 /**
15 12 * 跳转到登录页
16 13 *
... ...
core/base/src/main/java/com/cnlive/core/libs/base/frame/activity/BaseActivity.java
... ... @@ -85,8 +85,6 @@ public abstract class BaseActivity&lt;V extends BaseView, P extends BasePresenter&lt;V
85 85 private boolean isShowTitle = true;
86 86 //是否显示状态栏
87 87 private boolean isShowStatusBar = true;
88   - //是否允许旋转屏幕
89   - private boolean isAllowScreenRoate = false;
90 88 public Context context;
91 89 /*******************************************/
92 90 private int stateBarColor = -1;//状态栏颜色
... ... @@ -155,10 +153,6 @@ public abstract class BaseActivity&lt;V extends BaseView, P extends BasePresenter&lt;V
155 153 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
156 154 , WindowManager.LayoutParams.FLAG_FULLSCREEN);
157 155 }
158   - //设置屏幕是否可旋转
159   - if (!isAllowScreenRoate) {
160   - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
161   - }
162 156 //设置binding数据
163 157 if (null != baseBinding) {
164 158 initBindingLayoutData(baseBinding);
... ... @@ -320,14 +314,6 @@ public abstract class BaseActivity&lt;V extends BaseView, P extends BasePresenter&lt;V
320 314 isShowStatusBar = showStatusBar;
321 315 }
322 316  
323   - /**
324   - * 是否允许屏幕旋转
325   - *
326   - * @param allowScreenRoate true or false
327   - */
328   - public final void enableAllowScreenRoate(boolean allowScreenRoate) {
329   - isAllowScreenRoate = allowScreenRoate;
330   - }
331 317  
332 318  
333 319 /**
... ...
core/base/src/main/java/com/cnlive/core/libs/base/util/OpenServiceHelpUtil.java 0 → 100644
  1 +package com.cnlive.core.libs.base.util;
  2 +
  3 +import android.content.Context;
  4 +
  5 +import com.alibaba.android.arouter.launcher.ARouter;
  6 +import com.cnlive.core.libs.base.arouter.RouterConst;
  7 +import com.cnlive.core.libs.base.arouter.user.CNLiveUserService;
  8 +
  9 +public class OpenServiceHelpUtil {
  10 + /**
  11 + * 获取用户库
  12 + *
  13 + * @param context
  14 + * @return
  15 + */
  16 + public static CNLiveUserService getUserService(Context context) {
  17 + CNLiveUserService cnLiveUserService = null;
  18 + cnLiveUserService = (CNLiveUserService) ARouter.getInstance().build(RouterConst.OPEN_USER_SERVICE_PATH).navigation(context);
  19 + return cnLiveUserService;
  20 + }
  21 +
  22 +// /**
  23 +// * 获取支付库
  24 +// *
  25 +// * @param context
  26 +// * @return
  27 +// */
  28 +// public static CNLivePayService getPayService(Context context) {
  29 +// CNLivePayService cnLivePayService = null;
  30 +// cnLivePayService = (CNLivePayService) ARouter.getInstance().build(CNLivePayService.PATH).navigation(context);
  31 +// return cnLivePayService;
  32 +// }
  33 +//
  34 +// /**
  35 +// * 获取分享库
  36 +// *
  37 +// * @param context
  38 +// * @return
  39 +// */
  40 +// public static CNLiveShareService getShareService(Context context) {
  41 +// CNLiveShareService cnLiveShareService = null;
  42 +// cnLiveShareService = (CNLiveShareService) ARouter.getInstance().build(CNLiveShareService.PATH).navigation(context);
  43 +// return cnLiveShareService;
  44 +// }
  45 +}
... ...
core/imageload/src/main/java/com/cnlive/core/imageload/util/MultiView.java
1 1 package com.cnlive.core.imageload.util;
2 2  
3   -import android.annotation.SuppressLint;
4 3 import android.content.Context;
5 4 import android.content.res.TypedArray;
6 5 import android.graphics.Bitmap;
... ... @@ -158,7 +157,7 @@ public class MultiView extends ImageView {
158 157 .applyDimension(TypedValue.COMPLEX_UNIT_DIP,
159 158 BODER_RADIUS_DEFAULT, getResources()
160 159 .getDisplayMetrics()));// 默认为10dp
161   - type = typedArray.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);// 默认为Circle
  160 + type = typedArray.getInt(R.styleable.RoundImageView_shapeType, TYPE_CIRCLE);// 默认为Circle
162 161 angleCount = typedArray.getInt(R.styleable.RoundImageView_angleCount, ANGLECOUNT);
163 162 currentAngle = typedArray.getInt(R.styleable.RoundImageView_currentAngle, currentAngle);
164 163  
... ...
core/imageload/src/main/res/values/style.xml
... ... @@ -2,10 +2,10 @@
2 2 <resources>
3 3 <declare-styleable name="RoundImageView">
4 4 <attr name="borderRadius" format="dimension" />
5   - <attr name="type">
  5 + <attr name="shapeType">
6 6 <enum name="circle" value="0" />
7 7 <enum name="round" value="1" />
8   - <enum name="multi" value="3" />
  8 + <enum name="polygon" value="3" />
9 9 </attr>
10 10 <attr name="angleCount" format="integer" />
11 11 <attr name="currentAngle" format="integer" />
... ...
module/shanghaishop/build.gradle
... ... @@ -17,6 +17,14 @@ dependencies {
17 17 testImplementation 'junit:junit:4.12'
18 18 androidTestImplementation 'androidx.test.ext:junit:1.1.1'
19 19 androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
  20 + api('cn.cnlive.b2c:cnliveb2c:1.0.0-20191217.063321-17') {
  21 + exclude group: 'com.github.bumptech.glide'
  22 + }
  23 +// api 'cn.cnlive.b2c:cnliveb2c:1.0.0-20191217.063321-17'
  24 +
  25 + api 'cn.cnlive.b2c:nicevideoplayer:1.0.0-20191206.090854-2'
  26 + api 'cn.cnlive.b2c:udesk-sdk-ui:1.0.0'
  27 +
20 28  
21 29 /** 圆形图片处理 androidx **/
22 30 implementation 'de.hdodenhof:circleimageview:3.0.1'
... ... @@ -31,7 +39,8 @@ dependencies {
31 39 implementation 'com.alibaba:fastjson:1.2.61'
32 40  
33 41 /*轮播图*/
34   - implementation 'com.bigkoo:convenientbanner:2.1.5'
  42 +// implementation 'com.bigkoo:convenientbanner:2.1.5'
  43 + implementation 'com.bigkoo:convenientbanner:2.0.5'
35 44  
36 45 /*滚动播放的公告控件*/
37 46 implementation 'com.github.czy1121:noticeview:1.1.0'
... ... @@ -49,7 +58,7 @@ dependencies {
49 58 implementation 'com.songhang:smart-headerfooter-recyclerview:1.0.1'
50 59  
51 60 /*二维码*/
52   - implementation 'com.google.zxing:core:3.3.0'
  61 +// implementation 'com.google.zxing:core:3.3.0'
53 62  
54 63 // implementation ('cn.yipianfengye.android:zxing-library:2.2'){
55 64 // // 如果已经引入 support:appcompat-v7 可以加上这句话
... ... @@ -69,12 +78,12 @@ dependencies {
69 78 /*一个Android TabLayout库*/
70 79 implementation 'com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar'
71 80  
72   - /*应用卡顿检测*/
73   - debugImplementation 'com.github.markzhai:blockcanary-android:1.5.0'
74   - releaseImplementation 'com.github.markzhai:blockcanary-no-op:1.5.0'
  81 +// /*应用卡顿检测*/
  82 +// debugImplementation 'com.github.markzhai:blockcanary-android:1.5.0'
  83 +// releaseImplementation 'com.github.markzhai:blockcanary-no-op:1.5.0'
75 84  
76   - /*友盟统计*/
77   - implementation 'com.umeng.analytics:analytics:latest.integration'
  85 +// /*友盟统计*/
  86 +// implementation 'com.umeng.analytics:analytics:latest.integration'
78 87  
79 88 /*Float Labeled EditText*/
80 89 implementation 'com.wrapp.floatlabelededittext:library:0.0.6'
... ... @@ -87,30 +96,8 @@ dependencies {
87 96  
88 97 /*自定义各种形状的imageview */
89 98 implementation 'com.github.siyamed:android-shape-imageview:0.9.+@aar'
90   -
91   - // compile 'com.android.support:appcompat-v7:23.4.0'
92   -
93   - // compile 'com.android.support:recyclerview-v7:23.4.0'
94   -
95   - // compile 'com.android.support:design:23.4.0'
96   - implementation files('libs/alipaySingle-20161222.jar')
97   -// implementation files('libs/BaiduLBS_Android.jar')
98   - implementation files('libs/bankabccaller.jar')
99   - implementation files('libs/EhkingSDK-v2.0.jar')
100   - implementation files('libs/jackson-all-1.9.11.jar')
101   - implementation files('libs/MagicWindowSDK.jar')
102   - implementation files('libs/mqqopenpay.jar')
103   - implementation files('libs/SecurityEnvSDK-release-1.1.0.jar')
104   - implementation files('libs/umeng-common-1.5.3.jar')
105   - implementation files('libs/umeng-share-core-6.9.2.jar')
106   -// implementation files('libs/umeng-share-QQ-simplify-6.9.2.jar')
107   -// implementation files('libs/umeng-share-sina-simplify-6.9.2.jar')
108   -// implementation files('libs/umeng-share-wechat-simplify-6.9.2.jar')
109   -// implementation files('libs/umeng-share-sina-simplify-6.9.2.jar')
110   - implementation files('libs/umeng-shareboard-widget-6.9.2.jar')
111   - implementation files('libs/umeng-sharetool-6.9.2.jar')
112   - implementation files('libs/UPPayAssistEx.jar')
113   - implementation files('libs/UPPayPluginExPro.jar')
114   -
  99 + implementation 'com.cnlive.libs:base:1.7.6.18'
  100 + implementation project(path: ':library_baidumap')
  101 + implementation project(path: ':core:base')
115 102  
116 103 }
... ...
module/shanghaishop/libs/EhkingSDK-v2.0.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/MagicWindowSDK.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/SecurityEnvSDK-release-1.1.0.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/UPPayAssistEx.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/UPPayPluginExPro.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/alipaySingle-20161222.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/bankabccaller.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/jackson-all-1.9.11.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/mqqopenpay.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/umeng-common-1.5.3.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/umeng-share-core-6.9.2.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/umeng-shareboard-widget-6.9.2.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/libs/umeng-sharetool-6.9.2.jar deleted 100644 → 0
No preview for this file type
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/DisplayUtil.java deleted 100644 → 0
1   -package com.uuzuche.lib_zxing;
2   -
3   -import android.content.Context;
4   -
5   -/**
6   - * Created by aaron on 16/8/3.
7   - */
8   -public class DisplayUtil
9   -{
10   -
11   - public static int screenWidthPx; //屏幕宽 px
12   - public static int screenhightPx; //屏幕高 px
13   - public static float density;//屏幕密度
14   - public static int densityDPI;//屏幕密度
15   - public static float screenWidthDip;// dp单位
16   - public static float screenHightDip;// dp单位
17   -
18   -
19   -
20   - /**
21   - * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
22   - */
23   - public static int dip2px(Context context, float dpValue) {
24   - final float scale = context.getResources().getDisplayMetrics().density;
25   - return (int) (dpValue * scale + 0.5f);
26   - }
27   -
28   - /**
29   - * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
30   - */
31   - public static int px2dip(Context context, float pxValue) {
32   - final float scale = context.getResources().getDisplayMetrics().density;
33   - return (int) (pxValue / scale + 0.5f);
34   - }
35   -
36   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/ZApplication.java deleted 100644 → 0
1   -package com.uuzuche.lib_zxing;
2   -
3   -import android.app.Application;
4   -import android.util.DisplayMetrics;
5   -
6   -/**
7   - * Created by aaron on 16/8/9.
8   - */
9   -
10   -public class ZApplication extends Application {
11   -
12   - @Override
13   - public void onCreate() {
14   - super.onCreate();
15   - /**
16   - * 初始化尺寸工具类
17   - */
18   - initDisplayOpinion();
19   - }
20   -
21   - private void initDisplayOpinion() {
22   - DisplayMetrics dm = getResources().getDisplayMetrics();
23   - DisplayUtil.density = dm.density;
24   - DisplayUtil.densityDPI = dm.densityDpi;
25   - DisplayUtil.screenWidthPx = dm.widthPixels;
26   - DisplayUtil.screenhightPx = dm.heightPixels;
27   - DisplayUtil.screenWidthDip = DisplayUtil.px2dip(getApplicationContext(), dm.widthPixels);
28   - DisplayUtil.screenHightDip = DisplayUtil.px2dip(getApplicationContext(), dm.heightPixels);
29   - }
30   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/activity/CaptureActivity.java deleted 100644 → 0
1   -package com.uuzuche.lib_zxing.activity;
2   -
3   -import android.content.Intent;
4   -import android.graphics.Bitmap;
5   -import android.os.Bundle;
6   -import android.util.Log;
7   -
8   -import androidx.appcompat.app.AppCompatActivity;
9   -
10   -import com.cnlive.cloud.shanghaishop.R;
11   -
12   -
13   -/**
14   - * Initial the camera
15   - * <p>
16   - * 默认的二维码扫描Activity
17   - */
18   -public class CaptureActivity extends AppCompatActivity {
19   -
20   -
21   - @Override
22   - protected void onCreate(Bundle savedInstanceState) {
23   -
24   - super.onCreate(savedInstanceState);
25   - setContentView(R.layout.camera);
26   - CaptureFragment captureFragment = new CaptureFragment();
27   - captureFragment.setAnalyzeCallback(analyzeCallback);
28   - getSupportFragmentManager().beginTransaction().replace(R.id.fl_zxing_container, captureFragment).commit();
29   - captureFragment.setCameraInitCallBack(new CaptureFragment.CameraInitCallBack() {
30   - @Override
31   - public void callBack(Exception e) {
32   - if (e == null) {
33   -
34   - } else {
35   - Log.e("TAG", "callBack: ", e);
36   - }
37   - }
38   - });
39   -
40   - }
41   -
42   - /**
43   - * 二维码解析回调函数
44   - */
45   - CodeUtils.AnalyzeCallback analyzeCallback = new CodeUtils.AnalyzeCallback() {
46   - @Override
47   - public void onAnalyzeSuccess(Bitmap mBitmap, String result) {
48   - Intent resultIntent = new Intent();
49   - Bundle bundle = new Bundle();
50   - bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_SUCCESS);
51   - bundle.putString(CodeUtils.RESULT_STRING, result);
52   - resultIntent.putExtras(bundle);
53   - CaptureActivity.this.setResult(RESULT_OK, resultIntent);
54   - CaptureActivity.this.finish();
55   - }
56   -
57   - @Override
58   - public void onAnalyzeFailed() {
59   - Intent resultIntent = new Intent();
60   - Bundle bundle = new Bundle();
61   - bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_FAILED);
62   - bundle.putString(CodeUtils.RESULT_STRING, "");
63   - resultIntent.putExtras(bundle);
64   - CaptureActivity.this.setResult(RESULT_OK, resultIntent);
65   - CaptureActivity.this.finish();
66   - }
67   - };
68   -}
69 0 \ No newline at end of file
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/activity/CaptureFragment.java deleted 100644 → 0
1   -package com.uuzuche.lib_zxing.activity;
2   -
3   -import android.content.res.AssetFileDescriptor;
4   -import android.graphics.Bitmap;
5   -import android.hardware.Camera;
6   -import android.media.AudioManager;
7   -import android.media.MediaPlayer;
8   -import android.os.Bundle;
9   -import android.os.Handler;
10   -import android.os.Vibrator;
11   -import android.text.TextUtils;
12   -import android.view.LayoutInflater;
13   -import android.view.SurfaceHolder;
14   -import android.view.SurfaceView;
15   -import android.view.View;
16   -import android.view.ViewGroup;
17   -
18   -import androidx.annotation.Nullable;
19   -import androidx.fragment.app.Fragment;
20   -
21   -import com.cnlive.cloud.shanghaishop.R;
22   -import com.google.zxing.BarcodeFormat;
23   -import com.google.zxing.Result;
24   -import com.uuzuche.lib_zxing.camera.CameraManager;
25   -import com.uuzuche.lib_zxing.decoding.CaptureActivityHandler;
26   -import com.uuzuche.lib_zxing.decoding.InactivityTimer;
27   -import com.uuzuche.lib_zxing.view.ViewfinderView;
28   -
29   -import java.io.IOException;
30   -import java.util.Vector;
31   -
32   -/**
33   - * 自定义实现的扫描Fragment
34   - */
35   -public class CaptureFragment extends Fragment implements SurfaceHolder.Callback {
36   -
37   - private CaptureActivityHandler handler;
38   - private ViewfinderView viewfinderView;
39   - private boolean hasSurface;
40   - private Vector<BarcodeFormat> decodeFormats;
41   - private String characterSet;
42   - private InactivityTimer inactivityTimer;
43   - private MediaPlayer mediaPlayer;
44   - private boolean playBeep;
45   - private static final float BEEP_VOLUME = 0.10f;
46   - private boolean vibrate;
47   - private SurfaceView surfaceView;
48   - private SurfaceHolder surfaceHolder;
49   - private CodeUtils.AnalyzeCallback analyzeCallback;
50   - private Camera camera;
51   -
52   - @Override
53   - public void onCreate(@Nullable Bundle savedInstanceState) {
54   - super.onCreate(savedInstanceState);
55   -
56   -
57   - CameraManager.init(getActivity().getApplication());
58   -
59   - hasSurface = false;
60   - inactivityTimer = new InactivityTimer(this.getActivity());
61   - }
62   -
63   - @Nullable
64   - @Override
65   - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
66   -
67   - Bundle bundle = getArguments();
68   - View view = null;
69   - if (bundle != null) {
70   - int layoutId = bundle.getInt(CodeUtils.LAYOUT_ID);
71   - if (layoutId != -1) {
72   - view = inflater.inflate(layoutId, null);
73   - }
74   - }
75   -
76   - if (view == null) {
77   - view = inflater.inflate(R.layout.fragment_capture, null);
78   - }
79   -
80   - viewfinderView = (ViewfinderView) view.findViewById(R.id.viewfinder_view);
81   - surfaceView = (SurfaceView) view.findViewById(R.id.preview_view);
82   - surfaceHolder = surfaceView.getHolder();
83   -
84   - return view;
85   - }
86   -
87   - @Override
88   - public void onResume() {
89   - super.onResume();
90   - if (hasSurface) {
91   - initCamera(surfaceHolder);
92   - } else {
93   - surfaceHolder.addCallback(this);
94   - surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
95   - }
96   - decodeFormats = null;
97   - characterSet = null;
98   -
99   - playBeep = true;
100   - AudioManager audioService = (AudioManager) getActivity().getSystemService(getActivity().AUDIO_SERVICE);
101   - if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
102   - playBeep = false;
103   - }
104   - initBeepSound();
105   - vibrate = true;
106   - }
107   -
108   - @Override
109   - public void onPause() {
110   - super.onPause();
111   - if (handler != null) {
112   - handler.quitSynchronously();
113   - handler = null;
114   - }
115   - CameraManager.get().closeDriver();
116   - }
117   -
118   - @Override
119   - public void onDestroy() {
120   - super.onDestroy();
121   - inactivityTimer.shutdown();
122   - }
123   -
124   -
125   - /**
126   - * Handler scan result
127   - *
128   - * @param result
129   - * @param barcode
130   - */
131   - public void handleDecode(Result result, Bitmap barcode) {
132   - inactivityTimer.onActivity();
133   - playBeepSoundAndVibrate();
134   -
135   - if (result == null || TextUtils.isEmpty(result.getText())) {
136   - if (analyzeCallback != null) {
137   - analyzeCallback.onAnalyzeFailed();
138   - }
139   - } else {
140   - if (analyzeCallback != null) {
141   - analyzeCallback.onAnalyzeSuccess(barcode, result.getText());
142   - }
143   - }
144   - }
145   -
146   - private void initCamera(SurfaceHolder surfaceHolder) {
147   - try {
148   - CameraManager.get().openDriver(surfaceHolder);
149   - camera = CameraManager.get().getCamera();
150   - } catch (Exception e) {
151   - if (callBack != null) {
152   - callBack.callBack(e);
153   - }
154   - return;
155   - }
156   - if (callBack != null) {
157   - callBack.callBack(null);
158   - }
159   - if (handler == null) {
160   - handler = new CaptureActivityHandler(this, decodeFormats, characterSet, viewfinderView);
161   - }
162   - }
163   -
164   - @Override
165   - public void surfaceChanged(SurfaceHolder holder, int format, int width,
166   - int height) {
167   -
168   - }
169   -
170   - @Override
171   - public void surfaceCreated(SurfaceHolder holder) {
172   - if (!hasSurface) {
173   - hasSurface = true;
174   - initCamera(holder);
175   - }
176   -
177   - }
178   -
179   - @Override
180   - public void surfaceDestroyed(SurfaceHolder holder) {
181   - hasSurface = false;
182   - if (camera != null) {
183   - if (camera != null && CameraManager.get().isPreviewing()) {
184   - if (!CameraManager.get().isUseOneShotPreviewCallback()) {
185   - camera.setPreviewCallback(null);
186   - }
187   - camera.stopPreview();
188   - CameraManager.get().getPreviewCallback().setHandler(null, 0);
189   - CameraManager.get().getAutoFocusCallback().setHandler(null, 0);
190   - CameraManager.get().setPreviewing(false);
191   - }
192   - }
193   - }
194   -
195   - public Handler getHandler() {
196   - return handler;
197   - }
198   -
199   - public void drawViewfinder() {
200   - viewfinderView.drawViewfinder();
201   -
202   - }
203   -
204   - private void initBeepSound() {
205   - if (playBeep && mediaPlayer == null) {
206   - // The volume on STREAM_SYSTEM is not adjustable, and users found it
207   - // too loud,
208   - // so we now play on the music stream.
209   - getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC);
210   - mediaPlayer = new MediaPlayer();
211   - mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
212   - mediaPlayer.setOnCompletionListener(beepListener);
213   -
214   - AssetFileDescriptor file = getResources().openRawResourceFd(
215   - R.raw.beep);
216   - try {
217   - mediaPlayer.setDataSource(file.getFileDescriptor(),
218   - file.getStartOffset(), file.getLength());
219   - file.close();
220   - mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
221   - mediaPlayer.prepare();
222   - } catch (IOException e) {
223   - mediaPlayer = null;
224   - }
225   - }
226   - }
227   -
228   - private static final long VIBRATE_DURATION = 200L;
229   -
230   - private void playBeepSoundAndVibrate() {
231   - if (playBeep && mediaPlayer != null) {
232   - mediaPlayer.start();
233   - }
234   - if (vibrate) {
235   - Vibrator vibrator = (Vibrator) getActivity().getSystemService(getActivity().VIBRATOR_SERVICE);
236   - vibrator.vibrate(VIBRATE_DURATION);
237   - }
238   - }
239   -
240   - /**
241   - * When the beep has finished playing, rewind to queue up another one.
242   - */
243   - private final MediaPlayer.OnCompletionListener beepListener = new MediaPlayer.OnCompletionListener() {
244   - public void onCompletion(MediaPlayer mediaPlayer) {
245   - mediaPlayer.seekTo(0);
246   - }
247   - };
248   -
249   - public CodeUtils.AnalyzeCallback getAnalyzeCallback() {
250   - return analyzeCallback;
251   - }
252   -
253   - public void setAnalyzeCallback(CodeUtils.AnalyzeCallback analyzeCallback) {
254   - this.analyzeCallback = analyzeCallback;
255   - }
256   -
257   - @Nullable
258   - CameraInitCallBack callBack;
259   -
260   - /**
261   - * Set callback for Camera check whether Camera init success or not.
262   - */
263   - public void setCameraInitCallBack(CameraInitCallBack callBack) {
264   - this.callBack = callBack;
265   - }
266   -
267   - interface CameraInitCallBack {
268   - /**
269   - * Callback for Camera init result.
270   - * @param e If is's null,means success.otherwise Camera init failed with the Exception.
271   - */
272   - void callBack(Exception e);
273   - }
274   -
275   -
276   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/activity/CodeUtils.java deleted 100644 → 0
1   -package com.uuzuche.lib_zxing.activity;
2   -
3   -import android.graphics.Bitmap;
4   -import android.graphics.BitmapFactory;
5   -import android.graphics.Matrix;
6   -import android.hardware.Camera;
7   -import android.os.Bundle;
8   -import android.text.TextUtils;
9   -
10   -import com.google.zxing.BarcodeFormat;
11   -import com.google.zxing.BinaryBitmap;
12   -import com.google.zxing.DecodeHintType;
13   -import com.google.zxing.EncodeHintType;
14   -import com.google.zxing.MultiFormatReader;
15   -import com.google.zxing.Result;
16   -import com.google.zxing.WriterException;
17   -import com.google.zxing.common.BitMatrix;
18   -import com.google.zxing.common.HybridBinarizer;
19   -import com.google.zxing.qrcode.QRCodeWriter;
20   -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
21   -import com.uuzuche.lib_zxing.camera.BitmapLuminanceSource;
22   -import com.uuzuche.lib_zxing.camera.CameraManager;
23   -import com.uuzuche.lib_zxing.decoding.DecodeFormatManager;
24   -
25   -import java.util.Hashtable;
26   -import java.util.Vector;
27   -
28   -/**
29   - * Created by aaron on 16/7/27.
30   - * 二维码扫描工具类
31   - */
32   -public class CodeUtils {
33   -
34   - public static final String RESULT_TYPE = "result_type";
35   - public static final String RESULT_STRING = "result_string";
36   - public static final int RESULT_SUCCESS = 1;
37   - public static final int RESULT_FAILED = 2;
38   -
39   - public static final String LAYOUT_ID = "layout_id";
40   -
41   -
42   -
43   - /**
44   - * 解析二维码图片工具类
45   - * @param analyzeCallback
46   - */
47   - public static void analyzeBitmap(String path, AnalyzeCallback analyzeCallback) {
48   -
49   - /**
50   - * 首先判断图片的大小,若图片过大,则执行图片的裁剪操作,防止OOM
51   - */
52   - BitmapFactory.Options options = new BitmapFactory.Options();
53   - options.inJustDecodeBounds = true; // 先获取原大小
54   - Bitmap mBitmap = BitmapFactory.decodeFile(path, options);
55   - options.inJustDecodeBounds = false; // 获取新的大小
56   -
57   - int sampleSize = (int) (options.outHeight / (float) 400);
58   -
59   - if (sampleSize <= 0)
60   - sampleSize = 1;
61   - options.inSampleSize = sampleSize;
62   - mBitmap = BitmapFactory.decodeFile(path, options);
63   -
64   - MultiFormatReader multiFormatReader = new MultiFormatReader();
65   -
66   - // 解码的参数
67   - Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(2);
68   - // 可以解析的编码类型
69   - Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
70   - if (decodeFormats == null || decodeFormats.isEmpty()) {
71   - decodeFormats = new Vector<BarcodeFormat>();
72   -
73   - // 这里设置可扫描的类型,我这里选择了都支持
74   - decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
75   - decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
76   - decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
77   - }
78   - hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
79   - // 设置继续的字符编码格式为UTF8
80   - // hints.put(DecodeHintType.CHARACTER_SET, "UTF8");
81   - // 设置解析配置参数
82   - multiFormatReader.setHints(hints);
83   -
84   - // 开始对图像资源解码
85   - Result rawResult = null;
86   - try {
87   - rawResult = multiFormatReader.decodeWithState(new BinaryBitmap(new HybridBinarizer(new BitmapLuminanceSource(mBitmap))));
88   - } catch (Exception e) {
89   - e.printStackTrace();
90   - }
91   -
92   - if (rawResult != null) {
93   - if (analyzeCallback != null) {
94   - analyzeCallback.onAnalyzeSuccess(mBitmap, rawResult.getText());
95   - }
96   - } else {
97   - if (analyzeCallback != null) {
98   - analyzeCallback.onAnalyzeFailed();
99   - }
100   - }
101   - }
102   -
103   - /**
104   - * 生成二维码图片
105   - * @param text
106   - * @param w
107   - * @param h
108   - * @param logo
109   - * @return
110   - */
111   - public static Bitmap createImage(String text, int w, int h, Bitmap logo) {
112   - if (TextUtils.isEmpty(text)) {
113   - return null;
114   - }
115   - try {
116   - Bitmap scaleLogo = getScaleLogo(logo,w,h);
117   -
118   - int offsetX = w / 2;
119   - int offsetY = h / 2;
120   -
121   - int scaleWidth = 0;
122   - int scaleHeight = 0;
123   - if (scaleLogo != null) {
124   - scaleWidth = scaleLogo.getWidth();
125   - scaleHeight = scaleLogo.getHeight();
126   - offsetX = (w - scaleWidth) / 2;
127   - offsetY = (h - scaleHeight) / 2;
128   - }
129   - Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
130   - hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
131   - //容错级别
132   - hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
133   - //设置空白边距的宽度
134   - hints.put(EncodeHintType.MARGIN, 0);
135   - BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, w, h, hints);
136   - int[] pixels = new int[w * h];
137   - for (int y = 0; y < h; y++) {
138   - for (int x = 0; x < w; x++) {
139   - if(x >= offsetX && x < offsetX + scaleWidth && y>= offsetY && y < offsetY + scaleHeight){
140   - int pixel = scaleLogo.getPixel(x-offsetX,y-offsetY);
141   - if(pixel == 0){
142   - if(bitMatrix.get(x, y)){
143   - pixel = 0xff000000;
144   - }else{
145   - pixel = 0xffffffff;
146   - }
147   - }
148   - pixels[y * w + x] = pixel;
149   - }else{
150   - if (bitMatrix.get(x, y)) {
151   - pixels[y * w + x] = 0xff000000;
152   - } else {
153   - pixels[y * w + x] = 0xffffffff;
154   - }
155   - }
156   - }
157   - }
158   - Bitmap bitmap = Bitmap.createBitmap(w, h,
159   - Bitmap.Config.ARGB_8888);
160   - bitmap.setPixels(pixels, 0, w, 0, 0, w, h);
161   - return bitmap;
162   - } catch (WriterException e) {
163   - e.printStackTrace();
164   - }
165   - return null;
166   - }
167   -
168   - private static Bitmap getScaleLogo(Bitmap logo, int w, int h){
169   - if(logo == null)return null;
170   - Matrix matrix = new Matrix();
171   - float scaleFactor = Math.min(w * 1.0f / 5 / logo.getWidth(), h * 1.0f / 5 /logo.getHeight());
172   - matrix.postScale(scaleFactor,scaleFactor);
173   - Bitmap result = Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
174   - return result;
175   - }
176   -
177   - /**
178   - * 解析二维码结果
179   - */
180   - public interface AnalyzeCallback{
181   -
182   - public void onAnalyzeSuccess(Bitmap mBitmap, String result);
183   -
184   - public void onAnalyzeFailed();
185   - }
186   -
187   -
188   - /**
189   - * 为CaptureFragment设置layout参数
190   - * @param captureFragment
191   - * @param layoutId
192   - */
193   - public static void setFragmentArgs(CaptureFragment captureFragment, int layoutId) {
194   - if (captureFragment == null || layoutId == -1) {
195   - return;
196   - }
197   -
198   - Bundle bundle = new Bundle();
199   - bundle.putInt(LAYOUT_ID, layoutId);
200   - captureFragment.setArguments(bundle);
201   - }
202   -
203   - public static void isLightEnable(boolean isEnable) {
204   - if (isEnable) {
205   - Camera camera = CameraManager.get().getCamera();
206   - if (camera != null) {
207   - Camera.Parameters parameter = camera.getParameters();
208   - parameter.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
209   - camera.setParameters(parameter);
210   - }
211   - } else {
212   - Camera camera = CameraManager.get().getCamera();
213   - if (camera != null) {
214   - Camera.Parameters parameter = camera.getParameters();
215   - parameter.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
216   - camera.setParameters(parameter);
217   - }
218   - }
219   - }
220   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/activity/ZXingLibrary.java deleted 100644 → 0
1   -package com.uuzuche.lib_zxing.activity;
2   -
3   -import android.content.Context;
4   -import android.util.DisplayMetrics;
5   -
6   -import com.uuzuche.lib_zxing.DisplayUtil;
7   -
8   -/**
9   - * Created by aaron on 16/9/7.
10   - */
11   -
12   -public class ZXingLibrary {
13   -
14   - public static void initDisplayOpinion(Context context) {
15   - if (context == null) {
16   - return;
17   - }
18   - DisplayMetrics dm = context.getResources().getDisplayMetrics();
19   - DisplayUtil.density = dm.density;
20   - DisplayUtil.densityDPI = dm.densityDpi;
21   - DisplayUtil.screenWidthPx = dm.widthPixels;
22   - DisplayUtil.screenhightPx = dm.heightPixels;
23   - DisplayUtil.screenWidthDip = DisplayUtil.px2dip(context, dm.widthPixels);
24   - DisplayUtil.screenHightDip = DisplayUtil.px2dip(context, dm.heightPixels);
25   - }
26   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/camera/AutoFocusCallback.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2010 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.camera;
18   -
19   -import android.hardware.Camera;
20   -import android.os.Handler;
21   -import android.os.Message;
22   -import android.util.Log;
23   -
24   -public final class AutoFocusCallback implements Camera.AutoFocusCallback {
25   -
26   - private static final String TAG = AutoFocusCallback.class.getSimpleName();
27   -
28   - private static final long AUTOFOCUS_INTERVAL_MS = 1500L;
29   -
30   - private Handler autoFocusHandler;
31   - private int autoFocusMessage;
32   -
33   - public void setHandler(Handler autoFocusHandler, int autoFocusMessage) {
34   - this.autoFocusHandler = autoFocusHandler;
35   - this.autoFocusMessage = autoFocusMessage;
36   - }
37   -
38   - public void onAutoFocus(boolean success, Camera camera) {
39   - if (autoFocusHandler != null) {
40   - Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
41   - autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
42   - autoFocusHandler = null;
43   - } else {
44   - Log.d(TAG, "Got auto-focus callback, but no handler for it");
45   - }
46   - }
47   -
48   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/camera/BitmapLuminanceSource.java deleted 100644 → 0
1   -package com.uuzuche.lib_zxing.camera;
2   -
3   -import android.graphics.Bitmap;
4   -
5   -import com.google.zxing.LuminanceSource;
6   -
7   -/**
8   - * Created by aaron on 16/7/27.
9   - * 自定义解析Bitmap LuminanceSource
10   - */
11   -public class BitmapLuminanceSource extends LuminanceSource {
12   -
13   - private byte bitmapPixels[];
14   -
15   - public BitmapLuminanceSource(Bitmap bitmap) {
16   - super(bitmap.getWidth(), bitmap.getHeight());
17   -
18   - // 首先,要取得该图片的像素数组内容
19   - int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
20   - this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
21   - bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());
22   -
23   - // 将int数组转换为byte数组,也就是取像素值中蓝色值部分作为辨析内容
24   - for (int i = 0; i < data.length; i++) {
25   - this.bitmapPixels[i] = (byte) data[i];
26   - }
27   - }
28   -
29   - @Override
30   - public byte[] getMatrix() {
31   - // 返回我们生成好的像素数据
32   - return bitmapPixels;
33   - }
34   -
35   - @Override
36   - public byte[] getRow(int y, byte[] row) {
37   - // 这里要得到指定行的像素数据
38   - System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
39   - return row;
40   - }
41   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/camera/CameraConfigurationManager.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2010 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.camera;
18   -
19   -import android.content.Context;
20   -import android.graphics.Point;
21   -import android.hardware.Camera;
22   -import android.os.Build;
23   -import android.util.Log;
24   -import android.view.Display;
25   -import android.view.WindowManager;
26   -
27   -import java.util.regex.Pattern;
28   -
29   -final class CameraConfigurationManager {
30   -
31   - private static final String TAG = CameraConfigurationManager.class.getSimpleName();
32   -
33   - private static final int TEN_DESIRED_ZOOM = 27;
34   - private static final int DESIRED_SHARPNESS = 30;
35   -
36   - private static final Pattern COMMA_PATTERN = Pattern.compile(",");
37   -
38   - private final Context context;
39   - private Point screenResolution;
40   - private Point cameraResolution;
41   - private int previewFormat;
42   - private String previewFormatString;
43   -
44   - CameraConfigurationManager(Context context) {
45   - this.context = context;
46   - }
47   -
48   - /**
49   - * Reads, one time, values from the camera that are needed by the app.
50   - */
51   - void initFromCameraParameters(Camera camera) {
52   - Camera.Parameters parameters = camera.getParameters();
53   - previewFormat = parameters.getPreviewFormat();
54   - previewFormatString = parameters.get("preview-format");
55   - Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
56   - WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
57   - Display display = manager.getDefaultDisplay();
58   - screenResolution = new Point(display.getWidth(), display.getHeight());
59   - Log.d(TAG, "Screen resolution: " + screenResolution);
60   -
61   - Point screenResolutionForCamera = new Point();
62   - screenResolutionForCamera.x = screenResolution.x;
63   - screenResolutionForCamera.y = screenResolution.y;
64   - // preview size is always something like 480*320, other 320*480
65   - if (screenResolution.x < screenResolution.y) {
66   - screenResolutionForCamera.x = screenResolution.y;
67   - screenResolutionForCamera.y = screenResolution.x;
68   - }
69   - Log.i("#########", "screenX:" + screenResolutionForCamera.x + " screenY:" + screenResolutionForCamera.y);
70   - cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);
71   -
72   - // cameraResolution = getCameraResolution(parameters, screenResolution);
73   - Log.d(TAG, "Camera resolution: " + screenResolution);
74   - }
75   -
76   - /**
77   - * Sets the camera up to take preview images which are used for both preview and decoding.
78   - * We detect the preview format here so that buildLuminanceSource() can build an appropriate
79   - * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
80   - * and the planar Y can be used for barcode scanning without a copy in some cases.
81   - */
82   - void setDesiredCameraParameters(Camera camera) {
83   - Camera.Parameters parameters = camera.getParameters();
84   - Log.d(TAG, "Setting preview size: " + cameraResolution);
85   - parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
86   - setFlash(parameters);
87   - setZoom(parameters);
88   - //setSharpness(parameters);
89   - //modify here
90   - camera.setDisplayOrientation(90);
91   - camera.setParameters(parameters);
92   - }
93   -
94   - Point getCameraResolution() {
95   - return cameraResolution;
96   - }
97   -
98   - Point getScreenResolution() {
99   - return screenResolution;
100   - }
101   -
102   - int getPreviewFormat() {
103   - return previewFormat;
104   - }
105   -
106   - String getPreviewFormatString() {
107   - return previewFormatString;
108   - }
109   -
110   - private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) {
111   -
112   - String previewSizeValueString = parameters.get("preview-size-values");
113   - // saw this on Xperia
114   - if (previewSizeValueString == null) {
115   - previewSizeValueString = parameters.get("preview-size-value");
116   - }
117   -
118   - Point cameraResolution = null;
119   -
120   - if (previewSizeValueString != null) {
121   - Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString);
122   - cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
123   - }
124   -
125   - if (cameraResolution == null) {
126   - // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
127   - cameraResolution = new Point(
128   - (screenResolution.x >> 3) << 3,
129   - (screenResolution.y >> 3) << 3);
130   - }
131   -
132   - return cameraResolution;
133   - }
134   -
135   - private static Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) {
136   - int bestX = 0;
137   - int bestY = 0;
138   - int diff = Integer.MAX_VALUE;
139   - for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
140   -
141   - previewSize = previewSize.trim();
142   - int dimPosition = previewSize.indexOf('x');
143   - if (dimPosition < 0) {
144   - Log.w(TAG, "Bad preview-size: " + previewSize);
145   - continue;
146   - }
147   -
148   - int newX;
149   - int newY;
150   - try {
151   - newX = Integer.parseInt(previewSize.substring(0, dimPosition));
152   - newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
153   - } catch (NumberFormatException nfe) {
154   - Log.w(TAG, "Bad preview-size: " + previewSize);
155   - continue;
156   - }
157   -
158   - int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
159   - if (newDiff == 0) {
160   - bestX = newX;
161   - bestY = newY;
162   - break;
163   - } else if (newDiff < diff) {
164   - bestX = newX;
165   - bestY = newY;
166   - diff = newDiff;
167   - }
168   -
169   - }
170   -
171   - if (bestX > 0 && bestY > 0) {
172   - return new Point(bestX, bestY);
173   - }
174   - return null;
175   - }
176   -
177   - private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) {
178   - int tenBestValue = 0;
179   - for (String stringValue : COMMA_PATTERN.split(stringValues)) {
180   - stringValue = stringValue.trim();
181   - double value;
182   - try {
183   - value = Double.parseDouble(stringValue);
184   - } catch (NumberFormatException nfe) {
185   - return tenDesiredZoom;
186   - }
187   - int tenValue = (int) (10.0 * value);
188   - if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
189   - tenBestValue = tenValue;
190   - }
191   - }
192   - return tenBestValue;
193   - }
194   -
195   - private void setFlash(Camera.Parameters parameters) {
196   - // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
197   - // And this is a hack-hack to work around a different value on the Behold II
198   - // Restrict Behold II check to Cupcake, per Samsung's advice
199   - //if (Build.MODEL.contains("Behold II") &&
200   - // CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) {
201   - if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3 = Cupcake
202   - parameters.set("flash-value", 1);
203   - } else {
204   - parameters.set("flash-value", 2);
205   - }
206   - // This is the standard setting to turn the flash off that all devices should honor.
207   - parameters.set("flash-mode", "off");
208   - }
209   -
210   - private void setZoom(Camera.Parameters parameters) {
211   -
212   - String zoomSupportedString = parameters.get("zoom-supported");
213   - if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
214   - return;
215   - }
216   -
217   - int tenDesiredZoom = TEN_DESIRED_ZOOM;
218   -
219   - String maxZoomString = parameters.get("max-zoom");
220   - if (maxZoomString != null) {
221   - try {
222   - int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
223   - if (tenDesiredZoom > tenMaxZoom) {
224   - tenDesiredZoom = tenMaxZoom;
225   - }
226   - } catch (NumberFormatException nfe) {
227   - Log.w(TAG, "Bad max-zoom: " + maxZoomString);
228   - }
229   - }
230   -
231   - String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
232   - if (takingPictureZoomMaxString != null) {
233   - try {
234   - int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
235   - if (tenDesiredZoom > tenMaxZoom) {
236   - tenDesiredZoom = tenMaxZoom;
237   - }
238   - } catch (NumberFormatException nfe) {
239   - Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
240   - }
241   - }
242   -
243   - String motZoomValuesString = parameters.get("mot-zoom-values");
244   - if (motZoomValuesString != null) {
245   - tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
246   - }
247   -
248   - String motZoomStepString = parameters.get("mot-zoom-step");
249   - if (motZoomStepString != null) {
250   - try {
251   - double motZoomStep = Double.parseDouble(motZoomStepString.trim());
252   - int tenZoomStep = (int) (10.0 * motZoomStep);
253   - if (tenZoomStep > 1) {
254   - tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
255   - }
256   - } catch (NumberFormatException nfe) {
257   - // continue
258   - }
259   - }
260   -
261   - // Set zoom. This helps encourage the user to pull back.
262   - // Some devices like the Behold have a zoom parameter
263   - if (maxZoomString != null || motZoomValuesString != null) {
264   - parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
265   - }
266   -
267   - // Most devices, like the Hero, appear to expose this zoom parameter.
268   - // It takes on values like "27" which appears to mean 2.7x zoom
269   - if (takingPictureZoomMaxString != null) {
270   - parameters.set("taking-picture-zoom", tenDesiredZoom);
271   - }
272   - }
273   -
274   - public static int getDesiredSharpness() {
275   - return DESIRED_SHARPNESS;
276   - }
277   -
278   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/camera/CameraManager.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2008 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.camera;
18   -
19   -import android.content.Context;
20   -import android.graphics.PixelFormat;
21   -import android.graphics.Point;
22   -import android.graphics.Rect;
23   -import android.hardware.Camera;
24   -import android.os.Build;
25   -import android.os.Handler;
26   -import android.view.SurfaceHolder;
27   -
28   -import java.io.IOException;
29   -
30   -/**
31   - * This object wraps the Camera service object and expects to be the only one talking to it. The
32   - * implementation encapsulates the steps needed to take preview-sized images, which are used for
33   - * both preview and decoding.
34   - */
35   -public final class CameraManager {
36   -
37   - private static final String TAG = CameraManager.class.getSimpleName();
38   -
39   - public static int FRAME_WIDTH = -1;
40   - public static int FRAME_HEIGHT = -1;
41   - public static int FRAME_MARGINTOP = -1;
42   -
43   - private static CameraManager cameraManager;
44   -
45   - static final int SDK_INT; // Later we can use Build.VERSION.SDK_INT
46   -
47   - static {
48   - int sdkInt;
49   - try {
50   - sdkInt = Integer.parseInt(Build.VERSION.SDK);
51   - } catch (NumberFormatException nfe) {
52   - // Just to be safe
53   - sdkInt = 10000;
54   - }
55   - SDK_INT = sdkInt;
56   - }
57   -
58   - private final Context context;
59   - private final CameraConfigurationManager configManager;
60   - private Camera camera;
61   - private Rect framingRect;
62   - private Rect framingRectInPreview;
63   - private boolean initialized;
64   - private boolean previewing;
65   - private final boolean useOneShotPreviewCallback;
66   - /**
67   - * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
68   - * clear the handler so it will only receive one message.
69   - */
70   - private final PreviewCallback previewCallback;
71   - /**
72   - * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
73   - */
74   - private final AutoFocusCallback autoFocusCallback;
75   -
76   - /**
77   - * Initializes this static object with the Context of the calling Activity.
78   - *
79   - * @param context The Activity which wants to use the camera.
80   - */
81   - public static void init(Context context) {
82   - if (cameraManager == null) {
83   - cameraManager = new CameraManager(context);
84   - }
85   - }
86   -
87   - /**
88   - * Gets the CameraManager singleton instance.
89   - *
90   - * @return A reference to the CameraManager singleton.
91   - */
92   - public static CameraManager get() {
93   - return cameraManager;
94   - }
95   -
96   - private CameraManager(Context context) {
97   -
98   - this.context = context;
99   - this.configManager = new CameraConfigurationManager(context);
100   -
101   - // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
102   - // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
103   - // the more efficient one shot callback, as the older one can swamp the system and cause it
104   - // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
105   - //useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
106   - useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3; // 3 = Cupcake
107   -
108   - previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback);
109   - autoFocusCallback = new AutoFocusCallback();
110   - }
111   -
112   - /**
113   - * Opens the camera driver and initializes the hardware parameters.
114   - *
115   - * @param holder The surface object which the camera will draw preview frames into.
116   - * @throws IOException Indicates the camera driver failed to open.
117   - */
118   - public void openDriver(SurfaceHolder holder) throws IOException {
119   - if (camera == null) {
120   - camera = Camera.open();
121   - if (camera == null) {
122   - throw new IOException();
123   - }
124   - camera.setPreviewDisplay(holder);
125   -
126   - if (!initialized) {
127   - initialized = true;
128   - configManager.initFromCameraParameters(camera);
129   - }
130   - configManager.setDesiredCameraParameters(camera);
131   -
132   - //FIXME
133   - // SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
134   - //�Ƿ�ʹ��ǰ��
135   -// if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
136   -// FlashlightManager.enableFlashlight();
137   -// }
138   - FlashlightManager.enableFlashlight();
139   - }
140   - }
141   -
142   - /**
143   - * Closes the camera driver if still in use.
144   - */
145   - public void closeDriver() {
146   - if (camera != null) {
147   - FlashlightManager.disableFlashlight();
148   - camera.release();
149   - camera = null;
150   - }
151   - }
152   -
153   - /**
154   - * Asks the camera hardware to begin drawing preview frames to the screen.
155   - */
156   - public void startPreview() {
157   - if (camera != null && !previewing) {
158   - camera.startPreview();
159   - previewing = true;
160   - }
161   - }
162   -
163   - /**
164   - * Tells the camera to stop drawing preview frames.
165   - */
166   - public void stopPreview() {
167   - if (camera != null && previewing) {
168   - if (!useOneShotPreviewCallback) {
169   - camera.setPreviewCallback(null);
170   - }
171   - camera.stopPreview();
172   - previewCallback.setHandler(null, 0);
173   - autoFocusCallback.setHandler(null, 0);
174   - previewing = false;
175   - }
176   - }
177   -
178   - /**
179   - * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
180   - * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
181   - * respectively.
182   - *
183   - * @param handler The handler to send the message to.
184   - * @param message The what field of the message to be sent.
185   - */
186   - public void requestPreviewFrame(Handler handler, int message) {
187   - if (camera != null && previewing) {
188   - previewCallback.setHandler(handler, message);
189   - if (useOneShotPreviewCallback) {
190   - camera.setOneShotPreviewCallback(previewCallback);
191   - } else {
192   - camera.setPreviewCallback(previewCallback);
193   - }
194   - }
195   - }
196   -
197   - /**
198   - * Asks the camera hardware to perform an autofocus.
199   - *
200   - * @param handler The Handler to notify when the autofocus completes.
201   - * @param message The message to deliver.
202   - */
203   - public void requestAutoFocus(Handler handler, int message) {
204   - if (camera != null && previewing) {
205   - autoFocusCallback.setHandler(handler, message);
206   - //Log.d(TAG, "Requesting auto-focus callback");
207   - camera.autoFocus(autoFocusCallback);
208   - }
209   - }
210   -
211   - /**
212   - * Calculates the framing rect which the UI should draw to show the user where to place the
213   - * barcode. This target helps with alignment as well as forces the user to hold the device
214   - * far enough away to ensure the image will be in focus.
215   - *
216   - * @return The rectangle to draw on screen in window coordinates.
217   - */
218   - public Rect getFramingRect() {
219   - try {
220   - Point screenResolution = configManager.getScreenResolution();
221   - // if (framingRect == null) {
222   - if (camera == null) {
223   - return null;
224   - }
225   -
226   - int leftOffset = (screenResolution.x - FRAME_WIDTH) / 2;
227   -
228   - int topOffset;
229   - if (FRAME_MARGINTOP != -1) {
230   - topOffset = FRAME_MARGINTOP;
231   - } else {
232   - topOffset = (screenResolution.y - FRAME_HEIGHT) / 2;
233   - }
234   - framingRect = new Rect(leftOffset, topOffset, leftOffset + FRAME_WIDTH, topOffset + FRAME_HEIGHT);
235   - // }
236   - return framingRect;
237   - } catch (Exception e) {
238   - e.printStackTrace();
239   - return null;
240   - }
241   - }
242   -
243   - /**
244   - * Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
245   - * not UI / screen.
246   - */
247   - public Rect getFramingRectInPreview() {
248   - if (framingRectInPreview == null) {
249   - Rect rect = new Rect(getFramingRect());
250   - Point cameraResolution = configManager.getCameraResolution();
251   - Point screenResolution = configManager.getScreenResolution();
252   - //modify here
253   -// rect.left = rect.left * cameraResolution.x / screenResolution.x;
254   -// rect.right = rect.right * cameraResolution.x / screenResolution.x;
255   -// rect.top = rect.top * cameraResolution.y / screenResolution.y;
256   -// rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
257   - rect.left = rect.left * cameraResolution.y / screenResolution.x;
258   - rect.right = rect.right * cameraResolution.y / screenResolution.x;
259   - rect.top = rect.top * cameraResolution.x / screenResolution.y;
260   - rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
261   - framingRectInPreview = rect;
262   - }
263   - return framingRectInPreview;
264   - }
265   -
266   - /**
267   - * Converts the result points from still resolution coordinates to screen coordinates.
268   - *
269   - * @param points The points returned by the Reader subclass through Result.getResultPoints().
270   - * @return An array of Points scaled to the size of the framing rect and offset appropriately
271   - * so they can be drawn in screen coordinates.
272   - */
273   - /*
274   - public Point[] convertResultPoints(ResultPoint[] points) {
275   - Rect frame = getFramingRectInPreview();
276   - int count = points.length;
277   - Point[] output = new Point[count];
278   - for (int x = 0; x < count; x++) {
279   - output[x] = new Point();
280   - output[x].x = frame.left + (int) (points[x].getX() + 0.5f);
281   - output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
282   - }
283   - return output;
284   - }
285   - */
286   -
287   - /**
288   - * A factory method to build the appropriate LuminanceSource object based on the format
289   - * of the preview buffers, as described by Camera.Parameters.
290   - *
291   - * @param data A preview frame.
292   - * @param width The width of the image.
293   - * @param height The height of the image.
294   - * @return A PlanarYUVLuminanceSource instance.
295   - */
296   - public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
297   - Rect rect = getFramingRectInPreview();
298   - int previewFormat = configManager.getPreviewFormat();
299   - String previewFormatString = configManager.getPreviewFormatString();
300   - switch (previewFormat) {
301   - // This is the standard Android format which all devices are REQUIRED to support.
302   - // In theory, it's the only one we should ever care about.
303   - case PixelFormat.YCbCr_420_SP:
304   - // This format has never been seen in the wild, but is compatible as we only care
305   - // about the Y channel, so allow it.
306   - case PixelFormat.YCbCr_422_SP:
307   - return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
308   - rect.width(), rect.height());
309   - default:
310   - // The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
311   - // Fortunately, it too has all the Y data up front, so we can read it.
312   - if ("yuv420p".equals(previewFormatString)) {
313   - return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
314   - rect.width(), rect.height());
315   - }
316   - }
317   - throw new IllegalArgumentException("Unsupported picture format: " +
318   - previewFormat + '/' + previewFormatString);
319   - }
320   -
321   - public Context getContext() {
322   - return context;
323   - }
324   -
325   - public Camera getCamera() {
326   - return camera;
327   - }
328   -
329   - public boolean isPreviewing() {
330   - return previewing;
331   - }
332   -
333   - public boolean isUseOneShotPreviewCallback() {
334   - return useOneShotPreviewCallback;
335   - }
336   -
337   - public PreviewCallback getPreviewCallback() {
338   - return previewCallback;
339   - }
340   -
341   - public AutoFocusCallback getAutoFocusCallback() {
342   - return autoFocusCallback;
343   - }
344   -
345   - public void setPreviewing(boolean previewing) {
346   - this.previewing = previewing;
347   - }
348   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/camera/FlashlightManager.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2010 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.camera;
18   -
19   -import android.os.IBinder;
20   -import android.util.Log;
21   -
22   -import java.lang.reflect.InvocationTargetException;
23   -import java.lang.reflect.Method;
24   -
25   -/**
26   - * This class is used to activate the weak light on some camera phones (not flash)
27   - * in order to illuminate surfaces for scanning. There is no official way to do this,
28   - * but, classes which allow access to this function still exist on some devices.
29   - * This therefore proceeds through a great deal of reflection.
30   - * <p>
31   - * See <a href="http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/">
32   - * http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/</a> and
33   - * <a href="http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java">
34   - * http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java</a>.
35   - * Thanks to Ryan Alford for pointing out the availability of this class.
36   - */
37   -final class FlashlightManager {
38   -
39   - private static final String TAG = FlashlightManager.class.getSimpleName();
40   -
41   - private static final Object iHardwareService;
42   - private static final Method setFlashEnabledMethod;
43   -
44   - static {
45   - iHardwareService = getHardwareService();
46   - setFlashEnabledMethod = getSetFlashEnabledMethod(iHardwareService);
47   - if (iHardwareService == null) {
48   - Log.v(TAG, "This device does supports control of a flashlight");
49   - } else {
50   - Log.v(TAG, "This device does not support control of a flashlight");
51   - }
52   - }
53   -
54   - private FlashlightManager() {
55   - }
56   -
57   - /**
58   - * �����������ƿ���
59   - */
60   - //FIXME
61   - static void enableFlashlight() {
62   - setFlashlight(false);
63   - }
64   -
65   - static void disableFlashlight() {
66   - setFlashlight(false);
67   - }
68   -
69   - private static Object getHardwareService() {
70   - Class<?> serviceManagerClass = maybeForName("android.os.ServiceManager");
71   - if (serviceManagerClass == null) {
72   - return null;
73   - }
74   -
75   - Method getServiceMethod = maybeGetMethod(serviceManagerClass, "getService", String.class);
76   - if (getServiceMethod == null) {
77   - return null;
78   - }
79   -
80   - Object hardwareService = invoke(getServiceMethod, null, "hardware");
81   - if (hardwareService == null) {
82   - return null;
83   - }
84   -
85   - Class<?> iHardwareServiceStubClass = maybeForName("android.os.IHardwareService$Stub");
86   - if (iHardwareServiceStubClass == null) {
87   - return null;
88   - }
89   -
90   - Method asInterfaceMethod = maybeGetMethod(iHardwareServiceStubClass, "asInterface", IBinder.class);
91   - if (asInterfaceMethod == null) {
92   - return null;
93   - }
94   -
95   - return invoke(asInterfaceMethod, null, hardwareService);
96   - }
97   -
98   - private static Method getSetFlashEnabledMethod(Object iHardwareService) {
99   - if (iHardwareService == null) {
100   - return null;
101   - }
102   - Class<?> proxyClass = iHardwareService.getClass();
103   - return maybeGetMethod(proxyClass, "setFlashlightEnabled", boolean.class);
104   - }
105   -
106   - private static Class<?> maybeForName(String name) {
107   - try {
108   - return Class.forName(name);
109   - } catch (ClassNotFoundException cnfe) {
110   - // OK
111   - return null;
112   - } catch (RuntimeException re) {
113   - Log.w(TAG, "Unexpected error while finding class " + name, re);
114   - return null;
115   - }
116   - }
117   -
118   - private static Method maybeGetMethod(Class<?> clazz, String name, Class<?>... argClasses) {
119   - try {
120   - return clazz.getMethod(name, argClasses);
121   - } catch (NoSuchMethodException nsme) {
122   - // OK
123   - return null;
124   - } catch (RuntimeException re) {
125   - Log.w(TAG, "Unexpected error while finding method " + name, re);
126   - return null;
127   - }
128   - }
129   -
130   - private static Object invoke(Method method, Object instance, Object... args) {
131   - try {
132   - return method.invoke(instance, args);
133   - } catch (IllegalAccessException e) {
134   - Log.w(TAG, "Unexpected error while invoking " + method, e);
135   - return null;
136   - } catch (InvocationTargetException e) {
137   - Log.w(TAG, "Unexpected error while invoking " + method, e.getCause());
138   - return null;
139   - } catch (RuntimeException re) {
140   - Log.w(TAG, "Unexpected error while invoking " + method, re);
141   - return null;
142   - }
143   - }
144   -
145   - private static void setFlashlight(boolean active) {
146   - if (iHardwareService != null) {
147   - invoke(setFlashEnabledMethod, iHardwareService, active);
148   - }
149   - }
150   -
151   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/camera/PlanarYUVLuminanceSource.java deleted 100644 → 0
1   -/*
2   - * Copyright 2009 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.camera;
18   -
19   -import android.graphics.Bitmap;
20   -
21   -import com.google.zxing.LuminanceSource;
22   -
23   -/**
24   - * This object extends LuminanceSource around an array of YUV data returned from the camera driver,
25   - * with the option to crop to a rectangle within the full data. This can be used to exclude
26   - * superfluous pixels around the perimeter and speed up decoding.
27   - * <p>
28   - * It works for any pixel format where the Y channel is planar and appears first, including
29   - * YCbCr_420_SP and YCbCr_422_SP.
30   - *
31   - * @author dswitkin@google.com (Daniel Switkin)
32   - */
33   -public final class PlanarYUVLuminanceSource extends LuminanceSource {
34   - private final byte[] yuvData;
35   - private final int dataWidth;
36   - private final int dataHeight;
37   - private final int left;
38   - private final int top;
39   -
40   - public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top,
41   - int width, int height) {
42   - super(width, height);
43   -
44   - if (left + width > dataWidth || top + height > dataHeight) {
45   - throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
46   - }
47   -
48   - this.yuvData = yuvData;
49   - this.dataWidth = dataWidth;
50   - this.dataHeight = dataHeight;
51   - this.left = left;
52   - this.top = top;
53   - }
54   -
55   - @Override
56   - public byte[] getRow(int y, byte[] row) {
57   - if (y < 0 || y >= getHeight()) {
58   - throw new IllegalArgumentException("Requested row is outside the image: " + y);
59   - }
60   - int width = getWidth();
61   - if (row == null || row.length < width) {
62   - row = new byte[width];
63   - }
64   - int offset = (y + top) * dataWidth + left;
65   - System.arraycopy(yuvData, offset, row, 0, width);
66   - return row;
67   - }
68   -
69   - @Override
70   - public byte[] getMatrix() {
71   - int width = getWidth();
72   - int height = getHeight();
73   -
74   - // If the caller asks for the entire underlying image, save the copy and give them the
75   - // original data. The docs specifically warn that result.length must be ignored.
76   - if (width == dataWidth && height == dataHeight) {
77   - return yuvData;
78   - }
79   -
80   - int area = width * height;
81   - byte[] matrix = new byte[area];
82   - int inputOffset = top * dataWidth + left;
83   -
84   - // If the width matches the full width of the underlying data, perform a single copy.
85   - if (width == dataWidth) {
86   - System.arraycopy(yuvData, inputOffset, matrix, 0, area);
87   - return matrix;
88   - }
89   -
90   - // Otherwise copy one cropped row at a time.
91   - byte[] yuv = yuvData;
92   - for (int y = 0; y < height; y++) {
93   - int outputOffset = y * width;
94   - System.arraycopy(yuv, inputOffset, matrix, outputOffset, width);
95   - inputOffset += dataWidth;
96   - }
97   - return matrix;
98   - }
99   -
100   - @Override
101   - public boolean isCropSupported() {
102   - return true;
103   - }
104   -
105   - public int getDataWidth() {
106   - return dataWidth;
107   - }
108   -
109   - public int getDataHeight() {
110   - return dataHeight;
111   - }
112   -
113   - public Bitmap renderCroppedGreyscaleBitmap() {
114   - int width = getWidth();
115   - int height = getHeight();
116   - int[] pixels = new int[width * height];
117   - byte[] yuv = yuvData;
118   - int inputOffset = top * dataWidth + left;
119   -
120   - for (int y = 0; y < height; y++) {
121   - int outputOffset = y * width;
122   - for (int x = 0; x < width; x++) {
123   - int grey = yuv[inputOffset + x] & 0xff;
124   - pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
125   - }
126   - inputOffset += dataWidth;
127   - }
128   -
129   - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
130   - bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
131   - return bitmap;
132   - }
133   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/camera/PreviewCallback.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2010 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.camera;
18   -
19   -import android.graphics.Point;
20   -import android.hardware.Camera;
21   -import android.os.Handler;
22   -import android.os.Message;
23   -import android.util.Log;
24   -
25   -public final class PreviewCallback implements Camera.PreviewCallback {
26   -
27   - private static final String TAG = PreviewCallback.class.getSimpleName();
28   -
29   - private final CameraConfigurationManager configManager;
30   - private final boolean useOneShotPreviewCallback;
31   - private Handler previewHandler;
32   - private int previewMessage;
33   -
34   - PreviewCallback(CameraConfigurationManager configManager, boolean useOneShotPreviewCallback) {
35   - this.configManager = configManager;
36   - this.useOneShotPreviewCallback = useOneShotPreviewCallback;
37   - }
38   -
39   - public void setHandler(Handler previewHandler, int previewMessage) {
40   - this.previewHandler = previewHandler;
41   - this.previewMessage = previewMessage;
42   - }
43   -
44   - public void onPreviewFrame(byte[] data, Camera camera) {
45   - Point cameraResolution = configManager.getCameraResolution();
46   - if (!useOneShotPreviewCallback) {
47   - camera.setPreviewCallback(null);
48   - }
49   - if (previewHandler != null) {
50   - Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
51   - cameraResolution.y, data);
52   - message.sendToTarget();
53   - previewHandler = null;
54   - } else {
55   - Log.d(TAG, "Got preview callback, but no handler for it");
56   - }
57   - }
58   -
59   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/decoding/CaptureActivityHandler.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2008 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.decoding;
18   -
19   -import android.app.Activity;
20   -import android.content.Intent;
21   -import android.graphics.Bitmap;
22   -import android.net.Uri;
23   -import android.os.Bundle;
24   -import android.os.Handler;
25   -import android.os.Message;
26   -import android.util.Log;
27   -
28   -import com.cnlive.cloud.shanghaishop.R;
29   -import com.google.zxing.BarcodeFormat;
30   -import com.google.zxing.Result;
31   -import com.uuzuche.lib_zxing.activity.CaptureFragment;
32   -import com.uuzuche.lib_zxing.camera.CameraManager;
33   -import com.uuzuche.lib_zxing.view.ViewfinderResultPointCallback;
34   -import com.uuzuche.lib_zxing.view.ViewfinderView;
35   -
36   -import java.util.Vector;
37   -
38   -/**
39   - * This class handles all the messaging which comprises the state machine for capture.
40   - */
41   -public final class CaptureActivityHandler extends Handler {
42   -
43   - private static final String TAG = CaptureActivityHandler.class.getSimpleName();
44   -
45   - private final CaptureFragment fragment;
46   - private final DecodeThread decodeThread;
47   - private State state;
48   -
49   - private enum State {
50   - PREVIEW,
51   - SUCCESS,
52   - DONE
53   - }
54   -
55   - public CaptureActivityHandler(CaptureFragment fragment, Vector<BarcodeFormat> decodeFormats,
56   - String characterSet, ViewfinderView viewfinderView) {
57   - this.fragment = fragment;
58   - decodeThread = new DecodeThread(fragment, decodeFormats, characterSet,
59   - new ViewfinderResultPointCallback(viewfinderView));
60   - decodeThread.start();
61   - state = State.SUCCESS;
62   - // Start ourselves capturing previews and decoding.
63   - CameraManager.get().startPreview();
64   - restartPreviewAndDecode();
65   - }
66   -
67   - @Override
68   - public void handleMessage(Message message) {
69   - if (message.what == R.id.auto_focus) {
70   - //Log.d(TAG, "Got auto-focus message");
71   - // When one auto focus pass finishes, start another. This is the closest thing to
72   - // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
73   - if (state == State.PREVIEW) {
74   - CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
75   - }
76   - } else if (message.what == R.id.restart_preview) {
77   - Log.d(TAG, "Got restart preview message");
78   - restartPreviewAndDecode();
79   - } else if (message.what == R.id.decode_succeeded) {
80   - Log.d(TAG, "Got decode succeeded message");
81   - state = State.SUCCESS;
82   - Bundle bundle = message.getData();
83   -
84   - /***********************************************************************/
85   - Bitmap barcode = bundle == null ? null :
86   - (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);//���ñ����߳�
87   -
88   - fragment.handleDecode((Result) message.obj, barcode);//���ؽ��
89   - /***********************************************************************/
90   - } else if (message.what == R.id.decode_failed) {
91   - // We're decoding as fast as possible, so when one decode fails, start another.
92   - state = State.PREVIEW;
93   - CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
94   - } else if (message.what == R.id.return_scan_result) {
95   - Log.d(TAG, "Got return scan result message");
96   - fragment.getActivity().setResult(Activity.RESULT_OK, (Intent) message.obj);
97   - fragment.getActivity().finish();
98   - } else if (message.what == R.id.launch_product_query) {
99   - Log.d(TAG, "Got product query message");
100   - String url = (String) message.obj;
101   - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
102   - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
103   - fragment.getActivity().startActivity(intent);
104   - }
105   - }
106   -
107   - public void quitSynchronously() {
108   - state = State.DONE;
109   - CameraManager.get().stopPreview();
110   - Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
111   - quit.sendToTarget();
112   - try {
113   - decodeThread.join();
114   - } catch (InterruptedException e) {
115   - // continue
116   - }
117   -
118   - // Be absolutely sure we don't send any queued up messages
119   - removeMessages(R.id.decode_succeeded);
120   - removeMessages(R.id.decode_failed);
121   - }
122   -
123   - private void restartPreviewAndDecode() {
124   - if (state == State.SUCCESS) {
125   - state = State.PREVIEW;
126   - CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
127   - CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
128   - fragment.drawViewfinder();
129   - }
130   - }
131   -
132   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/decoding/DecodeFormatManager.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2010 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.decoding;
18   -
19   -import android.content.Intent;
20   -import android.net.Uri;
21   -
22   -import com.google.zxing.BarcodeFormat;
23   -
24   -import java.util.Arrays;
25   -import java.util.List;
26   -import java.util.Vector;
27   -import java.util.regex.Pattern;
28   -
29   -public class DecodeFormatManager {
30   -
31   - private static final Pattern COMMA_PATTERN = Pattern.compile(",");
32   -
33   - public static final Vector<BarcodeFormat> PRODUCT_FORMATS;
34   - public static final Vector<BarcodeFormat> ONE_D_FORMATS;
35   - public static final Vector<BarcodeFormat> QR_CODE_FORMATS;
36   - public static final Vector<BarcodeFormat> DATA_MATRIX_FORMATS;
37   -
38   - static {
39   - PRODUCT_FORMATS = new Vector<BarcodeFormat>(5);
40   - PRODUCT_FORMATS.add(BarcodeFormat.UPC_A);
41   - PRODUCT_FORMATS.add(BarcodeFormat.UPC_E);
42   - PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);
43   - PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);
44   - // PRODUCT_FORMATS.add(BarcodeFormat.RSS14);
45   - ONE_D_FORMATS = new Vector<BarcodeFormat>(PRODUCT_FORMATS.size() + 4);
46   - ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
47   - ONE_D_FORMATS.add(BarcodeFormat.CODE_39);
48   - ONE_D_FORMATS.add(BarcodeFormat.CODE_93);
49   - ONE_D_FORMATS.add(BarcodeFormat.CODE_128);
50   - ONE_D_FORMATS.add(BarcodeFormat.ITF);
51   - QR_CODE_FORMATS = new Vector<BarcodeFormat>(1);
52   - QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE);
53   - DATA_MATRIX_FORMATS = new Vector<BarcodeFormat>(1);
54   - DATA_MATRIX_FORMATS.add(BarcodeFormat.DATA_MATRIX);
55   - }
56   -
57   - private DecodeFormatManager() {
58   - }
59   -
60   - static Vector<BarcodeFormat> parseDecodeFormats(Intent intent) {
61   - List<String> scanFormats = null;
62   - String scanFormatsString = intent.getStringExtra(Intents.Scan.SCAN_FORMATS);
63   - if (scanFormatsString != null) {
64   - scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
65   - }
66   - return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
67   - }
68   -
69   - static Vector<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
70   - List<String> formats = inputUri.getQueryParameters(Intents.Scan.SCAN_FORMATS);
71   - if (formats != null && formats.size() == 1 && formats.get(0) != null) {
72   - formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
73   - }
74   - return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
75   - }
76   -
77   - private static Vector<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats,
78   - String decodeMode) {
79   - if (scanFormats != null) {
80   - Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>();
81   - try {
82   - for (String format : scanFormats) {
83   - formats.add(BarcodeFormat.valueOf(format));
84   - }
85   - return formats;
86   - } catch (IllegalArgumentException iae) {
87   - // ignore it then
88   - }
89   - }
90   - if (decodeMode != null) {
91   - if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) {
92   - return PRODUCT_FORMATS;
93   - }
94   - if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) {
95   - return QR_CODE_FORMATS;
96   - }
97   - if (Intents.Scan.DATA_MATRIX_MODE.equals(decodeMode)) {
98   - return DATA_MATRIX_FORMATS;
99   - }
100   - if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) {
101   - return ONE_D_FORMATS;
102   - }
103   - }
104   - return null;
105   - }
106   -
107   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/decoding/DecodeHandler.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2010 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.decoding;
18   -
19   -import android.os.Bundle;
20   -import android.os.Handler;
21   -import android.os.Looper;
22   -import android.os.Message;
23   -import android.util.Log;
24   -
25   -import com.cnlive.cloud.shanghaishop.R;
26   -import com.google.zxing.BinaryBitmap;
27   -import com.google.zxing.DecodeHintType;
28   -import com.google.zxing.MultiFormatReader;
29   -import com.google.zxing.ReaderException;
30   -import com.google.zxing.Result;
31   -import com.google.zxing.common.HybridBinarizer;
32   -import com.uuzuche.lib_zxing.activity.CaptureFragment;
33   -import com.uuzuche.lib_zxing.camera.CameraManager;
34   -import com.uuzuche.lib_zxing.camera.PlanarYUVLuminanceSource;
35   -
36   -import java.util.Hashtable;
37   -
38   -final class DecodeHandler extends Handler {
39   -
40   - private static final String TAG = DecodeHandler.class.getSimpleName();
41   -
42   - private final CaptureFragment fragment;
43   - private final MultiFormatReader multiFormatReader;
44   -
45   - DecodeHandler(CaptureFragment fragment, Hashtable<DecodeHintType, Object> hints) {
46   - multiFormatReader = new MultiFormatReader();
47   - multiFormatReader.setHints(hints);
48   - this.fragment = fragment;
49   - }
50   -
51   - @Override
52   - public void handleMessage(Message message) {
53   - if (message.what == R.id.decode) {
54   - decode((byte[]) message.obj, message.arg1, message.arg2);
55   - } else if (message.what == R.id.quit) {
56   - Looper.myLooper().quit();
57   - }
58   - }
59   -
60   - /**
61   - * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
62   - * reuse the same reader objects from one decode to the next.
63   - *
64   - * @param data The YUV preview frame.
65   - * @param width The width of the preview frame.
66   - * @param height The height of the preview frame.
67   - */
68   - private void decode(byte[] data, int width, int height) {
69   - long start = System.currentTimeMillis();
70   - Result rawResult = null;
71   -
72   - //modify here
73   - byte[] rotatedData = new byte[data.length];
74   - for (int y = 0; y < height; y++) {
75   - for (int x = 0; x < width; x++)
76   - rotatedData[x * height + height - y - 1] = data[x + y * width];
77   - }
78   - int tmp = width; // Here we are swapping, that's the difference to #11
79   - width = height;
80   - height = tmp;
81   -
82   - PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
83   - BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
84   - try {
85   - rawResult = multiFormatReader.decodeWithState(bitmap);
86   - } catch (ReaderException re) {
87   - // continue
88   - } finally {
89   - multiFormatReader.reset();
90   - }
91   -
92   - if (rawResult != null) {
93   - long end = System.currentTimeMillis();
94   - Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
95   - Message message = Message.obtain(fragment.getHandler(), R.id.decode_succeeded, rawResult);
96   - Bundle bundle = new Bundle();
97   - bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
98   - message.setData(bundle);
99   - //Log.d(TAG, "Sending decode succeeded message...");
100   - message.sendToTarget();
101   - } else {
102   - Message message = Message.obtain(fragment.getHandler(), R.id.decode_failed);
103   - message.sendToTarget();
104   - }
105   - }
106   -
107   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/decoding/DecodeThread.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2008 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.decoding;
18   -
19   -import android.os.Handler;
20   -import android.os.Looper;
21   -
22   -import com.google.zxing.BarcodeFormat;
23   -import com.google.zxing.DecodeHintType;
24   -import com.google.zxing.ResultPointCallback;
25   -import com.uuzuche.lib_zxing.activity.CaptureFragment;
26   -
27   -import java.util.Hashtable;
28   -import java.util.Vector;
29   -import java.util.concurrent.CountDownLatch;
30   -
31   -/**
32   - * This thread does all the heavy lifting of decoding the images.
33   - * �����߳�
34   - */
35   -final class DecodeThread extends Thread {
36   -
37   - public static final String BARCODE_BITMAP = "barcode_bitmap";
38   - private final CaptureFragment fragment;
39   - private final Hashtable<DecodeHintType, Object> hints;
40   - private Handler handler;
41   - private final CountDownLatch handlerInitLatch;
42   -
43   - DecodeThread(CaptureFragment fragment,
44   - Vector<BarcodeFormat> decodeFormats,
45   - String characterSet,
46   - ResultPointCallback resultPointCallback) {
47   -
48   - this.fragment = fragment;
49   - handlerInitLatch = new CountDownLatch(1);
50   -
51   - hints = new Hashtable<DecodeHintType, Object>(3);
52   -
53   - if (decodeFormats == null || decodeFormats.isEmpty()) {
54   - decodeFormats = new Vector<BarcodeFormat>();
55   - decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
56   - decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
57   - decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
58   - }
59   -
60   - hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
61   -
62   - if (characterSet != null) {
63   - hints.put(DecodeHintType.CHARACTER_SET, characterSet);
64   - }
65   -
66   - hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
67   - }
68   -
69   - Handler getHandler() {
70   - try {
71   - handlerInitLatch.await();
72   - } catch (InterruptedException ie) {
73   - // continue?
74   - }
75   - return handler;
76   - }
77   -
78   - @Override
79   - public void run() {
80   - Looper.prepare();
81   - handler = new DecodeHandler(fragment, hints);
82   - handlerInitLatch.countDown();
83   - Looper.loop();
84   - }
85   -
86   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/decoding/FinishListener.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2010 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.decoding;
18   -
19   -import android.app.Activity;
20   -import android.content.DialogInterface;
21   -
22   -/**
23   - * Simple listener used to exit the app in a few cases.
24   - */
25   -public final class FinishListener
26   - implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, Runnable {
27   -
28   - private final Activity activityToFinish;
29   -
30   - public FinishListener(Activity activityToFinish) {
31   - this.activityToFinish = activityToFinish;
32   - }
33   -
34   - public void onCancel(DialogInterface dialogInterface) {
35   - run();
36   - }
37   -
38   - public void onClick(DialogInterface dialogInterface, int i) {
39   - run();
40   - }
41   -
42   - public void run() {
43   - activityToFinish.finish();
44   - }
45   -
46   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/decoding/InactivityTimer.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2010 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.decoding;
18   -
19   -import android.app.Activity;
20   -
21   -import java.util.concurrent.Executors;
22   -import java.util.concurrent.ScheduledExecutorService;
23   -import java.util.concurrent.ScheduledFuture;
24   -import java.util.concurrent.ThreadFactory;
25   -import java.util.concurrent.TimeUnit;
26   -
27   -/**
28   - * Finishes an activity after a period of inactivity.
29   - */
30   -public final class InactivityTimer {
31   -
32   - private static final int INACTIVITY_DELAY_SECONDS = 5 * 60;
33   -
34   - private final ScheduledExecutorService inactivityTimer =
35   - Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory());
36   - private final Activity activity;
37   - private ScheduledFuture<?> inactivityFuture = null;
38   -
39   - public InactivityTimer(Activity activity) {
40   - this.activity = activity;
41   - onActivity();
42   - }
43   -
44   - public void onActivity() {
45   - cancel();
46   - inactivityFuture = inactivityTimer.schedule(new FinishListener(activity),
47   - INACTIVITY_DELAY_SECONDS,
48   - TimeUnit.SECONDS);
49   - }
50   -
51   - private void cancel() {
52   - if (inactivityFuture != null) {
53   - inactivityFuture.cancel(true);
54   - inactivityFuture = null;
55   - }
56   - }
57   -
58   - public void shutdown() {
59   - cancel();
60   - inactivityTimer.shutdown();
61   - }
62   -
63   - private static final class DaemonThreadFactory implements ThreadFactory {
64   - public Thread newThread(Runnable runnable) {
65   - Thread thread = new Thread(runnable);
66   - thread.setDaemon(true);
67   - return thread;
68   - }
69   - }
70   -
71   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/decoding/Intents.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2008 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.decoding;
18   -
19   -/**
20   - * This class provides the constants to use when sending an Intent to Barcode Scanner.
21   - * These strings are effectively API and cannot be changed.
22   - */
23   -public final class Intents {
24   - private Intents() {
25   - }
26   -
27   - public static final class Scan {
28   - /**
29   - * Send this intent to open the Barcodes app in scanning mode, find a barcode, and return
30   - * the results.
31   - */
32   - public static final String ACTION = "com.google.zxing.client.android.SCAN";
33   -
34   - /**
35   - * By default, sending Scan.ACTION will decode all barcodes that we understand. However it
36   - * may be useful to limit scanning to certain formats. Use Intent.putExtra(MODE, value) with
37   - * one of the values below ({@link #PRODUCT_MODE}, {@link #ONE_D_MODE}, {@link #QR_CODE_MODE}).
38   - * Optional.
39   - * <p>
40   - * Setting this is effectively shorthnad for setting explicit formats with {@link #SCAN_FORMATS}.
41   - * It is overridden by that setting.
42   - */
43   - public static final String MODE = "SCAN_MODE";
44   -
45   - /**
46   - * Comma-separated list of formats to scan for. The values must match the names of
47   - * {@link com.google.zxing.BarcodeFormat}s, such as {@link com.google.zxing.BarcodeFormat#EAN_13}.
48   - * Example: "EAN_13,EAN_8,QR_CODE"
49   - * <p>
50   - * This overrides {@link #MODE}.
51   - */
52   - public static final String SCAN_FORMATS = "SCAN_FORMATS";
53   -
54   - /**
55   - * @see com.google.zxing.DecodeHintType#CHARACTER_SET
56   - */
57   - public static final String CHARACTER_SET = "CHARACTER_SET";
58   -
59   - /**
60   - * Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get
61   - * prices, reviews, etc. for products.
62   - */
63   - public static final String PRODUCT_MODE = "PRODUCT_MODE";
64   -
65   - /**
66   - * Decode only 1D barcodes (currently UPC, EAN, Code 39, and Code 128).
67   - */
68   - public static final String ONE_D_MODE = "ONE_D_MODE";
69   -
70   - /**
71   - * Decode only QR codes.
72   - */
73   - public static final String QR_CODE_MODE = "QR_CODE_MODE";
74   -
75   - /**
76   - * Decode only Data Matrix codes.
77   - */
78   - public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE";
79   -
80   - /**
81   - * If a barcode is found, Barcodes returns RESULT_OK to onActivityResult() of the app which
82   - * requested the scan via startSubActivity(). The barcodes contents can be retrieved with
83   - * intent.getStringExtra(RESULT). If the user presses Back, the result code will be
84   - * RESULT_CANCELED.
85   - */
86   - public static final String RESULT = "SCAN_RESULT";
87   -
88   - /**
89   - * Call intent.getStringExtra(RESULT_FORMAT) to determine which barcode format was found.
90   - * See Contents.Format for possible values.
91   - */
92   - public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
93   -
94   - /**
95   - * Setting this to false will not save scanned codes in the history.
96   - */
97   - public static final String SAVE_HISTORY = "SAVE_HISTORY";
98   -
99   - private Scan() {
100   - }
101   - }
102   -
103   - public static final class Encode {
104   - /**
105   - * Send this intent to encode a piece of data as a QR code and display it full screen, so
106   - * that another person can scan the barcode from your screen.
107   - */
108   - public static final String ACTION = "com.google.zxing.client.android.ENCODE";
109   -
110   - /**
111   - * The data to encode. Use Intent.putExtra(DATA, data) where data is either a String or a
112   - * Bundle, depending on the type and format specified. Non-QR Code formats should
113   - * just use a String here. For QR Code, see Contents for details.
114   - */
115   - public static final String DATA = "ENCODE_DATA";
116   -
117   - /**
118   - * The type of data being supplied if the format is QR Code. Use
119   - * Intent.putExtra(TYPE, type) with one of Contents.Type.
120   - */
121   - public static final String TYPE = "ENCODE_TYPE";
122   -
123   - /**
124   - * The barcode format to be displayed. If this isn't specified or is blank,
125   - * it defaults to QR Code. Use Intent.putExtra(FORMAT, format), where
126   - * format is one of Contents.Format.
127   - */
128   - public static final String FORMAT = "ENCODE_FORMAT";
129   -
130   - private Encode() {
131   - }
132   - }
133   -
134   - public static final class SearchBookContents {
135   - /**
136   - * Use Google Book Search to search the contents of the book provided.
137   - */
138   - public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS";
139   -
140   - /**
141   - * The book to search, identified by ISBN number.
142   - */
143   - public static final String ISBN = "ISBN";
144   -
145   - /**
146   - * An optional field which is the text to search for.
147   - */
148   - public static final String QUERY = "QUERY";
149   -
150   - private SearchBookContents() {
151   - }
152   - }
153   -
154   - public static final class WifiConnect {
155   - /**
156   - * Internal intent used to trigger connection to a wi-fi network.
157   - */
158   - public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT";
159   -
160   - /**
161   - * The network to connect to, all the configuration provided here.
162   - */
163   - public static final String SSID = "SSID";
164   -
165   - /**
166   - * The network to connect to, all the configuration provided here.
167   - */
168   - public static final String TYPE = "TYPE";
169   -
170   - /**
171   - * The network to connect to, all the configuration provided here.
172   - */
173   - public static final String PASSWORD = "PASSWORD";
174   -
175   - private WifiConnect() {
176   - }
177   - }
178   -
179   -
180   - public static final class Share {
181   - /**
182   - * Give the user a choice of items to encode as a barcode, then render it as a QR Code and
183   - * display onscreen for a friend to scan with their phone.
184   - */
185   - public static final String ACTION = "com.google.zxing.client.android.SHARE";
186   -
187   - private Share() {
188   - }
189   - }
190   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/encoding/EncodingHandler.java deleted 100644 → 0
1   -package com.uuzuche.lib_zxing.encoding;
2   -
3   -import android.graphics.Bitmap;
4   -
5   -import com.google.zxing.BarcodeFormat;
6   -import com.google.zxing.EncodeHintType;
7   -import com.google.zxing.MultiFormatWriter;
8   -import com.google.zxing.WriterException;
9   -import com.google.zxing.common.BitMatrix;
10   -
11   -import java.util.Hashtable;
12   -
13   -/**
14   - * @author Ryan Tang
15   - */
16   -public final class EncodingHandler {
17   - private static final int BLACK = 0xff000000;
18   -
19   - public static Bitmap createQRCode(String str, int widthAndHeight) throws WriterException {
20   - Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
21   - hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
22   - BitMatrix matrix = new MultiFormatWriter().encode(str,
23   - BarcodeFormat.QR_CODE, widthAndHeight, widthAndHeight);
24   - int width = matrix.getWidth();
25   - int height = matrix.getHeight();
26   - int[] pixels = new int[width * height];
27   -
28   - for (int y = 0; y < height; y++) {
29   - for (int x = 0; x < width; x++) {
30   - if (matrix.get(x, y)) {
31   - pixels[y * width + x] = BLACK;
32   - }
33   - }
34   - }
35   - Bitmap bitmap = Bitmap.createBitmap(width, height,
36   - Bitmap.Config.ARGB_8888);
37   - bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
38   - return bitmap;
39   - }
40   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/view/ViewfinderResultPointCallback.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2009 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.view;
18   -
19   -import com.google.zxing.ResultPoint;
20   -import com.google.zxing.ResultPointCallback;
21   -
22   -public final class ViewfinderResultPointCallback implements ResultPointCallback {
23   -
24   - private final ViewfinderView viewfinderView;
25   -
26   - public ViewfinderResultPointCallback(ViewfinderView viewfinderView) {
27   - this.viewfinderView = viewfinderView;
28   - }
29   -
30   - public void foundPossibleResultPoint(ResultPoint point) {
31   - viewfinderView.addPossibleResultPoint(point);
32   - }
33   -
34   -}
module/shanghaishop/src/main/java/com/uuzuche/lib_zxing/view/ViewfinderView.java deleted 100644 → 0
1   -/*
2   - * Copyright (C) 2008 ZXing authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -package com.uuzuche.lib_zxing.view;
18   -
19   -import android.content.Context;
20   -import android.content.res.Resources;
21   -import android.content.res.TypedArray;
22   -import android.graphics.Bitmap;
23   -import android.graphics.BitmapFactory;
24   -import android.graphics.Canvas;
25   -import android.graphics.Color;
26   -import android.graphics.Paint;
27   -import android.graphics.Rect;
28   -import android.graphics.drawable.Drawable;
29   -import android.util.AttributeSet;
30   -import android.view.View;
31   -
32   -import com.cnlive.cloud.shanghaishop.R;
33   -import com.google.zxing.ResultPoint;
34   -import com.uuzuche.lib_zxing.DisplayUtil;
35   -import com.uuzuche.lib_zxing.camera.CameraManager;
36   -
37   -import java.util.Collection;
38   -import java.util.HashSet;
39   -
40   -/**
41   - * 自定义组件实现,扫描功能
42   - */
43   -public final class ViewfinderView extends View {
44   -
45   - private static final long ANIMATION_DELAY = 100L;
46   - private static final int OPAQUE = 0xFF;
47   -
48   - private final Paint paint;
49   - private Bitmap resultBitmap;
50   - private final int maskColor;
51   - private final int resultColor;
52   - private final int resultPointColor;
53   - private Collection<ResultPoint> possibleResultPoints;
54   - private Collection<ResultPoint> lastPossibleResultPoints;
55   -
56   - public ViewfinderView(Context context) {
57   - this(context, null);
58   - }
59   -
60   - public ViewfinderView(Context context, AttributeSet attrs) {
61   - this(context, attrs, -1);
62   -
63   - }
64   -
65   - public ViewfinderView(Context context, AttributeSet attrs, int defStyleAttr) {
66   - super(context, attrs, defStyleAttr);
67   - paint = new Paint();
68   - Resources resources = getResources();
69   - maskColor = resources.getColor(R.color.viewfinder_mask);
70   - resultColor = resources.getColor(R.color.result_view);
71   - resultPointColor = resources.getColor(R.color.possible_result_points);
72   - possibleResultPoints = new HashSet<>(5);
73   -
74   - scanLight = BitmapFactory.decodeResource(resources,
75   - R.drawable.scan_light);
76   -
77   - initInnerRect(context, attrs);
78   - }
79   -
80   - /**
81   - * 初始化内部框的大小
82   - *
83   - * @param context
84   - * @param attrs
85   - */
86   - private void initInnerRect(Context context, AttributeSet attrs) {
87   - TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
88   -
89   - // 扫描框距离顶部
90   - float innerMarginTop = ta.getDimension(R.styleable.ViewfinderView_inner_margintop, -1);
91   - if (innerMarginTop != -1) {
92   - CameraManager.FRAME_MARGINTOP = (int) innerMarginTop;
93   - }
94   -
95   - // 扫描框的宽度
96   - CameraManager.FRAME_WIDTH = (int) ta.getDimension(R.styleable.ViewfinderView_inner_width, DisplayUtil.screenWidthPx / 2);
97   -
98   - // 扫描框的高度
99   - CameraManager.FRAME_HEIGHT = (int) ta.getDimension(R.styleable.ViewfinderView_inner_height, DisplayUtil.screenWidthPx / 2);
100   -
101   - // 扫描框边角颜色
102   - innercornercolor = ta.getColor(R.styleable.ViewfinderView_inner_corner_color, Color.parseColor("#45DDDD"));
103   - // 扫描框边角长度
104   - innercornerlength = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_length, 65);
105   - // 扫描框边角宽度
106   - innercornerwidth = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_width, 15);
107   -
108   - // 扫描bitmap
109   - Drawable drawable = ta.getDrawable(R.styleable.ViewfinderView_inner_scan_bitmap);
110   - if (drawable != null) {
111   - }
112   -
113   - // 扫描控件
114   - scanLight = BitmapFactory.decodeResource(getResources(), ta.getResourceId(R.styleable.ViewfinderView_inner_scan_bitmap, R.drawable.scan_light));
115   - // 扫描速度
116   - SCAN_VELOCITY = ta.getInt(R.styleable.ViewfinderView_inner_scan_speed, 5);
117   -
118   - isCircle = ta.getBoolean(R.styleable.ViewfinderView_inner_scan_iscircle, true);
119   -
120   - ta.recycle();
121   - }
122   -
123   - @Override
124   - public void onDraw(Canvas canvas) {
125   - Rect frame = CameraManager.get().getFramingRect();
126   - if (frame == null) {
127   - return;
128   - }
129   - int width = canvas.getWidth();
130   - int height = canvas.getHeight();
131   -
132   - // Draw the exterior (i.e. outside the framing rect) darkened
133   - paint.setColor(resultBitmap != null ? resultColor : maskColor);
134   - canvas.drawRect(0, 0, width, frame.top, paint);
135   - canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
136   - canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
137   - canvas.drawRect(0, frame.bottom + 1, width, height, paint);
138   -
139   - if (resultBitmap != null) {
140   - // Draw the opaque result bitmap over the scanning rectangle
141   - paint.setAlpha(OPAQUE);
142   - canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
143   - } else {
144   -
145   - drawFrameBounds(canvas, frame);
146   -
147   - drawScanLight(canvas, frame);
148   -
149   - Collection<ResultPoint> currentPossible = possibleResultPoints;
150   - Collection<ResultPoint> currentLast = lastPossibleResultPoints;
151   - if (currentPossible.isEmpty()) {
152   - lastPossibleResultPoints = null;
153   - } else {
154   - possibleResultPoints = new HashSet<ResultPoint>(5);
155   - lastPossibleResultPoints = currentPossible;
156   - paint.setAlpha(OPAQUE);
157   - paint.setColor(resultPointColor);
158   -
159   - if (isCircle) {
160   - for (ResultPoint point : currentPossible) {
161   - canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint);
162   - }
163   - }
164   - }
165   - if (currentLast != null) {
166   - paint.setAlpha(OPAQUE / 2);
167   - paint.setColor(resultPointColor);
168   -
169   - if (isCircle) {
170   - for (ResultPoint point : currentLast) {
171   - canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint);
172   - }
173   - }
174   - }
175   -
176   - postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
177   - }
178   - }
179   -
180   - // 扫描线移动的y
181   - private int scanLineTop;
182   - // 扫描线移动速度
183   - private int SCAN_VELOCITY;
184   - // 扫描线
185   - private Bitmap scanLight;
186   - // 是否展示小圆点
187   - private boolean isCircle;
188   -
189   - /**
190   - * 绘制移动扫描线
191   - *
192   - * @param canvas
193   - * @param frame
194   - */
195   - private void drawScanLight(Canvas canvas, Rect frame) {
196   -
197   - if (scanLineTop == 0) {
198   - scanLineTop = frame.top;
199   - }
200   -
201   - if (scanLineTop >= frame.bottom - 30) {
202   - scanLineTop = frame.top;
203   - } else {
204   - scanLineTop += SCAN_VELOCITY;
205   - }
206   - Rect scanRect = new Rect(frame.left, scanLineTop, frame.right,
207   - scanLineTop + 30);
208   - canvas.drawBitmap(scanLight, null, scanRect, paint);
209   - }
210   -
211   -
212   - // 扫描框边角颜色
213   - private int innercornercolor;
214   - // 扫描框边角长度
215   - private int innercornerlength;
216   - // 扫描框边角宽度
217   - private int innercornerwidth;
218   -
219   - /**
220   - * 绘制取景框边框
221   - *
222   - * @param canvas
223   - * @param frame
224   - */
225   - private void drawFrameBounds(Canvas canvas, Rect frame) {
226   -
227   - /*paint.setColor(Color.WHITE);
228   - paint.setStrokeWidth(2);
229   - paint.setStyle(Paint.Style.STROKE);
230   -
231   - canvas.drawRect(frame, paint);*/
232   -
233   - paint.setColor(innercornercolor);
234   - paint.setStyle(Paint.Style.FILL);
235   -
236   - int corWidth = innercornerwidth;
237   - int corLength = innercornerlength;
238   -
239   - // 左上角
240   - canvas.drawRect(frame.left, frame.top, frame.left + corWidth, frame.top
241   - + corLength, paint);
242   - canvas.drawRect(frame.left, frame.top, frame.left
243   - + corLength, frame.top + corWidth, paint);
244   - // 右上角
245   - canvas.drawRect(frame.right - corWidth, frame.top, frame.right,
246   - frame.top + corLength, paint);
247   - canvas.drawRect(frame.right - corLength, frame.top,
248   - frame.right, frame.top + corWidth, paint);
249   - // 左下角
250   - canvas.drawRect(frame.left, frame.bottom - corLength,
251   - frame.left + corWidth, frame.bottom, paint);
252   - canvas.drawRect(frame.left, frame.bottom - corWidth, frame.left
253   - + corLength, frame.bottom, paint);
254   - // 右下角
255   - canvas.drawRect(frame.right - corWidth, frame.bottom - corLength,
256   - frame.right, frame.bottom, paint);
257   - canvas.drawRect(frame.right - corLength, frame.bottom - corWidth,
258   - frame.right, frame.bottom, paint);
259   - }
260   -
261   -
262   - public void drawViewfinder() {
263   - resultBitmap = null;
264   - invalidate();
265   - }
266   -
267   - public void addPossibleResultPoint(ResultPoint point) {
268   - possibleResultPoints.add(point);
269   - }
270   -
271   -
272   - /**
273   - * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
274   - */
275   - public static int dip2px(Context context, float dpValue) {
276   - final float scale = context.getResources().getDisplayMetrics().density;
277   - return (int) (dpValue * scale + 0.5f);
278   - }
279   -
280   -
281   -}