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'