commit a35d7977d559035dff879a52d803af453cd2f557
Author: Administrator <981964879@qq.com>
Date: Fri Feb 26 18:29:46 2021 +0800
init
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..124894a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+
+/.idea/
+/app/src/test/java/com/uiui/videoplayer/
+/app/src/androidTest/java/com/uiui/videoplayer/
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..973830e
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,67 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 28
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.uiui.videoplayer"
+ minSdkVersion 24
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ multiDexEnabled true
+ ndk {
+ // add support lib
+ abiFilters 'armeabi-v7a' //, 'arm64-v8a'//, "mips" //,'armeabi''x86',, 'x86_64',
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ implementation 'androidx.recyclerview:recyclerview:1.1.0'
+
+ implementation 'com.github.bumptech.glide:glide:4.11.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
+
+ implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
+ implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
+
+ implementation 'cn.jzvd:jiaozivideoplayer:7.6.0'
+ implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.15'
+ implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native:1.0.15'
+ implementation 'com.github.ittianyu:BottomNavigationViewEx:2.0.4'
+ implementation 'com.github.ctiao:DanmakuFlameMaster:0.9.25'
+ implementation 'com.github.ctiao:ndkbitmap-armv7a:0.9.21'
+ implementation 'com.danikula:videocache:2.7.0'
+ implementation 'com.aliyun.sdk.android:AliyunPlayer:4.5.0-full'
+ implementation 'com.alivc.conan:AlivcConan:0.9.5'
+ implementation 'com.scwang.smart:refresh-layout-kernel:2.0.1'
+ implementation 'com.scwang.smart:refresh-header-material:2.0.1'
+ implementation 'com.scwang.smart:refresh-footer-classics:2.0.1'
+ implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
+ implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4'
+ debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3'
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..387bf01
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/uiui/videoplayer/CustomJzvd/JzvdStdAssert.java b/app/src/main/java/com/uiui/videoplayer/CustomJzvd/JzvdStdAssert.java
new file mode 100644
index 0000000..3b68198
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/CustomJzvd/JzvdStdAssert.java
@@ -0,0 +1,27 @@
+package com.uiui.videoplayer.CustomJzvd;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import cn.jzvd.JzvdStd;
+
+public class JzvdStdAssert extends JzvdStd {
+ public JzvdStdAssert(Context context) {
+ super(context);
+ }
+
+ public JzvdStdAssert(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+
+ @Override
+ public void onPrepared() {
+ state = STATE_PREPARED;
+ if (!preloading) {
+ mediaInterface.start();
+ preloading = false;
+ }
+ onStatePlaying();
+ }
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/CustomJzvd/JzvdStdTikTok.java b/app/src/main/java/com/uiui/videoplayer/CustomJzvd/JzvdStdTikTok.java
new file mode 100644
index 0000000..221cac2
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/CustomJzvd/JzvdStdTikTok.java
@@ -0,0 +1,101 @@
+package com.uiui.videoplayer.CustomJzvd;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.uiui.videoplayer.R;
+
+import cn.jzvd.JzvdStd;
+
+public class JzvdStdTikTok extends JzvdStd {
+ public JzvdStdTikTok(Context context) {
+ super(context);
+ }
+
+ public JzvdStdTikTok(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void init(Context context) {
+ super.init(context);
+ bottomContainer.setVisibility(GONE);
+ topContainer.setVisibility(GONE);
+ bottomProgressBar.setVisibility(GONE);
+ posterImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ }
+
+
+ //changeUiTo 真能能修改ui的方法
+ @Override
+ public void changeUiToNormal() {
+ super.changeUiToNormal();
+ bottomContainer.setVisibility(GONE);
+ topContainer.setVisibility(GONE);
+ }
+
+ @Override
+ public void setAllControlsVisiblity(int topCon, int bottomCon, int startBtn, int loadingPro,
+ int posterImg, int bottomPro, int retryLayout) {
+ topContainer.setVisibility(INVISIBLE);
+ bottomContainer.setVisibility(INVISIBLE);
+ startButton.setVisibility(startBtn);
+ loadingProgressBar.setVisibility(loadingPro);
+ posterImageView.setVisibility(posterImg);
+ bottomProgressBar.setVisibility(GONE);
+ mRetryLayout.setVisibility(retryLayout);
+ }
+
+ @Override
+ public void dissmissControlView() {
+ if (state != STATE_NORMAL
+ && state != STATE_ERROR
+ && state != STATE_AUTO_COMPLETE) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ bottomContainer.setVisibility(View.INVISIBLE);
+ topContainer.setVisibility(View.INVISIBLE);
+ startButton.setVisibility(View.INVISIBLE);
+ if (clarityPopWindow != null) {
+ clarityPopWindow.dismiss();
+ }
+ if (screen != SCREEN_TINY) {
+ bottomProgressBar.setVisibility(View.GONE);
+ }
+ }
+ });
+ }
+ }
+
+
+ @Override
+ public void onClickUiToggle() {
+ super.onClickUiToggle();
+ Log.i(TAG, "click blank");
+ startButton.performClick();
+ bottomContainer.setVisibility(GONE);
+ topContainer.setVisibility(GONE);
+ }
+
+ public void updateStartImage() {
+ if (state == STATE_PLAYING) {
+ startButton.setVisibility(VISIBLE);
+ startButton.setImageResource(R.drawable.tiktok_play_tiktok);
+ replayTextView.setVisibility(GONE);
+ } else if (state == STATE_ERROR) {
+ startButton.setVisibility(INVISIBLE);
+ replayTextView.setVisibility(GONE);
+ } else if (state == STATE_AUTO_COMPLETE) {
+ startButton.setVisibility(VISIBLE);
+ startButton.setImageResource(R.drawable.tiktok_play_tiktok);
+ replayTextView.setVisibility(VISIBLE);
+ } else {
+ startButton.setImageResource(R.drawable.tiktok_play_tiktok);
+ replayTextView.setVisibility(GONE);
+ }
+ }
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/CustomJzvd/MyJzvdStd.java b/app/src/main/java/com/uiui/videoplayer/CustomJzvd/MyJzvdStd.java
new file mode 100644
index 0000000..9fe6846
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/CustomJzvd/MyJzvdStd.java
@@ -0,0 +1,188 @@
+package com.uiui.videoplayer.CustomJzvd;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.SeekBar;
+
+import com.uiui.videoplayer.R;
+
+import cn.jzvd.JzvdStd;
+
+/**
+ * 这里可以监听到视频播放的生命周期和播放状态
+ * 所有关于视频的逻辑都应该写在这里
+ * Created by Nathen on 2017/7/2.
+ */
+public class MyJzvdStd extends JzvdStd {
+ public MyJzvdStd(Context context) {
+ super(context);
+ }
+
+ public MyJzvdStd(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void init(Context context) {
+ super.init(context);
+ }
+
+ @Override
+ public void onClick(View v) {
+ super.onClick(v);
+ int i = v.getId();
+ if (i == cn.jzvd.R.id.fullscreen) {
+ Log.i(TAG, "onClick: fullscreen button");
+ } else if (i == R.id.start) {
+ Log.i(TAG, "onClick: start button");
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ super.onTouch(v, event);
+ int id = v.getId();
+ if (id == cn.jzvd.R.id.surface_container) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_UP:
+ if (mChangePosition) {
+ Log.i(TAG, "Touch screen seek position");
+ }
+ if (mChangeVolume) {
+ Log.i(TAG, "Touch screen change volume");
+ }
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int getLayoutId() {
+ return R.layout.jz_layout_std;
+ }
+
+ @Override
+ public void startVideo() {
+ super.startVideo();
+ Log.i(TAG, "startVideo");
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ super.onStopTrackingTouch(seekBar);
+ Log.i(TAG, "Seek position ");
+ }
+
+ @Override
+ public void gotoFullscreen() {
+ super.gotoFullscreen();
+ Log.i(TAG, "goto Fullscreen");
+ }
+
+ @Override
+ public void gotoNormalScreen() {
+ super.gotoNormalScreen();
+ Log.i(TAG, "quit Fullscreen");
+ }
+
+ @Override
+ public void autoFullscreen(float x) {
+ super.autoFullscreen(x);
+ Log.i(TAG, "auto Fullscreen");
+ }
+
+ @Override
+ public void onClickUiToggle() {
+ super.onClickUiToggle();
+ Log.i(TAG, "click blank");
+ }
+
+ //onState 代表了播放器引擎的回调,播放视频各个过程的状态的回调
+ @Override
+ public void onStateNormal() {
+ super.onStateNormal();
+ }
+
+ @Override
+ public void onStatePreparing() {
+ super.onStatePreparing();
+ }
+
+ @Override
+ public void onStatePlaying() {
+ super.onStatePlaying();
+ }
+
+ @Override
+ public void onStatePause() {
+ super.onStatePause();
+ }
+
+ @Override
+ public void onStateError() {
+ super.onStateError();
+ }
+
+ @Override
+ public void onStateAutoComplete() {
+ super.onStateAutoComplete();
+ Log.i(TAG, "Auto complete");
+ }
+
+ //changeUiTo 真能能修改ui的方法
+ @Override
+ public void changeUiToNormal() {
+ super.changeUiToNormal();
+ }
+
+ @Override
+ public void changeUiToPreparing() {
+ super.changeUiToPreparing();
+ }
+
+ @Override
+ public void changeUiToPlayingShow() {
+ super.changeUiToPlayingShow();
+ }
+
+ @Override
+ public void changeUiToPlayingClear() {
+ super.changeUiToPlayingClear();
+ }
+
+ @Override
+ public void changeUiToPauseShow() {
+ super.changeUiToPauseShow();
+ }
+
+ @Override
+ public void changeUiToPauseClear() {
+ super.changeUiToPauseClear();
+ }
+
+ @Override
+ public void changeUiToComplete() {
+ super.changeUiToComplete();
+ }
+
+ @Override
+ public void changeUiToError() {
+ super.changeUiToError();
+ }
+
+ @Override
+ public void onInfo(int what, int extra) {
+ super.onInfo(what, extra);
+ }
+
+ @Override
+ public void onError(int what, int extra) {
+ super.onError(what, extra);
+ }
+
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/activity/ActivityTikTok.java b/app/src/main/java/com/uiui/videoplayer/activity/ActivityTikTok.java
new file mode 100644
index 0000000..3670ccd
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/activity/ActivityTikTok.java
@@ -0,0 +1,137 @@
+package com.uiui.videoplayer.activity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.OrientationHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.uiui.videoplayer.CustomJzvd.JzvdStdAssert;
+import com.uiui.videoplayer.CustomJzvd.JzvdStdTikTok;
+import com.uiui.videoplayer.R;
+import com.uiui.videoplayer.adapter.TikTokRecyclerViewAdapter;
+import com.uiui.videoplayer.base.ViewPagerLayoutManager;
+import com.uiui.videoplayer.listener.OnViewPagerListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.jzvd.Jzvd;
+
+public class ActivityTikTok extends AppCompatActivity {
+
+ private RecyclerView rvTiktok;
+ private TikTokRecyclerViewAdapter mAdapter;
+ private ViewPagerLayoutManager mViewPagerLayoutManager;
+ private int mCurrentPosition = -1;
+ private int position = 0;
+ private List videoPath = new ArrayList<>();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ setContentView(R.layout.activity_tiktok);
+ Intent intent = getIntent();
+ if (null != intent) {
+ position = intent.getIntExtra("position", 0);
+ videoPath = intent.getStringArrayListExtra("list");
+ }
+ rvTiktok = findViewById(R.id.rv_tiktok);
+
+ mAdapter = new TikTokRecyclerViewAdapter(this,videoPath );
+ mViewPagerLayoutManager = new ViewPagerLayoutManager(this, OrientationHelper.VERTICAL);
+ rvTiktok.setLayoutManager(mViewPagerLayoutManager);
+ rvTiktok.setAdapter(mAdapter);
+ rvTiktok.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+
+ }
+ });
+ mViewPagerLayoutManager.setOnViewPagerListener(new OnViewPagerListener() {
+ @Override
+ public void onInitComplete() {
+ //自动播放第一条
+ autoPlayVideo(position);
+ }
+
+ @Override
+ public void onPageRelease(boolean isNext, int position) {
+ if (mCurrentPosition == position) {
+ Jzvd.releaseAllVideos();
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position, boolean isBottom) {
+ if (mCurrentPosition == position) {
+ return;
+ }
+ autoPlayVideo(position);
+ mCurrentPosition = position;
+ }
+ });
+
+ rvTiktok.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
+ @Override
+ public void onChildViewAttachedToWindow(View view) {
+
+ }
+
+ @Override
+ public void onChildViewDetachedFromWindow(View view) {
+ Jzvd jzvd = view.findViewById(R.id.videoplayer);
+ if (jzvd != null && Jzvd.CURRENT_JZVD != null && jzvd.jzDataSource != null &&
+ jzvd.jzDataSource.containsTheUrl(Jzvd.CURRENT_JZVD.jzDataSource.getCurrentUrl())) {
+ if (Jzvd.CURRENT_JZVD != null && Jzvd.CURRENT_JZVD.screen != Jzvd.SCREEN_FULLSCREEN) {
+ Jzvd.releaseAllVideos();
+ }
+ }
+ }
+ });
+ }
+
+ private void autoPlayVideo(int postion) {
+ if (rvTiktok == null || rvTiktok.getChildAt(0) == null) {
+ return;
+ }
+ JzvdStdAssert player = rvTiktok.getChildAt(0).findViewById(R.id.videoplayer);
+ if (player != null) {
+ player.startVideoAfterPreloading();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (Jzvd.backPress()) {
+ return;
+ }
+ super.onBackPressed();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Jzvd.releaseAllVideos();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/activity/MainActivity.java b/app/src/main/java/com/uiui/videoplayer/activity/MainActivity.java
new file mode 100644
index 0000000..e2b8015
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/activity/MainActivity.java
@@ -0,0 +1,195 @@
+package com.uiui.videoplayer.activity;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+
+import com.uiui.videoplayer.R;
+import com.uiui.videoplayer.base.RecycleGridLayoutManager;
+import com.uiui.videoplayer.base.SpacesItemDecoration;
+import com.uiui.videoplayer.adapter.VideoAdapter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class MainActivity extends AppCompatActivity {
+ private static final int REQUEST_PERMISSION_CODE = 200;
+ String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE};
+ private RecyclerView recyclerView;
+ private VideoAdapter adapter;
+ private RecycleGridLayoutManager mManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ checkSelfPermission();
+ initView();
+// String rootPath = Environment.getExternalStorageDirectory().getPath() + File.separator;
+// traverseFolder(rootPath);
+ ScanTask scanTask = new ScanTask();
+ scanTask.execute();
+ }
+
+ private void initView() {
+ recyclerView = findViewById(R.id.recyclerView);
+ }
+
+ // private String[] getFolder(String folderPath) {
+//
+// }
+ public void traverseFolder(String path) {
+ int fileNum = 0, folderNum = 0;
+ File file = new File(path);
+ if (file.exists()) {
+ LinkedList list = new LinkedList();
+ File[] files = file.listFiles();
+ for (File file2 : files) {
+ if (file2.isDirectory()) {
+ Log.e("traverseFolder1", "文件夹:" + file2.getAbsolutePath());
+ list.add(file2);
+ folderNum++;
+ } else {
+ Log.e("traverseFolder1", "文件:" + file2.getAbsolutePath());
+ fileNum++;
+ }
+ }
+ File temp_file;
+ while (!list.isEmpty()) {
+ temp_file = list.removeFirst();
+ files = temp_file.listFiles();
+ for (File file2 : files) {
+ if (file2.isDirectory()) {
+ Log.e("traverseFolder2", "文件夹:" + file2.getAbsolutePath());
+ list.add(file2);
+ folderNum++;
+ } else {
+ Log.e("traverseFolder2", "文件:" + file2.getAbsolutePath());
+ fileNum++;
+ }
+ }
+ }
+ } else {
+ Log.e("traverseFolder1", "文件不存在!");
+ }
+ Log.e("traverseFolder1", "文件夹共有:" + folderNum + ",文件共有:" + fileNum);
+ }
+
+ private static final String[] extension = new String[]{
+ ".3gp", ".flv", ".mkv", ".mov", ".mp4", ".webm"
+ };
+
+ private static boolean isVideoFormat(String filePath) {
+ for (String s : extension) {
+ if (filePath.endsWith(s)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public class ScanTask extends AsyncTask> {
+
+ @Override
+ protected List doInBackground(Void... voids) {
+ List fileList = new ArrayList<>();
+ String rootPath = Environment.getExternalStorageDirectory().getPath() + File.separator;
+ File file = new File(rootPath);
+ if (file.exists()) {
+ LinkedList list = new LinkedList();
+ File[] files = file.listFiles();
+ for (File file2 : files) {
+ publishProgress(file2.getAbsolutePath());
+ if (file2.isDirectory()) {
+ list.add(file2);
+ } else {
+ if (isVideoFormat(file2.getAbsolutePath())) {
+ fileList.add(file2.getAbsolutePath());
+ }
+ }
+ }
+ File temp_file;
+ while (!list.isEmpty()) {
+ temp_file = list.removeFirst();
+ files = temp_file.listFiles();
+ for (File file2 : files) {
+ publishProgress(file2.getAbsolutePath());
+ if (file2.isDirectory()) {
+ list.add(file2);
+ } else {
+ if (isVideoFormat(file2.getAbsolutePath())) {
+ fileList.add(file2.getAbsolutePath());
+ }
+ }
+ }
+ }
+ } else {
+ Log.e("traverseFolder1", "文件不存在!");
+ }
+ return fileList;
+ }
+
+ @Override
+ protected void onProgressUpdate(String... values) {
+ super.onProgressUpdate(values);
+// Log.e("ScanTask", "onProgressUpdate: " + values[0]);
+ }
+
+ @Override
+ protected void onPostExecute(List strings) {
+ super.onPostExecute(strings);
+ Log.e("ScanTask", "onPostExecute: " + strings);
+ adapter = new VideoAdapter(MainActivity.this, strings);
+ mManager = new RecycleGridLayoutManager(MainActivity.this, 2);
+ recyclerView.setLayoutManager(mManager);
+ recyclerView.setNestedScrollingEnabled(false);
+ recyclerView.addItemDecoration(new SpacesItemDecoration(getResources().getDimensionPixelSize(R.dimen.PX1x), getResources().getDimensionPixelSize(R.dimen.PX1x),
+ getResources().getDimensionPixelSize(R.dimen.PX1x), getResources().getDimensionPixelSize(R.dimen.PX1x)));
+ ((DefaultItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
+// recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
+ recyclerView.setAdapter(adapter);
+ }
+ }
+
+ private void checkSelfPermission() {
+ List mPermissionList = new ArrayList<>();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ for (String s : permissions) {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
+ mPermissionList.add(s);
+ }
+ }
+ if (mPermissionList.size() > 0) {//有权限没有通过,需要申请
+ ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSION_CODE);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == REQUEST_PERMISSION_CODE) {
+ //当然权限多了,建议使用Switch,不必纠结于此
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+
+ } else if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
+
+ }
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/activity/PlayActivity.java b/app/src/main/java/com/uiui/videoplayer/activity/PlayActivity.java
new file mode 100644
index 0000000..12a495d
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/activity/PlayActivity.java
@@ -0,0 +1,16 @@
+package com.uiui.videoplayer.activity;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+
+import com.uiui.videoplayer.R;
+
+public class PlayActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_play);
+ }
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/adapter/TikTokRecyclerViewAdapter.java b/app/src/main/java/com/uiui/videoplayer/adapter/TikTokRecyclerViewAdapter.java
new file mode 100644
index 0000000..513f9f3
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/adapter/TikTokRecyclerViewAdapter.java
@@ -0,0 +1,73 @@
+package com.uiui.videoplayer.adapter;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.uiui.videoplayer.CustomJzvd.JzvdStdAssert;
+import com.uiui.videoplayer.R;
+
+
+import java.util.List;
+
+import cn.jzvd.JZDataSource;
+import cn.jzvd.Jzvd;
+
+public class TikTokRecyclerViewAdapter extends RecyclerView.Adapter {
+
+ public static final String TAG = "AdapterTikTokRecyclerView";
+ private List videoPath;
+ private Context context;
+
+ public TikTokRecyclerViewAdapter(Context context) {
+ this.context = context;
+ }
+
+ public TikTokRecyclerViewAdapter(Context context, List list) {
+ this.context = context;
+ this.videoPath = list;
+ }
+
+ @Override
+ public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
+ context).inflate(R.layout.item_tiktok, parent,
+ false));
+ return holder;
+ }
+
+ @SuppressLint("LongLogTag")
+ @Override
+ public void onBindViewHolder(MyViewHolder holder, int position) {
+ Log.i(TAG, "onBindViewHolder [" + holder.jzvdStdAssert.hashCode() + "] position=" + position);
+ final String path = videoPath.get(position);
+ JZDataSource jzDataSource = new JZDataSource(path, getFileName(path));
+ jzDataSource.looping = true;
+ holder.jzvdStdAssert.setUp(jzDataSource, Jzvd.SCREEN_NORMAL);
+// Glide.with(holder.jzvdStd.getContext()).load(UrlsKt.getPl3()[position]).into(holder.jzvdStd.posterImageView);
+ }
+
+ @Override
+ public int getItemCount() {
+ return videoPath == null ? 0 : videoPath.size();
+ }
+
+ private String getFileName(String path) {
+ return path.substring(path.lastIndexOf("/") + 1);
+ }
+
+ class MyViewHolder extends RecyclerView.ViewHolder {
+ JzvdStdAssert jzvdStdAssert;
+
+ public MyViewHolder(View itemView) {
+ super(itemView);
+ jzvdStdAssert = itemView.findViewById(R.id.videoplayer);
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/adapter/VideoAdapter.java b/app/src/main/java/com/uiui/videoplayer/adapter/VideoAdapter.java
new file mode 100644
index 0000000..ce739b8
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/adapter/VideoAdapter.java
@@ -0,0 +1,168 @@
+package com.uiui.videoplayer.adapter;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.media.Image;
+import android.media.MediaMetadataRetriever;
+import android.os.AsyncTask;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.uiui.videoplayer.R;
+import com.uiui.videoplayer.activity.ActivityTikTok;
+import com.uiui.videoplayer.utils.ToastUtil;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+public class VideoAdapter extends RecyclerView.Adapter {
+ private Context mContext;
+ private List videoPath;
+
+
+ public VideoAdapter() {
+
+ }
+
+ public VideoAdapter(Context context) {
+ this.mContext = context;
+ }
+
+ public VideoAdapter(Context context, List path) {
+ this.mContext = context;
+ this.videoPath = path;
+ }
+
+ @NonNull
+ @Override
+ public VideoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ VideoHolder holder = new VideoHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_videofile, parent, false));
+ return holder;
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final VideoHolder holder, final int position) {
+ final String path = videoPath.get(position);
+ File file = new File(path);
+ if (file.exists() && file.isFile()) {
+// BitmapRetultListener bitmapRetultListener = new BitmapRetultListener() {
+// @Override
+// public void onScanCompleted(Bitmap bitmap) {
+// Glide.with(holder.video_image).load(bitmap).into(holder.video_image);
+// }
+// };
+ Observable.create(new ObservableOnSubscribe() {
+ @Override
+ public void subscribe(ObservableEmitter emitter) throws Exception {
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ mmr.setDataSource(path);
+ String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
+ Long time = Long.valueOf(duration);
+ Bitmap bitmap = mmr.getFrameAtTime();//获得视频第一帧的Bitmap对象
+ emitter.onNext(bitmap);
+ }
+ }).subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Observer() {
+ @Override
+ public void onSubscribe(Disposable d) {
+
+ }
+
+ @Override
+ public void onNext(Bitmap bitmap) {
+ Glide.with(holder.video_image).load(bitmap).into(holder.video_image);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+
+ }
+
+ @Override
+ public void onComplete() {
+
+ }
+ });
+
+// this.listener = bitmapRetultListener;
+ holder.title.setText(getFileName(path));
+ }
+ holder.root.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+// ToastUtil.show(path);
+ Intent intent = new Intent(mContext, ActivityTikTok.class);
+ intent.putExtra("position", position);
+ intent.putStringArrayListExtra("list", (ArrayList) videoPath);
+ mContext.startActivity(intent);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return videoPath == null ? 0 : videoPath.size();
+ }
+
+ private String getFileName(String path) {
+ return path.substring(path.lastIndexOf("/") + 1);
+ }
+
+ static class VideoHolder extends RecyclerView.ViewHolder {
+ ImageView video_image;
+ TextView title;
+ ConstraintLayout root;
+
+ public VideoHolder(@NonNull View itemView) {
+ super(itemView);
+ video_image = itemView.findViewById(R.id.video_image);
+ title = itemView.findViewById(R.id.title_text);
+ root = itemView.findViewById(R.id.root);
+ }
+
+ }
+
+ private BitmapRetultListener listener;
+
+ class BitmapTask extends AsyncTask {
+ @Override
+ protected Bitmap doInBackground(String... strings) {
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ mmr.setDataSource(strings[0]);
+ Bitmap bitmap = mmr.getFrameAtTime();//获得视频第一帧的Bitmap对象
+ return bitmap;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ listener.onScanCompleted(bitmap);
+ }
+
+ }
+
+
+ interface BitmapRetultListener {
+ void onScanCompleted(Bitmap bitmap);
+ }
+
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/base/MyApplication.java b/app/src/main/java/com/uiui/videoplayer/base/MyApplication.java
new file mode 100644
index 0000000..a3956ab
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/base/MyApplication.java
@@ -0,0 +1,13 @@
+package com.uiui.videoplayer.base;
+
+import android.app.Application;
+
+import com.uiui.videoplayer.utils.ToastUtil;
+
+public class MyApplication extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ ToastUtil.init(this);
+ }
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/base/RecycleGridLayoutManager.java b/app/src/main/java/com/uiui/videoplayer/base/RecycleGridLayoutManager.java
new file mode 100644
index 0000000..35273e9
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/base/RecycleGridLayoutManager.java
@@ -0,0 +1,116 @@
+package com.uiui.videoplayer.base;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Created by Administrator on 2016/11/18.
+ */
+
+public class RecycleGridLayoutManager extends GridLayoutManager {
+
+ private int mwidth = 0;
+ private int mheight = 0;
+ private int[] mMeasuredDimension = new int[2];
+
+ public RecycleGridLayoutManager(Context context, int spanCount) {
+ super(context, spanCount);
+ }
+
+ public RecycleGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
+ super(context, spanCount, orientation, reverseLayout);
+ }
+
+ public int getMwidth() {
+ return mwidth;
+ }
+
+ public void setMwidth(int mwidth) {
+ this.mwidth = mwidth;
+ }
+
+ public int getMheight() {
+ return mheight;
+ }
+
+ public void setMheight(int mheight) {
+ this.mheight = mheight;
+ }
+
+ @Override
+ public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
+ final int widthMode = View.MeasureSpec.getMode(widthSpec);
+ final int heightMode = View.MeasureSpec.getMode(heightSpec);
+ final int widthSize = View.MeasureSpec.getSize(widthSpec);
+ final int heightSize = View.MeasureSpec.getSize(heightSpec);
+ int width = 0;
+ int height = 0;
+ int count = getItemCount();
+ int span = getSpanCount();
+ for (int i = 0; i < count; i++) {
+ measureScrapChild(recycler, i,
+ View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
+ mMeasuredDimension);
+
+ if (getOrientation() == HORIZONTAL) {
+ if (i % span == 0) {
+ width = width + mMeasuredDimension[0];
+ }
+ if (i == 0) {
+ height = mMeasuredDimension[1];
+ }
+ } else {
+ if (i % span == 0) {
+ height = height + mMeasuredDimension[1];
+ }
+ if (i == 0) {
+ width = mMeasuredDimension[0];
+ }
+ }
+ }
+
+ switch (widthMode) {
+ case View.MeasureSpec.EXACTLY:
+ width = widthSize;
+ case View.MeasureSpec.AT_MOST:
+ case View.MeasureSpec.UNSPECIFIED:
+ }
+
+ switch (heightMode) {
+ case View.MeasureSpec.EXACTLY:
+ height = heightSize;
+ case View.MeasureSpec.AT_MOST:
+ case View.MeasureSpec.UNSPECIFIED:
+ }
+ setMheight(height);
+ setMwidth(width);
+ setMeasuredDimension(width, height);
+ }
+
+ private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
+ int heightSpec, int[] measuredDimension) {
+ if (position < getItemCount()) {
+ try {
+ View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException
+ if (view != null) {
+ RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
+ int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
+ getPaddingLeft() + getPaddingRight(), p.width);
+ int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
+ getPaddingTop() + getPaddingBottom(), p.height);
+ view.measure(childWidthSpec, childHeightSpec);
+ measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
+ measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
+ recycler.recycleView(view);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/uiui/videoplayer/base/SpacesItemDecoration.java b/app/src/main/java/com/uiui/videoplayer/base/SpacesItemDecoration.java
new file mode 100644
index 0000000..98b9f68
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/base/SpacesItemDecoration.java
@@ -0,0 +1,50 @@
+package com.uiui.videoplayer.base;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * RecycleView item 间距设置
+ */
+public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
+ private int left, right, bottom, top;
+
+ public SpacesItemDecoration(int space) {
+ this.left = space;
+ this.right = space;
+ this.bottom = space;
+ this.top = space;
+ }
+
+ public SpacesItemDecoration(int top, int bottom, boolean flag) {
+ this.top = top;
+ this.bottom = bottom;
+ this.left = 0;
+ this.right = 0;
+ }
+
+ public SpacesItemDecoration(int left, int right) {
+ this.left = left;
+ this.right = right;
+ this.bottom = 0;
+ this.top = 0;
+ }
+
+ public SpacesItemDecoration(int left, int right, int bottom, int top) {
+ this.left = left;
+ this.right = right;
+ this.bottom = bottom;
+ this.top = top;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view,
+ RecyclerView parent, RecyclerView.State state) {
+ outRect.left = left;
+ outRect.right = right;
+ outRect.bottom = bottom;
+ outRect.top = top;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/uiui/videoplayer/base/ViewPagerLayoutManager.java b/app/src/main/java/com/uiui/videoplayer/base/ViewPagerLayoutManager.java
new file mode 100644
index 0000000..9ef8d3c
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/base/ViewPagerLayoutManager.java
@@ -0,0 +1,132 @@
+package com.uiui.videoplayer.base;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.PagerSnapHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.uiui.videoplayer.listener.OnViewPagerListener;
+
+/**
+ * Created by 钉某人
+ * github: https://github.com/DingMouRen
+ * email: naildingmouren@gmail.com
+ */
+
+public class ViewPagerLayoutManager extends LinearLayoutManager {
+ private static final String TAG = "ViewPagerLayoutManager";
+ private PagerSnapHelper mPagerSnapHelper;
+ private OnViewPagerListener mOnViewPagerListener;
+ private RecyclerView mRecyclerView;
+ private int mDrift;//位移,用来判断移动方向
+ private RecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener = new RecyclerView.OnChildAttachStateChangeListener() {
+ @Override
+ public void onChildViewAttachedToWindow(View view) {
+ if (mOnViewPagerListener != null && getChildCount() == 1) {
+ mOnViewPagerListener.onInitComplete();
+ }
+ }
+
+ @Override
+ public void onChildViewDetachedFromWindow(View view) {
+ if (mDrift >= 0) {
+ if (mOnViewPagerListener != null)
+ mOnViewPagerListener.onPageRelease(true, getPosition(view));
+ } else {
+ if (mOnViewPagerListener != null)
+ mOnViewPagerListener.onPageRelease(false, getPosition(view));
+ }
+
+ }
+ };
+
+ public ViewPagerLayoutManager(Context context, int orientation) {
+ super(context, orientation, false);
+ init();
+ }
+
+ public ViewPagerLayoutManager(Context context, int orientation, boolean reverseLayout) {
+ super(context, orientation, reverseLayout);
+ init();
+ }
+
+ private void init() {
+ mPagerSnapHelper = new PagerSnapHelper();
+
+ }
+
+ @Override
+ public void onAttachedToWindow(RecyclerView view) {
+ super.onAttachedToWindow(view);
+ mPagerSnapHelper.attachToRecyclerView(view);
+ this.mRecyclerView = view;
+ mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);
+ }
+
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ super.onLayoutChildren(recycler, state);
+ }
+
+ /**
+ * 滑动状态的改变
+ * 缓慢拖拽-> SCROLL_STATE_DRAGGING
+ * 快速滚动-> SCROLL_STATE_SETTLING
+ * 空闲状态-> SCROLL_STATE_IDLE
+ *
+ * @param state
+ */
+ @Override
+ public void onScrollStateChanged(int state) {
+ switch (state) {
+ case RecyclerView.SCROLL_STATE_IDLE:
+ View viewIdle = mPagerSnapHelper.findSnapView(this);
+ if (viewIdle != null) {
+ int positionIdle = getPosition(viewIdle);
+ if (mOnViewPagerListener != null && getChildCount() == 1) {
+ mOnViewPagerListener.onPageSelected(positionIdle, positionIdle == getItemCount() - 1);
+ }
+ }
+ break;
+ }
+ }
+
+ /**
+ * 监听竖直方向的相对偏移量
+ *
+ * @param dy
+ * @param recycler
+ * @param state
+ * @return
+ */
+ @Override
+ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
+ this.mDrift = dy;
+ return super.scrollVerticallyBy(dy, recycler, state);
+ }
+
+ /**
+ * 监听水平方向的相对偏移量
+ *
+ * @param dx
+ * @param recycler
+ * @param state
+ * @return
+ */
+ @Override
+ public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
+ this.mDrift = dx;
+ return super.scrollHorizontallyBy(dx, recycler, state);
+ }
+
+ /**
+ * 设置监听
+ *
+ * @param listener
+ */
+ public void setOnViewPagerListener(OnViewPagerListener listener) {
+ this.mOnViewPagerListener = listener;
+ }
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/listener/OnViewPagerListener.java b/app/src/main/java/com/uiui/videoplayer/listener/OnViewPagerListener.java
new file mode 100644
index 0000000..d0a5823
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/listener/OnViewPagerListener.java
@@ -0,0 +1,22 @@
+package com.uiui.videoplayer.listener;
+
+/**
+ * Created by 钉某人
+ * github: https://github.com/DingMouRen
+ * email: naildingmouren@gmail.com
+ * 用于ViewPagerLayoutManager的监听
+ */
+
+public interface OnViewPagerListener {
+
+ /*初始化完成*/
+ void onInitComplete();
+
+ /*释放的监听*/
+ void onPageRelease(boolean isNext, int position);
+
+ /*选中的监听以及判断是否滑动到底部*/
+ void onPageSelected(int position, boolean isBottom);
+
+
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/utils/FileUtils.java b/app/src/main/java/com/uiui/videoplayer/utils/FileUtils.java
new file mode 100644
index 0000000..52f345f
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/utils/FileUtils.java
@@ -0,0 +1,14 @@
+package com.uiui.videoplayer.utils;
+
+import android.os.AsyncTask;
+
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileUtils {
+
+
+
+}
diff --git a/app/src/main/java/com/uiui/videoplayer/utils/ToastUtil.java b/app/src/main/java/com/uiui/videoplayer/utils/ToastUtil.java
new file mode 100644
index 0000000..a3d4853
--- /dev/null
+++ b/app/src/main/java/com/uiui/videoplayer/utils/ToastUtil.java
@@ -0,0 +1,101 @@
+package com.uiui.videoplayer.utils;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.uiui.videoplayer.BuildConfig;
+
+
+/**
+ * Created by haoge on 2017/3/2.
+ */
+
+public class ToastUtil {
+ private static Handler mainHandler = new Handler(Looper.getMainLooper());
+ private static Toast toast;
+ private static Context mContext;
+
+ @SuppressLint("ShowToast")
+ public static void init(Context context) {
+ mContext = context;
+ toast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT);
+ debugToast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT);
+
+ }
+
+ private static long time1 = 0L;
+ private static long time2 = 0L;
+
+ public static void show(final String msg) {
+ mainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ time2 = System.currentTimeMillis();
+ if ((time2 - time1) > 3499) {
+ showToast(mContext, msg, Toast.LENGTH_LONG);
+ Log.e("fht", "LENGTH_LONG");
+ time1 = time2;
+ }
+ } else {
+ if (toast != null) {
+ toast.setText(msg);
+ toast.show();
+ }
+ }
+ }
+ });
+
+ }
+
+ private static Toast mToast = null;
+
+ //android 8.0以后限制
+ //https://www.jianshu.com/p/d9813ad03d59
+ //https://www.jianshu.com/p/050ce052b873
+ public static void showToast(Context context, String text, int duration) {
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
+ Toast.makeText(context, text, duration).show();
+ } else {
+ if (mToast == null) {
+ mToast = Toast.makeText(context, text, duration);
+ } else {
+ mToast.setText(text);
+ mToast.setDuration(duration);
+ }
+ mToast.show();
+ }
+ }
+
+ // public static void showInCenter(String msg) {
+// mainHandler.post(() -> {
+// if (toast != null) {
+// toast.setGravity(Gravity.CENTER, 0, 0);
+// toast.setText(msg);
+// toast.show();
+// }
+// });
+// }
+ static Handler debugHandler = new Handler(Looper.getMainLooper());
+ static Toast debugToast;
+
+ public static void debugShow(final String msg) {
+ mainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (BuildConfig.DEBUG) {
+ if (toast != null) {
+ toast.setText(msg);
+ toast.show();
+ }
+ }
+ }
+ });
+ }
+
+}
diff --git a/app/src/main/res/drawable-hdpi/tiktok_comment_normal.png b/app/src/main/res/drawable-hdpi/tiktok_comment_normal.png
new file mode 100644
index 0000000..0f164a7
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/tiktok_comment_normal.png differ
diff --git a/app/src/main/res/drawable-hdpi/tiktok_douyin_icon.png b/app/src/main/res/drawable-hdpi/tiktok_douyin_icon.png
new file mode 100644
index 0000000..b2ed547
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/tiktok_douyin_icon.png differ
diff --git a/app/src/main/res/drawable-hdpi/tiktok_music_play.png b/app/src/main/res/drawable-hdpi/tiktok_music_play.png
new file mode 100644
index 0000000..b9105b6
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/tiktok_music_play.png differ
diff --git a/app/src/main/res/drawable-hdpi/tiktok_play_tiktok.png b/app/src/main/res/drawable-hdpi/tiktok_play_tiktok.png
new file mode 100644
index 0000000..707aea8
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/tiktok_play_tiktok.png differ
diff --git a/app/src/main/res/drawable-hdpi/tiktok_star_normal.png b/app/src/main/res/drawable-hdpi/tiktok_star_normal.png
new file mode 100644
index 0000000..88872ed
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/tiktok_star_normal.png differ
diff --git a/app/src/main/res/drawable-hdpi/tiktok_star_selected.png b/app/src/main/res/drawable-hdpi/tiktok_star_selected.png
new file mode 100644
index 0000000..d4f6b5f
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/tiktok_star_selected.png differ
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_border_white.xml b/app/src/main/res/drawable/ic_border_white.xml
new file mode 100644
index 0000000..df48158
--- /dev/null
+++ b/app/src/main/res/drawable/ic_border_white.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_circle_red.xml b/app/src/main/res/drawable/ic_circle_red.xml
new file mode 100644
index 0000000..4b82773
--- /dev/null
+++ b/app/src/main/res/drawable/ic_circle_red.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_star_selector.xml b/app/src/main/res/drawable/ic_star_selector.xml
new file mode 100644
index 0000000..dd84df8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_star_selector.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..4e82778
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_play.xml b/app/src/main/res/layout/activity_play.xml
new file mode 100644
index 0000000..6c61946
--- /dev/null
+++ b/app/src/main/res/layout/activity_play.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_tiktok.xml b/app/src/main/res/layout/activity_tiktok.xml
new file mode 100644
index 0000000..3fb6c33
--- /dev/null
+++ b/app/src/main/res/layout/activity_tiktok.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_tiktok.xml b/app/src/main/res/layout/item_tiktok.xml
new file mode 100644
index 0000000..9bbebce
--- /dev/null
+++ b/app/src/main/res/layout/item_tiktok.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_videofile.xml b/app/src/main/res/layout/item_videofile.xml
new file mode 100644
index 0000000..605d3bc
--- /dev/null
+++ b/app/src/main/res/layout/item_videofile.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/tiktok_add_star.png b/app/src/main/res/mipmap-xhdpi/tiktok_add_star.png
new file mode 100644
index 0000000..bb3f53a
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/tiktok_add_star.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/tiktok_bg_add.png b/app/src/main/res/mipmap-xhdpi/tiktok_bg_add.png
new file mode 100644
index 0000000..275bc9e
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/tiktok_bg_add.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/tiktok_btn_share.png b/app/src/main/res/mipmap-xhdpi/tiktok_btn_share.png
new file mode 100644
index 0000000..5641f76
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/tiktok_btn_share.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..379e3ae
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,9 @@
+
+
+ #6200EE
+ #3700B3
+ #03DAC5
+ #FFFFFF
+ #000000
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..14c986b
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,24 @@
+
+ 0dp
+ 1dp
+ 5dp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..441d04d
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ 视频播放器
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..0d38722
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,31 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext.kotlin_version = '1.4.21'
+
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.6.3'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ maven { url "https://www.jitpack.io" }
+ maven { url "https://maven.aliyun.com/repository/public" } //jcenter & central
+ maven { url "https://maven.aliyun.com/repository/google" }
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..199d16e
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..5ad4355
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Feb 24 10:40:26 CST 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..ee86dfd
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name='视频播放器'
+include ':app'