├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── spx
│ │ └── exoplayertest
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ ├── sogo
│ │ │ └── exoplayer
│ │ │ │ ├── LdDefaultLoadControl.java
│ │ │ │ ├── LdExoPlayerController.java
│ │ │ │ ├── PlayerManager.java
│ │ │ │ ├── PlayerWrapper.java
│ │ │ │ ├── VLog.java
│ │ │ │ ├── VUtil.java
│ │ │ │ └── VideoUrlCache.java
│ │ │ └── spx
│ │ │ └── exoplayertest
│ │ │ ├── DraggableLayout.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MyApplication.java
│ │ │ └── VideoPlayActivity.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xhdpi
│ │ ├── ic_play_video_normal.png
│ │ ├── ic_replay_normal.png
│ │ ├── sg_player_player_btn.png
│ │ ├── sg_seek_dot.png
│ │ └── sg_stop_btn.png
│ │ ├── drawable
│ │ ├── bg_video_control.xml
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── activity_video_play.xml
│ │ ├── exo_playback_control_view.xml
│ │ ├── list_item_layout.xml
│ │ └── video_layout.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values-v19
│ │ └── styles.xml
│ │ ├── values-v21
│ │ └── styles.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── spx
│ └── exoplayertest
│ └── ExampleUnitTest.java
├── build.gradle
├── core
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── spx
│ │ └── core
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── spx
│ └── core
│ └── ExampleUnitTest.java
├── gradle.properties
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ExoplayerTest
2 | exoplayer demo app
3 | 一个exoplayer2的学习示例.
4 |
5 | 包括的功能点:
6 | 1. 预加载 本地缓存
7 | 2. 列表中播放
8 | 3. 从列表中动画切换至播放页面
9 | 4. 手机非wifi模式下停止预加载
10 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | defaultConfig {
6 | applicationId "com.spx.exoplayertest"
7 | minSdkVersion 17
8 | targetSdkVersion 27
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:27.0.0'
24 | implementation 'com.android.support:recyclerview-v7:27.0.0'
25 | implementation 'com.android.support.constraint:constraint-layout:1.1.2'
26 | implementation 'com.google.android.exoplayer:exoplayer-core:2.8.3'
27 | // compile 'com.google.android.exoplayer:exoplayer-dash:2.6.0'
28 | implementation 'com.google.android.exoplayer:exoplayer-ui:2.8.3'
29 |
30 | testImplementation 'junit:junit:4.12'
31 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
33 | }
34 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/spx/exoplayertest/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.spx.exoplayertest;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.spx.exoplayertest", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sogo/exoplayer/LdDefaultLoadControl.java:
--------------------------------------------------------------------------------
1 | package com.sogo.exoplayer;
2 |
3 |
4 | import com.google.android.exoplayer2.C;
5 | import com.google.android.exoplayer2.LoadControl;
6 | import com.google.android.exoplayer2.Renderer;
7 | import com.google.android.exoplayer2.source.TrackGroupArray;
8 | import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
9 | import com.google.android.exoplayer2.upstream.Allocator;
10 | import com.google.android.exoplayer2.upstream.DefaultAllocator;
11 | import com.google.android.exoplayer2.util.PriorityTaskManager;
12 | import com.google.android.exoplayer2.util.Util;
13 |
14 | /**
15 | * The default {@link LoadControl} implementation.
16 | */
17 | public final class LdDefaultLoadControl implements LoadControl {
18 |
19 | /**
20 | * The default minimum duration of media that the player will attempt to ensure is buffered at all
21 | * times, in milliseconds.
22 | */
23 | public static final int DEFAULT_MIN_BUFFER_MS = 15000;
24 |
25 | /**
26 | * The default maximum duration of media that the player will attempt to buffer, in milliseconds.
27 | */
28 | public static final int DEFAULT_MAX_BUFFER_MS = 30000;
29 |
30 | /**
31 | * The default duration of media that must be buffered for playback to start or resume following a
32 | * user action such as a seek, in milliseconds.
33 | */
34 | public static final int DEFAULT_BUFFER_FOR_PLAYBACK_MS = 2500;
35 |
36 | /**
37 | * The default duration of media that must be buffered for playback to resume after a rebuffer,
38 | * in milliseconds. A rebuffer is defined to be caused by buffer depletion rather than a user
39 | * action.
40 | */
41 | public static final int DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = 5000;
42 |
43 | private static final int ABOVE_HIGH_WATERMARK = 0;
44 | private static final int BETWEEN_WATERMARKS = 1;
45 | private static final int BELOW_LOW_WATERMARK = 2;
46 |
47 | private final DefaultAllocator allocator;
48 |
49 | private final long minBufferUs;
50 | private final long maxBufferUs;
51 | private final long bufferForPlaybackUs;
52 | private final long bufferForPlaybackAfterRebufferUs;
53 | private final PriorityTaskManager priorityTaskManager;
54 |
55 | private int targetBufferSize;
56 | private boolean isBuffering;
57 |
58 | private boolean userAction;
59 |
60 | /**
61 | * Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class.
62 | */
63 | public LdDefaultLoadControl(boolean userAction) {
64 | this(new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE));
65 | this.userAction = userAction;
66 | }
67 |
68 | /**
69 | * Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class.
70 | *
71 | * @param allocator The {@link DefaultAllocator} used by the loader.
72 | */
73 | public LdDefaultLoadControl(DefaultAllocator allocator) {
74 | this(allocator, DEFAULT_MIN_BUFFER_MS, DEFAULT_MAX_BUFFER_MS, DEFAULT_BUFFER_FOR_PLAYBACK_MS,
75 | DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);
76 | }
77 |
78 | /**
79 | * Constructs a new instance.
80 | *
81 | * @param allocator The {@link DefaultAllocator} used by the loader.
82 | * @param minBufferMs The minimum duration of media that the player will attempt to ensure is
83 | * buffered at all times, in milliseconds.
84 | * @param maxBufferMs The maximum duration of media that the player will attempt buffer, in
85 | * milliseconds.
86 | * @param bufferForPlaybackMs The duration of media that must be buffered for playback to start or
87 | * resume following a user action such as a seek, in milliseconds.
88 | * @param bufferForPlaybackAfterRebufferMs The default duration of media that must be buffered for
89 | * playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be caused by
90 | * buffer depletion rather than a user action.
91 | */
92 | public LdDefaultLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs,
93 | long bufferForPlaybackMs, long bufferForPlaybackAfterRebufferMs) {
94 | this(allocator, minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs,
95 | null);
96 | }
97 |
98 | /**
99 | * Constructs a new instance.
100 | *
101 | * @param allocator The {@link DefaultAllocator} used by the loader.
102 | * @param minBufferMs The minimum duration of media that the player will attempt to ensure is
103 | * buffered at all times, in milliseconds.
104 | * @param maxBufferMs The maximum duration of media that the player will attempt buffer, in
105 | * milliseconds.
106 | * @param bufferForPlaybackMs The duration of media that must be buffered for playback to start or
107 | * resume following a user action such as a seek, in milliseconds.
108 | * @param bufferForPlaybackAfterRebufferMs The default duration of media that must be buffered for
109 | * playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be caused by
110 | * buffer depletion rather than a user action.
111 | * @param priorityTaskManager If not null, registers itself as a task with priority
112 | * {@link C#PRIORITY_PLAYBACK} during loading periods, and unregisters itself during draining
113 | * periods.
114 | */
115 | public LdDefaultLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs,
116 | long bufferForPlaybackMs, long bufferForPlaybackAfterRebufferMs,
117 | PriorityTaskManager priorityTaskManager) {
118 | this.allocator = allocator;
119 | minBufferUs = minBufferMs * 1000L;
120 | maxBufferUs = maxBufferMs * 1000L;
121 | bufferForPlaybackUs = bufferForPlaybackMs * 1000L;
122 | bufferForPlaybackAfterRebufferUs = bufferForPlaybackAfterRebufferMs * 1000L;
123 | this.priorityTaskManager = priorityTaskManager;
124 | }
125 |
126 | @Override
127 | public void onPrepared() {
128 | reset(false);
129 | }
130 |
131 | @Override
132 | public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
133 | TrackSelectionArray trackSelections) {
134 | targetBufferSize = 0;
135 | for (int i = 0; i < renderers.length; i++) {
136 | if (trackSelections.get(i) != null) {
137 | targetBufferSize += Util.getDefaultBufferSize(renderers[i].getTrackType());
138 | }
139 | }
140 | allocator.setTargetBufferSize(targetBufferSize);
141 | }
142 |
143 | @Override
144 | public void onStopped() {
145 | reset(true);
146 | }
147 |
148 | @Override
149 | public void onReleased() {
150 | reset(true);
151 | }
152 |
153 | @Override
154 | public Allocator getAllocator() {
155 | return allocator;
156 | }
157 |
158 | @Override
159 | public long getBackBufferDurationUs() {
160 | return 0;
161 | }
162 |
163 | @Override
164 | public boolean retainBackBufferFromKeyframe() {
165 | return false;
166 | }
167 |
168 | // @Override
169 | // public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
170 | // return false;
171 | // }
172 |
173 | @Override
174 | public boolean shouldStartPlayback(long bufferedDurationUs, float playbackSpeed, boolean rebuffering) {
175 | long minBufferDurationUs = rebuffering ? bufferForPlaybackAfterRebufferUs : bufferForPlaybackUs;
176 | return minBufferDurationUs <= 0 || bufferedDurationUs >= minBufferDurationUs;
177 | }
178 |
179 | // @Override
180 | // public boolean shouldStartPlayback(long bufferedDurationUs, boolean rebuffering) {
181 | // long minBufferDurationUs = rebuffering ? bufferForPlaybackAfterRebufferUs : bufferForPlaybackUs;
182 | // return minBufferDurationUs <= 0 || bufferedDurationUs >= minBufferDurationUs;
183 | // }
184 |
185 | // @Override
186 | public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
187 | if (!VUtil.isWifiConnected(VUtil.getApplication())) {
188 | if(!userAction){
189 | return false;
190 | }
191 |
192 | }
193 | int bufferTimeState = getBufferTimeState(bufferedDurationUs);
194 | boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
195 | boolean wasBuffering = isBuffering;
196 | isBuffering = bufferTimeState == BELOW_LOW_WATERMARK
197 | || (bufferTimeState == BETWEEN_WATERMARKS && isBuffering && !targetBufferSizeReached);
198 | if (priorityTaskManager != null && isBuffering != wasBuffering) {
199 | if (isBuffering) {
200 | priorityTaskManager.add(C.PRIORITY_PLAYBACK);
201 | } else {
202 | priorityTaskManager.remove(C.PRIORITY_PLAYBACK);
203 | }
204 | }
205 | return isBuffering;
206 | }
207 |
208 | private int getBufferTimeState(long bufferedDurationUs) {
209 | return bufferedDurationUs > maxBufferUs ? ABOVE_HIGH_WATERMARK
210 | : (bufferedDurationUs < minBufferUs ? BELOW_LOW_WATERMARK : BETWEEN_WATERMARKS);
211 | }
212 |
213 | private void reset(boolean resetAllocator) {
214 | targetBufferSize = 0;
215 | if (priorityTaskManager != null && isBuffering) {
216 | priorityTaskManager.remove(C.PRIORITY_PLAYBACK);
217 | }
218 | isBuffering = false;
219 | if (resetAllocator) {
220 | allocator.reset();
221 | }
222 | }
223 |
224 | }
225 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sogo/exoplayer/LdExoPlayerController.java:
--------------------------------------------------------------------------------
1 | package com.sogo.exoplayer;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.text.TextUtils;
6 | import android.view.View;
7 | import android.widget.ImageView;
8 | import android.widget.ProgressBar;
9 | import android.widget.Toast;
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.source.TrackGroupArray;
17 | import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
18 | import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
19 | import com.spx.exoplayertest.R;
20 |
21 |
22 | import java.io.IOException;
23 |
24 | /**
25 | * Created by shaopengxiang on 2017/12/18.
26 | */
27 |
28 | public class LdExoPlayerController implements PlayerManager.PlayerHolder {
29 |
30 | private static final String TAG = "Player.Controller";
31 |
32 | private Context mContext;
33 | private int mId;
34 | private String mContent;
35 | private String mVideoUrl;
36 | private String mCoverImgUrl;
37 |
38 |
39 | private SimpleExoPlayerView mPlayerView;
40 | private PlayerWrapper mPlayer;
41 | private ImageView mCoverIv;
42 | private ImageView mStartPlayIv;
43 |
44 | private ProgressBar mProgressBar;
45 |
46 | private long mOnClickTime = 0L;
47 |
48 | public LdExoPlayerController(View itemView) {
49 | mPlayerView = itemView.findViewById(R.id.player_view);
50 | mPlayer = PlayerManager.getInstance().getPlayer(this);
51 | mCoverIv = itemView.findViewById(R.id.video_cover_iv);
52 | mStartPlayIv = itemView.findViewById(R.id.video_startplay_iv);
53 | mProgressBar = itemView.findViewById(R.id.progressBar);
54 |
55 | if (mCoverIv != null) {
56 | mCoverIv.setOnClickListener(clickListener);
57 | }
58 |
59 | mStartPlayIv.setOnClickListener(clickListener);
60 | }
61 |
62 | public void bindData(Context context, int id, String content, String videoUrl, String coverImgUrl) {
63 | this.mContext = context;
64 | this.mId = id;
65 | this.mContent = content;
66 | this.mVideoUrl = videoUrl;
67 | this.mCoverImgUrl = coverImgUrl;
68 | VLog.e(TAG, "bindData[" + id + "]: content:" + content);
69 | // Log.d(TAG, "bindData["+position+"]: video url:"+card.video.getVideoUrl());
70 |
71 | // ImageUtils.showImage(context, mCoverIv, mCoverImgUrl);
72 |
73 | PlayerManager.getInstance().preAttch(videoUrl, this);
74 |
75 | initViews();
76 | }
77 |
78 | public void transformIn(String videoUrl){
79 | PlayerManager instance = PlayerManager.getInstance();
80 | PlayerWrapper player = instance.getPlayer(null);
81 | player.transformIn(mPlayerView);
82 | }
83 |
84 | public void transformOut(){
85 | PlayerManager instance = PlayerManager.getInstance();
86 | PlayerWrapper player = instance.getPlayer(null);
87 | player.transformOut(mPlayerView);
88 | }
89 |
90 | View.OnClickListener clickListener = new View.OnClickListener() {
91 | @Override
92 | public void onClick(View v) {
93 | VLog.d(TAG, "mStartPlayIv onClick: ...");
94 | start(true);
95 | }
96 | };
97 |
98 | public void start(boolean userAction) {
99 | mOnClickTime = System.currentTimeMillis();
100 | mStartPlayIv.setVisibility(View.GONE);
101 | // 点击时初始化player
102 | initPlayer(userAction);
103 | mPlayer.play();
104 | }
105 |
106 |
107 | private SimpleExoPlayer.VideoListener videoListener = new SimpleExoPlayer.VideoListener() {
108 | @Override
109 | public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
110 | VLog.d(TAG, "onVideoSizeChanged: ...width:" + width + ", height:" + height
111 | + ", pixelWidthHeightRatio:" + pixelWidthHeightRatio);
112 | }
113 |
114 | @Override
115 | public void onRenderedFirstFrame() {
116 | VLog.d(TAG, "onRenderedFirstFrame: ...");
117 | long playingTime = System.currentTimeMillis();
118 | long useTime = playingTime - mOnClickTime;
119 | if (useTime < 100000) {
120 | VLog.d(TAG, "播放: use:" + (useTime) + "ms");
121 | showToast("播放: 耗时:" + (useTime) + "ms");
122 | }
123 |
124 | }
125 | };
126 |
127 | private Player.EventListener listener = new Player.EventListener() {
128 | // @Override
129 | // public void onTimelineChanged(Timeline timeline, Object manifest) {
130 | // VLog.d(TAG, "onTimelineChanged: timeline:" + timeline.toString());
131 | // }
132 |
133 | @Override
134 | public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) {
135 |
136 | }
137 |
138 | @Override
139 | public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
140 |
141 | }
142 |
143 | @Override
144 | public void onLoadingChanged(boolean isLoading) {
145 | VLog.d(TAG, "onLoadingChanged content:" + mContent);
146 | VLog.d(TAG, "onLoadingChanged: isLoading:" + isLoading);
147 | }
148 |
149 | @Override
150 | public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
151 | VLog.d(TAG, "onPlayerStateChanged content:" + mContent);
152 | VLog.d(TAG, "onPlayerStateChanged: playWhenReady:" + playWhenReady + ", playbackState:" + playbackState);
153 | mProgressBar.setVisibility(View.GONE);
154 | if (playbackState == Player.STATE_BUFFERING) {
155 | if (mPlayer != null && mPlayer.isPlaying()) {
156 | mProgressBar.setVisibility(View.VISIBLE);
157 | }
158 | }
159 |
160 | // 开始播放
161 | if (playbackState == Player.STATE_READY) {
162 |
163 | if (playWhenReady) {
164 | mCoverIv.setVisibility(View.GONE);
165 | }
166 | }
167 |
168 | if (playbackState == Player.STATE_ENDED) {
169 | mPlayer.onPlayFinished();
170 | mStartPlayIv.setImageResource(R.drawable.ic_replay_normal);
171 | mStartPlayIv.setVisibility(View.VISIBLE);
172 | // mCoverIv.setAlpha(1f);
173 | mCoverIv.setVisibility(View.VISIBLE);
174 | }
175 | }
176 |
177 | @Override
178 | public void onRepeatModeChanged(int repeatMode) {
179 |
180 | }
181 |
182 | @Override
183 | public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
184 |
185 | }
186 |
187 | @Override
188 | public void onPlayerError(ExoPlaybackException error) {
189 | VLog.d(TAG, "onPlayerError content:" + mContent);
190 | VLog.d(TAG, "onPlayerError mId:" + mId);
191 | VLog.e(TAG, "onPlayerError: ", error);
192 | IOException sourceException = error.getSourceException();
193 | if (sourceException != null && sourceException.getClass().getName().contains("InvalidResponseCodeException")) {
194 | Toast.makeText(mContext, "这个视频403了, 请一会重试", Toast.LENGTH_SHORT).show();
195 |
196 | }
197 |
198 | }
199 |
200 | @Override
201 | public void onPositionDiscontinuity(int reason) {
202 | VLog.d(TAG, "onLoadingChanged content:" + mContent);
203 | VLog.d(TAG, "onPositionDiscontinuity: reason:" + reason);
204 | }
205 |
206 | @Override
207 | public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
208 |
209 | }
210 |
211 | @Override
212 | public void onSeekProcessed() {
213 |
214 | }
215 | };
216 |
217 | private void showToast(final String s) {
218 | mPlayerView.post(new Runnable() {
219 | @Override
220 | public void run() {
221 | Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();
222 | }
223 | });
224 | }
225 |
226 |
227 | private void initViews() {
228 | mProgressBar.setVisibility(View.GONE);
229 | mStartPlayIv.setImageResource(R.drawable.ic_play_video_normal);
230 | mStartPlayIv.setVisibility(View.VISIBLE);
231 | mCoverIv.setAlpha(1f);
232 | mCoverIv.setVisibility(View.VISIBLE);
233 | }
234 |
235 | public void initPlayer(boolean userAction) {
236 | if (!TextUtils.isEmpty(mVideoUrl)) {
237 |
238 | String newUrl = VideoUrlCache.getVideoUrl(mId);
239 | if (!TextUtils.isEmpty(newUrl)) {
240 | mVideoUrl = newUrl;
241 | }
242 |
243 | PlayerManager.getInstance().attchPlayer(mContext, this, mPlayerView,
244 | mVideoUrl, listener, videoListener, userAction);
245 |
246 | }
247 | }
248 |
249 | public void resetUi() {
250 | initViews();
251 | }
252 |
253 | public void onViewAttachedToWindow() {
254 | VLog.d(TAG, "onViewAttachedToWindow content:" + mContent + ", articleId:" + mId);
255 | PlayerManager.getInstance().preAttch(mVideoUrl, this);
256 | resetUi();
257 | }
258 |
259 |
260 | public void onViewDetachedFromWindow() {
261 | VLog.d(TAG, "onViewDetachedFromWindow content:" + mContent + ", articleId:" + mId);
262 | VLog.d(TAG, "onViewDetachedFromWindow: ...videoUrl:" + mVideoUrl);
263 | PlayerManager.getInstance().detachPlayer(this, mVideoUrl);
264 | }
265 |
266 | public void release(){
267 | VLog.d(TAG, "release content:" + mContent + ", articleId:" + mId);
268 | PlayerManager.getInstance().release(mVideoUrl);
269 | }
270 |
271 | @Override
272 | public void onDetached() {
273 | // 跟player分离后, 恢复为默认卡片显示
274 | resetUi();
275 | }
276 |
277 | @Override
278 | public void preload() {
279 | initPlayer(false);
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sogo/exoplayer/PlayerManager.java:
--------------------------------------------------------------------------------
1 | package com.sogo.exoplayer;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import com.google.android.exoplayer2.Player;
7 | import com.google.android.exoplayer2.SimpleExoPlayer;
8 | import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | /**
14 | * Created by shaopengxiang on 2017/12/18.
15 | */
16 |
17 | public class PlayerManager {
18 | private static final String TAG = "PlayerManager";
19 | private static final boolean USE_PRELOAD = true;
20 |
21 | private static PlayerManager instance = new PlayerManager();
22 |
23 | private String sVideoUrl = null;
24 | private PlayerHolder sPlayerHolder = null;
25 |
26 | private Map currentPlayerHolders = new HashMap<>();
27 |
28 | private PlayerManager() {
29 | }
30 |
31 | public static PlayerManager getInstance() {
32 | return instance;
33 | }
34 |
35 | private static PlayerWrapper player = PlayerWrapper.getInstance();
36 |
37 | public PlayerWrapper getPlayer(PlayerHolder viewHolder) {
38 | return player;
39 | }
40 |
41 | public void preAttch(final String videoUrl, PlayerHolder playerHolder) {
42 | currentPlayerHolders.put(videoUrl, playerHolder);
43 | if (USE_PRELOAD) {
44 | new Thread(new Runnable() {
45 | @Override
46 | public void run() {
47 | player.preload(videoUrl);
48 | }
49 | }).start();
50 |
51 | }
52 | }
53 |
54 | public interface PlayerHolder {
55 | void onDetached();
56 |
57 | void preload();
58 | }
59 |
60 | public void preload(int pos, String videoUrl) {
61 | if (!VUtil.isWifiConnected(VUtil.getApplication())) {
62 | VLog.d(TAG, "preload[" + pos + "]: NO WIFI NO PRELOAD!" + videoUrl);
63 | return;
64 | }
65 |
66 | VLog.d(TAG, "preload[" + pos + "]: videoUrl:" + videoUrl);
67 | VLog.d(TAG, "preload[" + pos + "]: sVideoUrl:" + sVideoUrl);
68 | VLog.d(TAG, "preload[" + pos + "]: player:" + player);
69 | VLog.d(TAG, "preload[" + pos + "]: player.isPlaying():" + player.isPlaying());
70 | if (player.isPlaying() && videoUrl.equals(player.getPlayUrl())) {
71 | return;
72 | }
73 | PlayerHolder playerHolder = currentPlayerHolders.get(videoUrl);
74 | VLog.d(TAG, "preload[" + pos + "]: playerHolder:" + playerHolder);
75 | if (playerHolder != null) {
76 | playerHolder.preload();
77 | }
78 |
79 | }
80 |
81 | // 初始化 exoplayer
82 | public void attchPlayer(Context context, PlayerHolder playerHolder, SimpleExoPlayerView simpleExoPlayerView,
83 | String videoUri,
84 | Player.EventListener eventListener,
85 | SimpleExoPlayer.VideoListener videoListener,
86 | boolean userAction) {
87 | VLog.d(TAG, "attchPlayer videoUrl:" + videoUri + ", player:" + player);
88 | VLog.d(TAG, "attchPlayer player.isReleased():" + player.isReleased());
89 | if ((sPlayerHolder == playerHolder || videoUri.equals(player.getPlayUrl()))
90 | && !player.isReleased()) {
91 | // if (player.isPlaying()) {
92 | Log.d(TAG, "attchPlayer: return!");
93 | return;
94 | // }
95 | }
96 |
97 | long start = System.currentTimeMillis();
98 | if (player != null) {
99 | player.release();
100 |
101 | if (!videoUri.equals(sVideoUrl) && sPlayerHolder != null && sPlayerHolder != playerHolder) {
102 | sPlayerHolder.onDetached();
103 | }
104 | }
105 | sPlayerHolder = playerHolder;
106 | sVideoUrl = videoUri;
107 |
108 | player.init(context, simpleExoPlayerView, videoUri, eventListener, videoListener, userAction);
109 | currentPlayerHolders.put(videoUri, playerHolder);
110 | long end = System.currentTimeMillis();
111 | VLog.d(TAG, "attchPlayer finished! use:" + (end - start) + "ms");
112 | }
113 |
114 | public void detachPlayer(PlayerHolder playerHolder, String videoUrl) {
115 | VLog.d(TAG, "detachPlayer: videoUrl:" + videoUrl);
116 | if (sPlayerHolder != null
117 | && sPlayerHolder == playerHolder
118 | & videoUrl.equals(sVideoUrl)) {
119 |
120 | VLog.d(TAG, "detachPlayer: will pause player!");
121 | if (player != null) {
122 | player.pause();
123 | }
124 |
125 | sPlayerHolder.onDetached();
126 | sPlayerHolder = null;
127 | sVideoUrl = null;
128 | currentPlayerHolders.remove(videoUrl);
129 | }
130 | }
131 |
132 | public void release(String videoUrl){
133 | if (player != null) {
134 | player.release();
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sogo/exoplayer/PlayerWrapper.java:
--------------------------------------------------------------------------------
1 | package com.sogo.exoplayer;
2 |
3 |
4 | import android.content.Context;
5 | import android.net.Uri;
6 |
7 | import com.google.android.exoplayer2.DefaultRenderersFactory;
8 | import com.google.android.exoplayer2.ExoPlayer;
9 | import com.google.android.exoplayer2.ExoPlayerFactory;
10 | import com.google.android.exoplayer2.Player;
11 | import com.google.android.exoplayer2.SimpleExoPlayer;
12 | import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
13 | import com.google.android.exoplayer2.extractor.ExtractorsFactory;
14 | import com.google.android.exoplayer2.source.ExtractorMediaSource;
15 | import com.google.android.exoplayer2.source.MediaSource;
16 | import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
17 | import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
18 | import com.google.android.exoplayer2.trackselection.TrackSelection;
19 | import com.google.android.exoplayer2.trackselection.TrackSelector;
20 | import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
21 | import com.google.android.exoplayer2.upstream.BandwidthMeter;
22 | import com.google.android.exoplayer2.upstream.DataSource;
23 | import com.google.android.exoplayer2.upstream.DataSpec;
24 | import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
25 | import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
26 | import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
27 | import com.google.android.exoplayer2.upstream.cache.CacheUtil;
28 | import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
29 | import com.google.android.exoplayer2.upstream.cache.SimpleCache;
30 | import com.google.android.exoplayer2.util.Util;
31 | import com.google.android.exoplayer2.video.VideoListener;
32 | import com.spx.exoplayertest.R;
33 |
34 | import java.io.File;
35 | import java.io.IOException;
36 |
37 |
38 | /**
39 | * Manages the {@link ExoPlayer}, the IMA plugin and all video playback.
40 | */
41 | public final class PlayerWrapper {
42 |
43 | private static final String TAG = "Player.Wrapper";
44 | private static PlayerWrapper instance = new PlayerWrapper();
45 |
46 | public static PlayerWrapper getInstance() {
47 | return instance;
48 | }
49 |
50 | // private final ImaAdsLoader adsLoader;
51 |
52 | private SimpleExoPlayer player;
53 | private SimpleExoPlayerView simpleExoPlayerView;
54 | private long contentPosition;
55 | private boolean playFinished = false;
56 | private boolean isPlaying = false;
57 |
58 | private TrackSelector mTrackSelector;
59 | private String mVideoUrl;
60 | private Player.EventListener mEventListener;
61 | private VideoListener mVideoListener;
62 |
63 |
64 | private DataSource.Factory dataSourceFactory = null;
65 | private ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
66 |
67 | private File cacheFile = null;
68 |
69 | SimpleCache simpleCache = null;
70 | DataSource.Factory cachedDataSourceFactory = null;
71 | // This is the MediaSource representing the content media (i.e. not the ad).
72 | // String contentUrl = context.getString(R.string.content_url);
73 | // MediaSource contentMediaSource = new ExtractorMediaSource(
74 | // Uri.parse(contentUrl), dataSourceFactory, extractorsFactory, null, null);
75 |
76 | // Compose the content media source into a new AdsMediaSource with both ads and content.
77 | // MediaSource mediaSourceWithAds = new AdsMediaSource(contentMediaSource, dataSourceFactory,
78 | // adsLoader, simpleExoPlayerView.getOverlayFrameLayout());
79 |
80 |
81 | // This is the MediaSource representing the media to be played.
82 | MediaSource videoSource = null;
83 |
84 | private PlayerWrapper() {
85 | dataSourceFactory = new DefaultDataSourceFactory(VUtil.getApplication(),
86 | Util.getUserAgent(VUtil.getApplication(), VUtil.getApplication().getString(R.string.app_name)));
87 | cacheFile = new File(VUtil.getApplication().getExternalCacheDir().getAbsolutePath(), "video");
88 | VLog.d(TAG, "PlayerWrapper() cache file:" + cacheFile.getAbsolutePath());
89 | simpleCache = new SimpleCache(cacheFile, new LeastRecentlyUsedCacheEvictor(512 * 1024 * 1024));
90 | cachedDataSourceFactory = new CacheDataSourceFactory(simpleCache, dataSourceFactory);
91 | BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
92 | TrackSelection.Factory videoTrackSelectionFactory =
93 | new AdaptiveTrackSelection.Factory(bandwidthMeter);
94 | mTrackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
95 |
96 | }
97 |
98 | public void preload(String videoUri) {
99 | DataSpec dataSpec = new DataSpec(Uri.parse(videoUri), 0, 512 * 1024, null);
100 | CacheUtil.CachingCounters counters = new CacheUtil.CachingCounters();
101 | try {
102 | CacheUtil.cache(dataSpec, simpleCache, dataSourceFactory.createDataSource(), counters, null);
103 | } catch (IOException e) {
104 | e.printStackTrace();
105 | } catch (InterruptedException e) {
106 | e.printStackTrace();
107 | }
108 | }
109 |
110 | public void init(Context context, SimpleExoPlayerView simpleExoPlayerView, String videoUri,
111 | Player.EventListener eventListener, SimpleExoPlayer.VideoListener videoListener,
112 | boolean userAction) {
113 | this.mVideoUrl = videoUri;
114 | this.mEventListener = eventListener;
115 | this.mVideoListener = videoListener;
116 | initPlayer(context, simpleExoPlayerView, userAction);
117 | }
118 |
119 | private void initPlayer(Context context, SimpleExoPlayerView simpleExoPlayerView, boolean userAction) {
120 | // Create a default track selector.
121 | VLog.d(TAG, "initPlayer: ... " + userAction);
122 | this.simpleExoPlayerView = simpleExoPlayerView;
123 | player = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(context),
124 | mTrackSelector, new LdDefaultLoadControl(userAction));
125 |
126 | // Bind the player to the view.
127 | simpleExoPlayerView.setPlayer(player);
128 |
129 | // Produces DataSource instances through which media data is loaded.
130 |
131 | // DataSource.Factory dataSourceFactory = new OkHttpDataSourceFactory();
132 | // Produces Extractor instances for parsing the content media (i.e. not the ad).
133 |
134 |
135 | // Prepare the player with the source.
136 | isPlaying = false;
137 | // player.seekTo(contentPosition);
138 |
139 | new Thread(new Runnable() {
140 | @Override
141 | public void run() {
142 | videoSource = new ExtractorMediaSource(Uri.parse(mVideoUrl),
143 | cachedDataSourceFactory, extractorsFactory, null, null);
144 | player.prepare(videoSource);
145 | }
146 | }).start();
147 |
148 | // player.setPlayWhenReady(true);
149 |
150 | player.addListener(mEventListener);
151 | player.addVideoListener(mVideoListener);
152 | hasReleased = false;
153 | }
154 |
155 | public void transformIn(SimpleExoPlayerView newSimpleExoPlayerView){
156 | newSimpleExoPlayerView.setPlayer(player);
157 | simpleExoPlayerView.setPlayer(null);
158 | }
159 | public void transformOut(SimpleExoPlayerView newSimpleExoPlayerView){
160 | simpleExoPlayerView.setPlayer(player);
161 | newSimpleExoPlayerView.setPlayer(null);
162 | }
163 |
164 | public void onPlayFinished() {
165 | VLog.d(TAG, "onPlayFinished: ...");
166 | playFinished = true;
167 | isPlaying = false;
168 | }
169 |
170 | public boolean isPlaying() {
171 | return isPlaying;
172 | }
173 |
174 | public void play() {
175 | VLog.d(TAG, "play: ...");
176 | if (playFinished) {
177 | player.seekTo(0);
178 | playFinished = false;
179 | }
180 | player.setPlayWhenReady(true);
181 |
182 | if (player.getPlaybackState() == Player.STATE_IDLE) {
183 | VLog.d(TAG, "player is IDLE!!!");
184 | }
185 |
186 | isPlaying = true;
187 | hasReleased = false;
188 | }
189 |
190 | public boolean isIdle() {
191 | if (player != null) {
192 | if (player.getPlaybackState() == Player.STATE_IDLE) {
193 | return true;
194 | }
195 | }
196 | return false;
197 | }
198 |
199 | public void pause() {
200 | VLog.d(TAG, "pause: ...");
201 | if (player != null) {
202 | isPlaying = false;
203 | player.stop();
204 | }
205 | }
206 |
207 | private boolean hasReleased = false;
208 |
209 | public void release() {
210 | VLog.d(TAG, "release: ..."+this);
211 | if (player != null) {
212 | isPlaying = false;
213 | player.removeListener(mEventListener);
214 | player.removeVideoListener(mVideoListener);
215 | player.release();
216 | hasReleased = true;
217 | VLog.d(TAG, "release: ...hasReleased!");
218 | // player = null;
219 | }
220 | // adsLoader.release();
221 | }
222 |
223 | public String getPlayUrl() {
224 | return mVideoUrl;
225 | }
226 |
227 | public boolean isReleased() {
228 | return hasReleased;
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sogo/exoplayer/VLog.java:
--------------------------------------------------------------------------------
1 | package com.sogo.exoplayer;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by shaopengxiang on 2017/12/20.
7 | */
8 |
9 | public class VLog {
10 | public static void d(String tag, String msg) {
11 | Log.d(tag, msg);
12 | }
13 |
14 | public static void d(String tag, String msg, Throwable th) {
15 | Log.d(tag, msg, th);
16 | }
17 |
18 | public static void e(String tag, String msg) {
19 | Log.d(tag, msg);
20 | }
21 |
22 | public static void e(String tag, String msg, Throwable th) {
23 | Log.d(tag, msg, th);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sogo/exoplayer/VUtil.java:
--------------------------------------------------------------------------------
1 | package com.sogo.exoplayer;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.net.ConnectivityManager;
6 | import android.net.NetworkInfo;
7 |
8 | import com.spx.exoplayertest.MyApplication;
9 |
10 | import java.lang.reflect.Field;
11 |
12 |
13 | /**
14 | * Created by shaopengxiang on 2017/12/20.
15 | */
16 |
17 | public class VUtil {
18 |
19 | /**
20 | * 判断wifi是否连接
21 | *
22 | * @param context
23 | * @return true:wifi连接状态
24 | */
25 | public static boolean isWifiConnected(Context context) {
26 | ConnectivityManager connManager = (ConnectivityManager) context
27 | .getSystemService(Context.CONNECTIVITY_SERVICE);
28 | NetworkInfo mWifi = connManager
29 | .getNetworkInfo(ConnectivityManager.TYPE_WIFI);
30 | return mWifi.isConnected();
31 | }
32 |
33 | public static Application getApplication(){
34 | return MyApplication.getApp();
35 | }
36 |
37 | // 获得通知栏高度
38 | public static int getStatusBarHeight(Context context) {
39 | Class> c = null;
40 | Object obj = null;
41 | Field field = null;
42 | int x = 0, statusBarHeight = 0;
43 | try {
44 | c = Class.forName("com.android.internal.R$dimen");
45 | obj = c.newInstance();
46 | field = c.getField("status_bar_height");
47 | x = Integer.parseInt(field.get(obj).toString());
48 | statusBarHeight = context.getResources().getDimensionPixelSize(x);
49 | } catch (Exception e1) {
50 | e1.printStackTrace();
51 | }
52 | return statusBarHeight;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sogo/exoplayer/VideoUrlCache.java:
--------------------------------------------------------------------------------
1 | package com.sogo.exoplayer;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * @auther shaopx
8 | * @date 2017/12/13.
9 | */
10 |
11 | public class VideoUrlCache {
12 |
13 | private static Map videoUrls = new HashMap<>();
14 |
15 | public static void putVideoUrl(int id, String data) {
16 | videoUrls.put(id, data);
17 | }
18 |
19 | public static String getVideoUrl(int id){
20 | return videoUrls.get(id);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/spx/exoplayertest/DraggableLayout.java:
--------------------------------------------------------------------------------
1 | package com.spx.exoplayertest;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.Log;
6 | import android.view.MotionEvent;
7 | import android.view.View;
8 | import android.widget.RelativeLayout;
9 |
10 | /**
11 | * Created by shaopengxiang on 2017/12/22.
12 | */
13 |
14 | public class DraggableLayout extends RelativeLayout {
15 |
16 | private static final String TAG = "DraggableLayout";
17 | private View mDragView;
18 |
19 | public DraggableLayout(Context context) {
20 | super(context);
21 | }
22 |
23 | public DraggableLayout(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public DraggableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
28 | super(context, attrs, defStyleAttr);
29 | }
30 |
31 | @Override
32 | protected void onFinishInflate() {
33 | super.onFinishInflate();
34 | mDragView = findViewById(R.id.player_area);
35 | }
36 |
37 | private float rawX, rawY;
38 | private float last_x, last_y;
39 |
40 | // @Override
41 | // public boolean dispatchTouchEvent(MotionEvent ev) {
42 | // Log.d(TAG, "dispatchTouchEvent: ..."+ev.toString());
43 | // return super.dispatchTouchEvent(ev);
44 | // }
45 | //
46 | // @Override
47 | // public boolean onInterceptTouchEvent(MotionEvent ev) {
48 | // Log.d(TAG, "onInterceptTouchEvent: ...");
49 | //// return super.onInterceptTouchEvent(ev);
50 | // return true;
51 | // }
52 | //
53 | // @Override
54 | // public boolean onTouchEvent(MotionEvent event) {
55 | // Log.d(TAG, "onTouchEvent: ..."+event.toString());
56 | // switch (event.getAction()) {
57 | // case MotionEvent.ACTION_DOWN:
58 | // rawX = event.getX();
59 | // rawY = event.getY();
60 | // break;
61 | // case MotionEvent.ACTION_MOVE:
62 | // float touch_x = event.getX();
63 | // float touch_y = event.getY();
64 | // mDragView.setTranslationY(touch_y - rawY);
65 | // mDragView.setTranslationX(touch_x - rawX);
66 | // last_y = touch_y;
67 | // last_x = touch_x;
68 | // break;
69 | // case MotionEvent.ACTION_UP:
70 | // break;
71 | // }
72 | // return true;
73 | // }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/spx/exoplayertest/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.spx.exoplayertest;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.TextView;
12 |
13 | import com.sogo.exoplayer.LdExoPlayerController;
14 |
15 | import java.util.Arrays;
16 | import java.util.List;
17 |
18 | /**
19 | *
20 | */
21 | public class MainActivity extends AppCompatActivity {
22 | private String[] sources = new String[]{
23 | "http://video.wenwen.sogou.com/e75aa445e10f4a51cf9b1df72b947e40.mp4.f20.mp4",
24 | "http://video.wenwen.sogou.com/8640cc39cd2abaa644dbc9f4eef4b49e.mp4.f20.mp4",
25 | "http://video.wenwen.sogou.com/31bc728f5bd22e0cf6aa4898f7f78f0d.mp4.f20.mp4",
26 | "http://video.wenwen.sogou.com/8cd0ffa1bea136eef0b42057f305b7b0.mp4.f20.mp4",
27 | "http://video.wenwen.sogou.com/4dfd051e27a32ae319588e7066937398.mp4.f20.mp4",
28 | };
29 |
30 | private RecyclerView mRecyclerView;
31 | private Adapter mAdapter;
32 |
33 | private List mUrls = Arrays.asList(sources);
34 |
35 | @Override
36 | protected void onCreate(Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 | setContentView(R.layout.activity_main);
39 |
40 | mRecyclerView = findViewById(R.id.recyclerView);
41 | mAdapter = new Adapter();
42 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
43 | mRecyclerView.setAdapter(mAdapter);
44 |
45 |
46 | }
47 |
48 | @Override
49 | protected void onResume() {
50 | super.onResume();
51 | }
52 |
53 | @Override
54 | protected void onPause() {
55 | super.onPause();
56 | }
57 |
58 | private class ViewHolder extends RecyclerView.ViewHolder {
59 |
60 | private LdExoPlayerController controller;
61 | private TextView titleTv;
62 | private View playView;
63 |
64 | public ViewHolder(final View itemView) {
65 | super(itemView);
66 | controller = new LdExoPlayerController(itemView);
67 | titleTv = itemView.findViewById(R.id.title_tv);
68 | playView = itemView.findViewById(R.id.player_view);
69 |
70 |
71 | itemView.setOnClickListener(new View.OnClickListener() {
72 | @Override
73 | public void onClick(View v) {
74 | int adapterPosition = getAdapterPosition();
75 | String url = mUrls.get(adapterPosition);
76 | Intent intent = new Intent(itemView.getContext(), VideoPlayActivity.class);
77 | intent.putExtra("play_url", url);
78 | intent.putExtra("play_id", adapterPosition);
79 | int[] location = new int[2];
80 | playView.getLocationOnScreen(location);
81 | int width = playView.getWidth();
82 | int height = playView.getHeight();
83 | intent.putExtra("view_x", location[0]);
84 | intent.putExtra("view_y", location[1]);
85 | intent.putExtra("view_w", width);
86 | intent.putExtra("view_h", height);
87 |
88 | itemView.getContext().startActivity(intent);
89 | itemView.postDelayed(new Runnable() {
90 | @Override
91 | public void run() {
92 | controller.onDetached();
93 | }
94 | }, 300);
95 | }
96 | });
97 | }
98 |
99 | public void bindData(int position) {
100 | titleTv.setText("这是测试数据:[" + position + "] url:" + mUrls.get(position));
101 |
102 | controller.bindData(itemView.getContext(), position,
103 | "position_" + position, mUrls.get(position), "");
104 | }
105 | }
106 |
107 | private class Adapter extends RecyclerView.Adapter {
108 |
109 | @Override
110 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
111 | LayoutInflater inflater = LayoutInflater.from(parent.getContext());
112 | View view = inflater.inflate(R.layout.list_item_layout, parent, false);
113 | return new ViewHolder(view);
114 | }
115 |
116 | @Override
117 | public void onBindViewHolder(ViewHolder holder, int position) {
118 | holder.bindData(position);
119 | }
120 |
121 | @Override
122 | public int getItemCount() {
123 | return mUrls.size();
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/com/spx/exoplayertest/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.spx.exoplayertest;
2 |
3 | import android.app.Application;
4 |
5 | /**
6 | * Created by shaopengxiang on 2017/12/20.
7 | */
8 |
9 | public class MyApplication extends Application {
10 | private static MyApplication sApplication = null;
11 | @Override
12 | public void onCreate() {
13 | super.onCreate();
14 | sApplication = this;
15 | }
16 |
17 | public static MyApplication getApp(){
18 | return sApplication;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/spx/exoplayertest/VideoPlayActivity.java:
--------------------------------------------------------------------------------
1 | package com.spx.exoplayertest;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.PropertyValuesHolder;
6 | import android.animation.ValueAnimator;
7 | import android.app.Activity;
8 | import android.graphics.Color;
9 | import android.os.Bundle;
10 | import android.util.DisplayMetrics;
11 | import android.util.Log;
12 | import android.view.MotionEvent;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.view.animation.AccelerateDecelerateInterpolator;
16 |
17 | import com.sogo.exoplayer.LdExoPlayerController;
18 | import com.sogo.exoplayer.VUtil;
19 |
20 | public class VideoPlayActivity extends Activity {
21 | private static final String TAG = "VideoPlayActivity";
22 | private static final long ANIMATION_DURATION = 300;
23 |
24 | private int mVideoId;
25 | private String mVideoUrl = null;
26 | private View playView;
27 | private LdExoPlayerController controller;
28 | private View mRootView;
29 |
30 | private int viewX, viewY, viewW, viewH;
31 | private ValueAnimator animatorStart = new ValueAnimator();
32 | private ValueAnimator animatorEnd = new ValueAnimator();
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.activity_video_play);
38 |
39 | mRootView = findViewById(R.id.rootview);
40 | // mRootView.setOnTouchListener(new View.OnTouchListener() {
41 | // @Override
42 | // public boolean onTouch(View v, MotionEvent event) {
43 | // Log.d(TAG, "onTouch: ..."+event.toString());
44 | // return true;
45 | // }
46 | // });
47 |
48 | mVideoId = getIntent().getIntExtra("play_id", 0);
49 | mVideoUrl = getIntent().getStringExtra("play_url");
50 |
51 | viewX = getIntent().getIntExtra("view_x", 0);
52 | viewY = getIntent().getIntExtra("view_y", 0);
53 | viewW = getIntent().getIntExtra("view_w", 0);
54 | viewH = getIntent().getIntExtra("view_h", 0);
55 |
56 | Log.d(TAG, "onCreate: url:" + mVideoUrl);
57 | Log.d(TAG, "onCreate: viewX:" + viewX + ", viewY:" + viewY + ", viewW:" + viewW + ", viewH:" + viewH);
58 |
59 | playView = findViewById(R.id.player_area);
60 | controller = new LdExoPlayerController(playView);
61 | controller.bindData(this, mVideoId, "", mVideoUrl, "");
62 | controller.initPlayer(false);
63 |
64 | controller.transformIn(mVideoUrl);
65 |
66 | initTransform();
67 | transformIn();
68 | }
69 |
70 | private void transformIn() {
71 | animatorStart.start();
72 | }
73 |
74 | private void transformOut() {
75 | animatorEnd.start();
76 | }
77 |
78 | private void initTransform() {
79 | DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
80 | int widthPixels = displayMetrics.widthPixels;
81 | int heightPixels = displayMetrics.heightPixels;
82 |
83 | int playerViewHeight = getResources().getDimensionPixelSize(R.dimen.player_view_height);
84 | int statusBarHeight = VUtil.getStatusBarHeight(this);
85 | Log.d(TAG, "onCreate: statusBarHeight:" + statusBarHeight);
86 | viewY = viewY - statusBarHeight;
87 |
88 | {
89 | animatorStart.setDuration(ANIMATION_DURATION);
90 | animatorStart.setInterpolator(new AccelerateDecelerateInterpolator());
91 |
92 | PropertyValuesHolder wHolder = PropertyValuesHolder.ofInt("width", viewW, widthPixels);
93 | PropertyValuesHolder hHolder = PropertyValuesHolder.ofInt("height", viewH, playerViewHeight);
94 | PropertyValuesHolder xHolder = PropertyValuesHolder.ofFloat("transactionX", viewX, 0);
95 | PropertyValuesHolder yHolder = PropertyValuesHolder.ofFloat("transactionY", viewY, 0);
96 | animatorStart.setValues(wHolder, hHolder, xHolder, yHolder);
97 |
98 | animatorStart.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
99 | @Override
100 | public void onAnimationUpdate(ValueAnimator animation) {
101 | int width = (Integer) animation.getAnimatedValue("width");
102 | int height = (Integer) animation.getAnimatedValue("height");
103 | float transactionX = (float) animation.getAnimatedValue("transactionX");
104 | float transactionY = (float) animation.getAnimatedValue("transactionY");
105 |
106 | ViewGroup.LayoutParams layoutParams = playView.getLayoutParams();
107 | layoutParams.width = width;
108 | layoutParams.height = height;
109 | playView.setLayoutParams(layoutParams);
110 |
111 | playView.setTranslationX(transactionX);
112 | playView.setTranslationY(transactionY);
113 |
114 | float animatedFraction = animation.getAnimatedFraction();
115 | float alpha = animatedFraction * 0.8f;
116 | Log.d(TAG, "onAnimationUpdate: alpha:" + alpha);
117 | mRootView.setBackgroundColor(getColorWithAlpha(alpha, Color.BLACK));
118 | }
119 | });
120 | }
121 |
122 |
123 | {
124 | animatorEnd.setDuration(ANIMATION_DURATION);
125 | animatorEnd.setInterpolator(new AccelerateDecelerateInterpolator());
126 |
127 | PropertyValuesHolder wHolder = PropertyValuesHolder.ofInt("width", widthPixels, viewW);
128 | PropertyValuesHolder hHolder = PropertyValuesHolder.ofInt("height", playerViewHeight, viewH);
129 | PropertyValuesHolder xHolder = PropertyValuesHolder.ofFloat("transactionX", 0, viewX);
130 | PropertyValuesHolder yHolder = PropertyValuesHolder.ofFloat("transactionY", 0, viewY);
131 | animatorEnd.setValues(wHolder, hHolder, xHolder, yHolder);
132 |
133 | animatorEnd.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
134 | @Override
135 | public void onAnimationUpdate(ValueAnimator animation) {
136 | int width = (Integer) animation.getAnimatedValue("width");
137 | int height = (Integer) animation.getAnimatedValue("height");
138 | float transactionX = (float) animation.getAnimatedValue("transactionX");
139 | float transactionY = (float) animation.getAnimatedValue("transactionY");
140 |
141 | ViewGroup.LayoutParams layoutParams = playView.getLayoutParams();
142 | layoutParams.width = width;
143 | layoutParams.height = height;
144 | playView.setLayoutParams(layoutParams);
145 |
146 | playView.setTranslationX(transactionX);
147 | playView.setTranslationY(transactionY);
148 |
149 | float animatedFraction = animation.getAnimatedFraction();
150 | float alpha = (1 - animatedFraction) * 0.8f;
151 | Log.d(TAG, "onAnimationUpdate: alpha:" + alpha);
152 | mRootView.setBackgroundColor(getColorWithAlpha(alpha, Color.BLACK));
153 | }
154 | });
155 |
156 | animatorEnd.addListener(new AnimatorListenerAdapter() {
157 | @Override
158 | public void onAnimationEnd(Animator animation) {
159 | exit();
160 | }
161 | });
162 | }
163 |
164 | }
165 |
166 | @Override
167 | protected void onResume() {
168 | super.onResume();
169 | controller.onViewAttachedToWindow();
170 | }
171 |
172 | @Override
173 | protected void onPause() {
174 | super.onPause();
175 | controller.onViewDetachedFromWindow();
176 | }
177 |
178 | @Override
179 | public void onBackPressed() {
180 | transformOut();
181 | }
182 |
183 | @Override
184 | protected void onDestroy() {
185 | super.onDestroy();
186 | controller.release();
187 | }
188 |
189 | /**
190 | * 关闭页面
191 | */
192 | public void exit() {
193 | controller.transformOut();
194 | finish();
195 | overridePendingTransition(0, 0);
196 | }
197 |
198 | @Override
199 | public void onWindowFocusChanged(boolean hasFocus) {
200 | super.onWindowFocusChanged(hasFocus);
201 | // if (hasFocus && Build.VERSION.SDK_INT >= 19) {
202 | // View decorView = getWindow().getDecorView();
203 | // decorView.setSystemUiVisibility(
204 | // View.SYSTEM_UI_FLAG_LAYOUT_STABLE
205 | // | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
206 | // | View.SYSTEM_UI_FLAG_FULLSCREEN
207 | // | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
208 | // }
209 | }
210 |
211 | public static int getColorWithAlpha(float alpha, int baseColor) {
212 | int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24;
213 | int rgb = 0x00ffffff & baseColor;
214 | return a + rgb;
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_play_video_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/drawable-xhdpi/ic_play_video_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_replay_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/drawable-xhdpi/ic_replay_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sg_player_player_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/drawable-xhdpi/sg_player_player_btn.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sg_seek_dot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/drawable-xhdpi/sg_seek_dot.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sg_stop_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/drawable-xhdpi/sg_stop_btn.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_video_control.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_video_play.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/exo_playback_control_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
22 |
23 |
33 |
34 |
42 |
43 |
44 |
53 |
54 |
63 |
64 |
73 |
74 |
75 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
18 |
19 |
27 |
28 |
29 |
30 |
31 |
32 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/video_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
33 |
34 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaopx/ExoplayerTest/23e61519f7097a26b15ff8861093e502bd3562c6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-v19/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 220dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ExoplayerTest
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/test/java/com/spx/exoplayertest/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.spx.exoplayertest;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.0.1'
11 |
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/core/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 17
10 | targetSdkVersion 26
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 |
30 | implementation 'com.android.support:appcompat-v7:26.1.0'
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
34 | }
35 |
--------------------------------------------------------------------------------
/core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/core/src/androidTest/java/com/spx/core/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.spx.core;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.spx.core.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/core/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | core
3 |
4 |
--------------------------------------------------------------------------------
/core/src/test/java/com/spx/core/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.spx.core;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':core'
2 |
--------------------------------------------------------------------------------