diff --git a/NiuDroidPlayer/.gitignore b/NiuDroidPlayer/.gitignore new file mode 100644 index 0000000..c6cbe56 --- /dev/null +++ b/NiuDroidPlayer/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/NiuDroidPlayer/app/.gitignore b/NiuDroidPlayer/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/NiuDroidPlayer/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/NiuDroidPlayer/app/build.gradle b/NiuDroidPlayer/app/build.gradle new file mode 100644 index 0000000..dc3ebdf --- /dev/null +++ b/NiuDroidPlayer/app/build.gradle @@ -0,0 +1,44 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 27 + buildToolsVersion '27.0.3' + + defaultConfig { + applicationId "com.qiniu.droid.niuplayer" + minSdkVersion 14 + targetSdkVersion 27 + versionCode 4 + versionName "2.1.3" + buildConfigField "long", "BUILD_TIMESTAMP", System.currentTimeMillis() + "L" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + sourceSets { + main { + jniLibs.srcDirs = ['../../releases/openssl'] + jniLibs.srcDirs += ['../../releases/qplayer'] + } + } + + lintOptions { + checkReleaseBuilds false + } +} + +dependencies { + implementation files('../../releases/pldroid-player-2.2.2.jar') + implementation 'com.android.support:appcompat-v7:27.0.2' + implementation 'com.android.support:design:27.0.2' + implementation 'com.android.support:recyclerview-v7:27.0.2' + implementation 'com.journeyapps:zxing-android-embedded:3.0.2@aar' + implementation 'com.google.zxing:core:3.2.0' + implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' + implementation 'com.squareup.okhttp3:okhttp:3.9.1' + implementation 'com.bugsnag:bugsnag-android-ndk:1.+' +} diff --git a/NiuDroidPlayer/app/proguard-rules.pro b/NiuDroidPlayer/app/proguard-rules.pro new file mode 100644 index 0000000..b332720 --- /dev/null +++ b/NiuDroidPlayer/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/lujun/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 *; +#} diff --git a/NiuDroidPlayer/app/release/output.json b/NiuDroidPlayer/app/release/output.json new file mode 100644 index 0000000..d1c5abc --- /dev/null +++ b/NiuDroidPlayer/app/release/output.json @@ -0,0 +1 @@ +[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1},"path":"app-release.apk","properties":{"packageId":"com.qiniu.droid.niuplayer","split":"","minSdkVersion":"14"}}] \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/AndroidManifest.xml b/NiuDroidPlayer/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..06092bb --- /dev/null +++ b/NiuDroidPlayer/app/src/main/AndroidManifest.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/MainActivity.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/MainActivity.java new file mode 100644 index 0000000..0602d01 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/MainActivity.java @@ -0,0 +1,278 @@ +package com.qiniu.droid.niuplayer; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.Toast; + +import com.bugsnag.android.Bugsnag; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; +import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.utils.StorageUtils; +import com.pili.pldroid.player.AVOptions; +import com.qiniu.droid.niuplayer.fragment.FragmentLifecycle; +import com.qiniu.droid.niuplayer.fragment.LiveVideoListFragment; +import com.qiniu.droid.niuplayer.fragment.MovieListFragment; +import com.qiniu.droid.niuplayer.fragment.ShortVideoListFragment; +import com.qiniu.droid.niuplayer.model.ModelFactory; +import com.qiniu.droid.niuplayer.model.UpgradeInfo; +import com.qiniu.droid.niuplayer.utils.PermissionChecker; +import com.qiniu.droid.niuplayer.widget.CommomDialog; +import com.qiniu.droid.niuplayer.widget.ScrollEnableViewPager; +import com.qiniu.droid.niuplayer.widget.UpgradeDialog; + +import java.io.File; + +import static com.qiniu.droid.niuplayer.ScanActivity.SCAN_URL_OK; +import static com.qiniu.droid.niuplayer.model.ModelFactory.createUpgradeInfoByURL; +import static com.qiniu.droid.niuplayer.utils.Config.DEFAULT_CACHE_DIR_NAME; +import static com.qiniu.droid.niuplayer.utils.Config.UPGRADE_URL_PREFIX; +import static com.qiniu.droid.niuplayer.utils.Utils.getAppProcessName; +import static com.qiniu.droid.niuplayer.utils.Utils.getAppVersionCode; +import static com.qiniu.droid.niuplayer.utils.Utils.wrapTabIndicatorToTitle; + + +public class MainActivity extends AppCompatActivity { + private static String[] TAB_TITLE = {"短视频"}; + + private TabLayout mTabLayout; + private FragmentViewPageAdapter mViewPageAdapter; + private ScrollEnableViewPager mViewPager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING); + setContentView(com.qiniu.droid.niuplayer.R.layout.activity_main); + Bugsnag.init(this); + isStoragePermissionOK(); + initImageLoader(); + initView(); + checkUpgrade(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK && requestCode == IntentIntegrator.REQUEST_CODE) { + + IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); + if (result != null) { + if (result.getContents() == null) { + Toast.makeText(this, "无法识别二维码", Toast.LENGTH_LONG).show(); + } else { + jumpToVideoTextureActivity(result.getContents()); + } + } + } else if (resultCode == SCAN_URL_OK && requestCode == IntentIntegrator.REQUEST_CODE) { + String path = data.getStringExtra("url"); + if (path != null && !path.isEmpty()) { + jumpToVideoTextureActivity(path); + } + } + } + + public void showPathDialog() { + new CommomDialog(this, R.style.dialog, "添加播放地址", new CommomDialog.OnCloseListener() { + @Override + public void onClick(Dialog dialog, boolean confirm) { + if (confirm) { + String path = ((CommomDialog) dialog).getEditString(); + jumpToVideoTextureActivity(path); + } + } + }).setTitle("添加播放地址").setPositiveButton("播放").show(); + } + + public void setTabViewVisible(boolean isVisible) { + mTabLayout.setVisibility(isVisible ? View.VISIBLE : View.GONE); + mViewPager.setScrollEnable(isVisible); + } + + public boolean isCameraPermissionOK() { + PermissionChecker checker = new PermissionChecker(this); + boolean isPermissionOK = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || checker.isCameraPermissionOK(); + if (!isPermissionOK) { + Toast.makeText(this, "Camera permissions is necessary !!!", Toast.LENGTH_SHORT).show(); + } + return isPermissionOK; + } + + public boolean isStoragePermissionOK() { + PermissionChecker checker = new PermissionChecker(this); + boolean isPermissionOK = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || checker.isStoragePermissionOK(); + if (!isPermissionOK) { + Toast.makeText(this, "Storage permissions is necessary !!!", Toast.LENGTH_SHORT).show(); + } + return isPermissionOK; + } + + public void scanQrcode() { + if (isCameraPermissionOK()) { + IntentIntegrator integrator = new IntentIntegrator(this); + integrator.setCaptureActivity(ScanActivity.class); + integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES); + integrator.setOrientationLocked(true); + integrator.setCameraId(0); + integrator.setBeepEnabled(true); + integrator.initiateScan(); + } + } + + @Override + public void onResume() { + super.onResume(); + if (mViewPageAdapter != null && mViewPageAdapter.getCurrentFragment() != null) { + mViewPageAdapter.getCurrentFragment().onActivityResume(); + } + } + + @Override + public void onPause() { + super.onPause(); + if (mViewPageAdapter != null && mViewPageAdapter.getCurrentFragment() != null) { + mViewPageAdapter.getCurrentFragment().onActivityPause(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mViewPageAdapter != null && mViewPageAdapter.getCurrentFragment() != null) { + mViewPageAdapter.getCurrentFragment().onActivityDestroy(); + } + } + + @Override + public void onBackPressed() { + if (mViewPageAdapter != null) { + mViewPageAdapter.getCurrentFragment().onBackPressed(); + } else { + super.onBackPressed(); + } + } + + private void initImageLoader() { + File cacheDir = StorageUtils.getOwnCacheDirectory(this, DEFAULT_CACHE_DIR_NAME);//sdcard目录 + ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this) + .diskCache(new UnlimitedDiskCache(cacheDir)) // default 可以自定义缓存路径 + .diskCacheSize(50 * 1024 * 1024) + .diskCacheFileCount(100) + .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) + .memoryCacheSize(2 * 1024 * 1024) + .memoryCacheSizePercentage(13) + .build(); + ImageLoader.getInstance().init(config); + } + + private void initView() { + mViewPager = findViewById(com.qiniu.droid.niuplayer.R.id.view_pager); + mViewPager.setOffscreenPageLimit(2); + mViewPageAdapter = new FragmentViewPageAdapter(getSupportFragmentManager()); + mViewPager.setAdapter(mViewPageAdapter); + + mTabLayout = findViewById(com.qiniu.droid.niuplayer.R.id.tab_layout); + mTabLayout.setupWithViewPager(mViewPager); + + wrapTabIndicatorToTitle(mTabLayout, 70, 70); + mViewPager.setCurrentItem(0); + } + + private void jumpToVideoTextureActivity(String path) { + Intent intent = new Intent(this, PLVideoTextureActivity.class); + intent.putExtra("videoPath", path); + intent.putExtra("mediaCodec", AVOptions.MEDIA_CODEC_AUTO); + intent.putExtra("liveStreaming", 1); + this.startActivity(intent); + } + + private void checkUpgrade() { + String pageName = getAppProcessName(this); + String url = UPGRADE_URL_PREFIX + pageName; + createUpgradeInfoByURL(url, new ModelFactory.OnResultListener() { + @Override + public void onSuccess(int statusCode, Object data) { + final UpgradeInfo upgradeInfo = (UpgradeInfo) data; + String appVersion = getAppVersionCode(MainActivity.this) + ""; + if (upgradeInfo.getVersion().compareTo(appVersion) > 0) { + runOnUiThread(new Runnable() { + @Override + public void run() { + UpgradeDialog.show(MainActivity.this, upgradeInfo.getDescription(), upgradeInfo.getDownloadURL()); + } + }); + } + } + + @Override + public void onFailure() { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(MainActivity.this, "更新失败", Toast.LENGTH_LONG).show(); + } + }); + } + }); + } + + private class FragmentViewPageAdapter extends FragmentStatePagerAdapter { + private FragmentLifecycle mCurrentFragment; + + public FragmentViewPageAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public CharSequence getPageTitle(int position) { + return TAB_TITLE[position]; + } + + @Override + public Fragment getItem(int position) { + switch (position) { + case 0: + return new ShortVideoListFragment(); + case 1: + return new MovieListFragment(); + default: + return new LiveVideoListFragment(); + } + } + + @Override + public int getCount() { + return TAB_TITLE.length; + } + + @Override + public void setPrimaryItem(ViewGroup container, int position, Object object) { + if (getCurrentFragment() != object) { + if (mCurrentFragment != null) { + mCurrentFragment.onFragmentPause(); + } + mCurrentFragment = ((FragmentLifecycle) object); + mCurrentFragment.onFragmentResume(); + } + super.setPrimaryItem(container, position, object); + } + + public FragmentLifecycle getCurrentFragment() { + return mCurrentFragment; + } + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/PLVideoTextureActivity.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/PLVideoTextureActivity.java new file mode 100644 index 0000000..2fe8d19 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/PLVideoTextureActivity.java @@ -0,0 +1,296 @@ +package com.qiniu.droid.niuplayer; + +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.ImageButton; +import android.widget.TextView; + +import com.qiniu.droid.niuplayer.utils.Config; +import com.pili.pldroid.player.AVOptions; +import com.pili.pldroid.player.PLOnBufferingUpdateListener; +import com.pili.pldroid.player.PLOnCompletionListener; +import com.pili.pldroid.player.PLOnErrorListener; +import com.pili.pldroid.player.PLOnInfoListener; +import com.pili.pldroid.player.PLOnVideoSizeChangedListener; +import com.pili.pldroid.player.widget.PLVideoTextureView; +import com.qiniu.droid.niuplayer.widget.MediaController; + +/** + * This is a demo activity of PLVideoTextureView + */ +public class PLVideoTextureActivity extends VideoPlayerBaseActivity { + + private static final String TAG = PLVideoTextureActivity.class.getSimpleName(); + + private PLVideoTextureView mVideoView; + private int mRotation = 0; + private int mDisplayAspectRatio = PLVideoTextureView.ASPECT_RATIO_FIT_PARENT; //default + private TextView mStatInfoTextView; + private boolean mIsLiveStreaming; + + String videoPath; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_pl_video_texture); + + videoPath = getIntent().getStringExtra("videoPath"); + mIsLiveStreaming = getIntent().getIntExtra("liveStreaming", 1) == 1; + + mVideoView = findViewById(R.id.VideoView); + + View loadingView = findViewById(R.id.loading_view); + mVideoView.setBufferingIndicator(loadingView); + + View coverView = findViewById(R.id.cover_view); + mVideoView.setCoverView(coverView); + + mStatInfoTextView = findViewById(R.id.StatInfoTextView); + + // If you want to fix display orientation such as landscape, you can use the code show as follow + // + // if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + // mVideoView.setPreviewOrientation(0); + // } + // else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + // mVideoView.setPreviewOrientation(270); + // } + + final ImageButton playImageBtn = findViewById(R.id.play_image_btn); + + View topView = findViewById(R.id.top_view); + topView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mVideoView.isPlaying()) { + mVideoView.pause(); + playImageBtn.setVisibility(View.VISIBLE); + } else { + mVideoView.start(); + playImageBtn.setVisibility(View.GONE); + } + } + }); + + + // 1 -> hw codec enable, 0 -> disable [recommended] + int codec = getIntent().getIntExtra("mediaCodec", AVOptions.MEDIA_CODEC_SW_DECODE); + AVOptions options = new AVOptions(); + // the unit of timeout is ms + options.setInteger(AVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000); + options.setInteger(AVOptions.KEY_LIVE_STREAMING, mIsLiveStreaming ? 1 : 0); + // 1 -> hw codec enable, 0 -> disable [recommended] + options.setInteger(AVOptions.KEY_MEDIACODEC, codec); + boolean disableLog = getIntent().getBooleanExtra("disable-log", false); + options.setInteger(AVOptions.KEY_LOG_LEVEL, disableLog ? 5 : 0); + boolean cache = getIntent().getBooleanExtra("cache", false); + if (!mIsLiveStreaming && cache) { + options.setString(AVOptions.KEY_CACHE_DIR, Config.DEFAULT_CACHE_DIR_PATH); + } + mVideoView.setAVOptions(options); + + // You can mirror the display + // mVideoView.setMirror(true); + + // You can also use a custom `MediaController` widget + // MediaController mediaController = new MediaController(this, !mIsLiveStreaming, mIsLiveStreaming); + // mediaController.setOnClickSpeedAdjustListener(mOnClickSpeedAdjustListener); + // mVideoView.setMediaController(mediaController); + + mVideoView.setOnInfoListener(mOnInfoListener); + mVideoView.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener); + mVideoView.setOnBufferingUpdateListener(mOnBufferingUpdateListener); + mVideoView.setOnCompletionListener(mOnCompletionListener); + mVideoView.setOnErrorListener(mOnErrorListener); + //mVideoView.setOnPreparedListener(mOnPreparedListener); + //mVideoView.setOnSeekCompleteListener(mOnSeekCompleteListener); + + mVideoView.setLooping(getIntent().getBooleanExtra("loop", false)); + + mVideoView.setVideoPath(videoPath); + mVideoView.start(); + } + + @Override + protected void onPause() { + super.onPause(); + mVideoView.pause(); + } + + @Override + protected void onResume() { + super.onResume(); + mVideoView.start(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mVideoView.stopPlayback(); + } + + public void onClickRotate(View v) { + mRotation = (mRotation + 90) % 360; + mVideoView.setDisplayOrientation(mRotation); + } + + public void onClose(View v) { + finish(); + } + + public void onClickSwitchScreen(View v) { + mDisplayAspectRatio = (mDisplayAspectRatio + 1) % 5; + mVideoView.setDisplayAspectRatio(mDisplayAspectRatio); + switch (mVideoView.getDisplayAspectRatio()) { + case PLVideoTextureView.ASPECT_RATIO_ORIGIN: + showLogTips("Origin mode"); + break; + case PLVideoTextureView.ASPECT_RATIO_FIT_PARENT: + showLogTips("Fit parent !"); + break; + case PLVideoTextureView.ASPECT_RATIO_PAVED_PARENT: + showLogTips("Paved parent !"); + break; + case PLVideoTextureView.ASPECT_RATIO_16_9: + showLogTips("16 : 9 !"); + break; + case PLVideoTextureView.ASPECT_RATIO_4_3: + showLogTips("4 : 3 !"); + break; + default: + break; + } + } + + private PLOnInfoListener mOnInfoListener = new PLOnInfoListener() { + @Override + public void onInfo(int what, int extra) { + Log.i(TAG, "OnInfo, what = " + what + ", extra = " + extra); + switch (what) { + case PLOnInfoListener.MEDIA_INFO_BUFFERING_START: + break; + case PLOnInfoListener.MEDIA_INFO_BUFFERING_END: + break; + case PLOnInfoListener.MEDIA_INFO_VIDEO_RENDERING_START: + showLogTips("First video render time: " + extra + "ms"); + break; + case PLOnInfoListener.MEDIA_INFO_AUDIO_RENDERING_START: + Log.i(TAG, "First audio render time: " + extra + "ms"); + break; + case PLOnInfoListener.MEDIA_INFO_VIDEO_FRAME_RENDERING: + Log.i(TAG, "video frame rendering, ts = " + extra); + break; + case PLOnInfoListener.MEDIA_INFO_AUDIO_FRAME_RENDERING: + Log.i(TAG, "audio frame rendering, ts = " + extra); + break; + case PLOnInfoListener.MEDIA_INFO_VIDEO_GOP_TIME: + Log.i(TAG, "Gop Time: " + extra); + break; + case PLOnInfoListener.MEDIA_INFO_SWITCHING_SW_DECODE: + Log.i(TAG, "Hardware decoding failure, switching software decoding!"); + break; + case PLOnInfoListener.MEDIA_INFO_METADATA: + Log.i(TAG, mVideoView.getMetadata().toString()); + break; + case PLOnInfoListener.MEDIA_INFO_VIDEO_BITRATE: + case PLOnInfoListener.MEDIA_INFO_VIDEO_FPS: + updateStatInfo(); + break; + case PLOnInfoListener.MEDIA_INFO_CONNECTED: + Log.i(TAG, "Connected !"); + break; + case PLOnInfoListener.MEDIA_INFO_VIDEO_ROTATION_CHANGED: + Log.i(TAG, "Rotation changed: " + extra); + break; + default: + break; + } + } + }; + + private PLOnErrorListener mOnErrorListener = new PLOnErrorListener() { + @Override + public boolean onError(int errorCode) { + Log.e(TAG, "Error happened, errorCode = " + errorCode); + switch (errorCode) { + case PLOnErrorListener.ERROR_CODE_IO_ERROR: + /** + * SDK will do reconnecting automatically + */ + showLogTips("IO Error !"); + return false; + case PLOnErrorListener.ERROR_CODE_OPEN_FAILED: + showLogTips("failed to open player !"); + break; + case PLOnErrorListener.ERROR_CODE_SEEK_FAILED: + showLogTips("failed to seek !"); + break; + default: + showLogTips("unknown error !"); + break; + } + finish(); + return true; + } + }; + + private PLOnCompletionListener mOnCompletionListener = new PLOnCompletionListener() { + @Override + public void onCompletion() { + Log.i(TAG, "Play Completed !"); + showLogTips("Play Completed !"); + finish(); + } + }; + + private PLOnBufferingUpdateListener mOnBufferingUpdateListener = new PLOnBufferingUpdateListener() { + @Override + public void onBufferingUpdate(int precent) { + Log.i(TAG, "onBufferingUpdate: " + precent); + } + }; + + private PLOnVideoSizeChangedListener mOnVideoSizeChangedListener = new PLOnVideoSizeChangedListener() { + @Override + public void onVideoSizeChanged(int width, int height) { + Log.i(TAG, "onVideoSizeChanged: width = " + width + ", height = " + height); + } + }; + + private MediaController.OnClickSpeedAdjustListener mOnClickSpeedAdjustListener = new MediaController.OnClickSpeedAdjustListener() { + @Override + public void onClickNormal() { + // 0x0001/0x0001 = 2 + mVideoView.setPlaySpeed(0X00010001); + } + + @Override + public void onClickFaster() { + // 0x0002/0x0001 = 2 + mVideoView.setPlaySpeed(0X00020001); + } + + @Override + public void onClickSlower() { + // 0x0001/0x0002 = 0.5 + mVideoView.setPlaySpeed(0X00010002); + } + }; + + private void showLogTips(final String tips) { + Log.i(TAG, tips); + } + + private void updateStatInfo() { + long bitrate = mVideoView.getVideoBitrate() / 1024; + final String stat = bitrate + "kbps, " + mVideoView.getVideoFps() + "fps"; + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatInfoTextView.setText(stat); + } + }); + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/ScanActivity.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/ScanActivity.java new file mode 100644 index 0000000..a82a965 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/ScanActivity.java @@ -0,0 +1,94 @@ +package com.qiniu.droid.niuplayer; + +import android.app.Activity; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.KeyEvent; +import android.view.View; +import com.journeyapps.barcodescanner.CaptureManager; +import com.journeyapps.barcodescanner.CompoundBarcodeView; +import com.qiniu.droid.niuplayer.utils.GetPathFromUri; + + +public class ScanActivity extends AppCompatActivity { + private CaptureManager mCapture; + private CompoundBarcodeView mBarcodeScannerView; + public static int SCAN_URL_OK = 100; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_scan); + mBarcodeScannerView = (CompoundBarcodeView) findViewById(R.id.zxing_barcode_scanner); + + mCapture = new CaptureManager(this, mBarcodeScannerView); + mCapture.initializeFromIntent(getIntent(), savedInstanceState); + mCapture.decode(); + } + + @Override + protected void onResume() { + super.onResume(); + mCapture.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mCapture.onPause(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mCapture.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mCapture.onSaveInstanceState(outState); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return mBarcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + } + + public void onClickBack(View view) { + finish(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode != Activity.RESULT_OK) { + return; + } + if (requestCode == 0) { + String selectedFilepath = GetPathFromUri.getPath(this, data.getData()); + if (selectedFilepath != null && !"".equals(selectedFilepath)) { + Intent intent = new Intent(); + intent.putExtra("url", selectedFilepath); + setResult(SCAN_URL_OK, intent); + finish(); + } + } + } + + public void onClickAlbum(View view) { + Intent intent = new Intent(); + if (Build.VERSION.SDK_INT < 19) { + intent.setAction(Intent.ACTION_GET_CONTENT); + intent.setType("video/*"); + } else { + intent.setAction(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("video/*"); + } + + this.startActivityForResult(Intent.createChooser(intent, "选择要导入的视频"), 0); + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/VideoPlayerBaseActivity.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/VideoPlayerBaseActivity.java new file mode 100644 index 0000000..cbd76a0 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/VideoPlayerBaseActivity.java @@ -0,0 +1,28 @@ +package com.qiniu.droid.niuplayer; + +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.WindowManager; + +/** + * Auto hide and show navigation bar and status bar for API >= 19. + * Keep screen on. + */ + +public class VideoPlayerBaseActivity extends AppCompatActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + View decorView = getWindow().getDecorView(); + decorView.setSystemUiVisibility( + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/FragmentLifecycle.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/FragmentLifecycle.java new file mode 100644 index 0000000..06d1807 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/FragmentLifecycle.java @@ -0,0 +1,11 @@ +package com.qiniu.droid.niuplayer.fragment; + + +public interface FragmentLifecycle { + void onFragmentPause(); + void onFragmentResume(); + void onBackPressed(); + void onActivityPause(); + void onActivityResume(); + void onActivityDestroy(); +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/LiveVideoListAdapter.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/LiveVideoListAdapter.java new file mode 100644 index 0000000..8c4a3f2 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/LiveVideoListAdapter.java @@ -0,0 +1,64 @@ +package com.qiniu.droid.niuplayer.fragment; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.pili.pldroid.player.AVOptions; +import com.qiniu.droid.niuplayer.PLVideoTextureActivity; +import com.qiniu.droid.niuplayer.R; + +import static com.qiniu.droid.niuplayer.utils.Config.LIVE_TEST_URL; + +public class LiveVideoListAdapter extends RecyclerView.Adapter { + + private Activity mActivity; + + public LiveVideoListAdapter(Activity activity) { + mActivity = activity; + } + + class ViewHolder extends RecyclerView.ViewHolder { + String mPath; + ImageView mCoverImage; + + public ViewHolder(View itemView) { + super(itemView); + mCoverImage = itemView.findViewById(R.id.cover_image); + mCoverImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(mActivity, PLVideoTextureActivity.class); + intent.putExtra("videoPath", mPath); + intent.putExtra("mediaCodec", AVOptions.MEDIA_CODEC_AUTO); + intent.putExtra("liveStreaming", 1); + mActivity.startActivity(intent); + } + }); + } + } + + @Override + public LiveVideoListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + Context context = parent.getContext(); + LayoutInflater inflater = LayoutInflater.from(context); + View contactView = inflater.inflate(R.layout.view_live_video, parent, false); + LiveVideoListAdapter.ViewHolder viewHolder = new LiveVideoListAdapter.ViewHolder(contactView); + return viewHolder; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + holder.mPath = LIVE_TEST_URL; + } + + @Override + public int getItemCount() { + return 1; + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/LiveVideoListFragment.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/LiveVideoListFragment.java new file mode 100644 index 0000000..2d96500 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/LiveVideoListFragment.java @@ -0,0 +1,72 @@ +package com.qiniu.droid.niuplayer.fragment; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; + +import com.qiniu.droid.niuplayer.MainActivity; +import com.qiniu.droid.niuplayer.R; + +public class LiveVideoListFragment extends Fragment implements FragmentLifecycle, View.OnClickListener { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.fragment_live_video_list, container, false); + + RecyclerView videoList = root.findViewById(R.id.video_list); + LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); + videoList.setLayoutManager(layoutManager); + videoList.setHasFixedSize(true); + LiveVideoListAdapter adapter = new LiveVideoListAdapter(getActivity()); + videoList.setAdapter(adapter); + + ImageButton imageButton = root.findViewById(R.id.path_image); + imageButton.setOnClickListener(this); + imageButton = root.findViewById(R.id.scan_image); + imageButton.setOnClickListener(this); + + return root; + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.path_image: + ((MainActivity) getActivity()).showPathDialog(); + break; + case R.id.scan_image: + ((MainActivity) getActivity()).scanQrcode(); + break; + } + } + + @Override + public void onFragmentPause() { + } + + @Override + public void onFragmentResume() { + } + + @Override + public void onActivityPause() { + } + + @Override + public void onActivityResume() { + } + + @Override + public void onActivityDestroy() { + } + + @Override + public void onBackPressed() { + getActivity().finish(); + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/MovieListAdapter.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/MovieListAdapter.java new file mode 100644 index 0000000..ae5940c --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/MovieListAdapter.java @@ -0,0 +1,192 @@ +package com.qiniu.droid.niuplayer.fragment; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.pili.pldroid.player.PLOnInfoListener; +import com.pili.pldroid.player.widget.PLVideoTextureView; +import com.qiniu.droid.niuplayer.R; +import com.qiniu.droid.niuplayer.model.VideoItem; +import com.qiniu.droid.niuplayer.widget.MediaController; + +import java.util.ArrayList; + +import static com.qiniu.droid.niuplayer.utils.Utils.createAVOptions; +import static com.qiniu.droid.niuplayer.widget.PlayConfigView.BACKSTAGE_PLAY_TAG; + +public class MovieListAdapter extends RecyclerView.Adapter { + + public interface OnFullScreenListener { + void onFullScreen(PLVideoTextureView videoView, MediaController mediaController); + } + + private MovieListAdapter.OnFullScreenListener mOnFullScreenListener; + private MovieListAdapter.ViewHolder mCurViewHolder; + private DisplayImageOptions mDisplayImageOptions; + private ArrayList mItemList; + + public MovieListAdapter(ArrayList arrayList) { + mItemList = arrayList; + + mDisplayImageOptions = new DisplayImageOptions.Builder() + .showImageOnLoading(R.drawable.defualt_bg) //加载图片时的图片 + .showImageForEmptyUri(R.drawable.defualt_bg) //没有图片资源时的默认图片 + .showImageOnFail(R.drawable.defualt_bg) //加载失败时的图片 + .cacheInMemory(true) //启用内存缓存 + .cacheOnDisk(true) //启用外存缓存 + .considerExifParams(true) //启用EXIF和JPEG图像格式 + .build(); + } + + public void setOnFullScreenListener(MovieListAdapter.OnFullScreenListener listener) { + mOnFullScreenListener = listener; + } + + class ViewHolder extends RecyclerView.ViewHolder { + PLVideoTextureView videoView; + String videoPath; + String coverPath; + ImageButton stopPlayImage; + ImageView coverImage; + TextView detailText; + TextView nameText; + View loadingView; + ImageButton fullScreenImage; + + public ViewHolder(View itemView) { + super(itemView); + + coverImage = itemView.findViewById(R.id.cover_image); + stopPlayImage = itemView.findViewById(R.id.cover_stop_play); + videoView = itemView.findViewById(R.id.video_texture_view); + detailText = itemView.findViewById(R.id.detail_text); + nameText = itemView.findViewById(R.id.name_text); + loadingView = itemView.findViewById(R.id.loading_view); + fullScreenImage = itemView.findViewById(R.id.full_screen_image); + final MediaController mediaController = itemView.findViewById(R.id.media_controller); + + videoView.setAVOptions(createAVOptions()); + videoView.setBufferingIndicator(loadingView); + videoView.setMediaController(mediaController); + videoView.setDisplayAspectRatio(PLVideoTextureView.ASPECT_RATIO_PAVED_PARENT); + videoView.setLooping(true); + videoView.setOnInfoListener(new PLOnInfoListener() { + @Override + public void onInfo(int i, int i1) { + if (i == PLOnInfoListener.MEDIA_INFO_VIDEO_RENDERING_START) { + coverImage.setVisibility(View.GONE); + stopPlayImage.setVisibility(View.GONE); + mediaController.hide(); + } + } + }); + + coverImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + stopCurVideoView(); + mCurViewHolder = ViewHolder.this; + startCurVideoView(); + } + }); + + fullScreenImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mOnFullScreenListener != null) { + mOnFullScreenListener.onFullScreen(ViewHolder.this.videoView, mediaController); + } + } + }); + } + } + + @Override + public MovieListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + Context context = parent.getContext(); + LayoutInflater inflater = LayoutInflater.from(context); + View contactView = inflater.inflate(R.layout.view_movie_video, parent, false); + MovieListAdapter.ViewHolder viewHolder = new MovieListAdapter.ViewHolder(contactView); + return viewHolder; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + VideoItem item = mItemList.get(position); + holder.videoPath = item.getVideoPath(); + holder.coverPath = item.getCoverPath(); + holder.detailText.setText(item.getTime()); + holder.nameText.setText(item.getName()); + ImageLoader.getInstance().displayImage(holder.coverPath, holder.coverImage, mDisplayImageOptions); + } + + @Override + public int getItemCount() { + return mItemList.size(); + } + + @Override + public void onViewDetachedFromWindow(MovieListAdapter.ViewHolder holder) { + holder.videoView.pause(); + holder.loadingView.setVisibility(View.GONE); + holder.coverImage.setVisibility(View.VISIBLE); + holder.stopPlayImage.setVisibility(View.VISIBLE); + } + + public void startCurVideoView() { + if (mCurViewHolder != null) { + mCurViewHolder.videoView.setVideoPath(mCurViewHolder.videoPath); + mCurViewHolder.videoView.start(); + mCurViewHolder.loadingView.setVisibility(View.VISIBLE); + mCurViewHolder.stopPlayImage.setVisibility(View.GONE); + } + } + + public void restartCurVideoView() { + if (mCurViewHolder != null) { + mCurViewHolder.videoView.start(); + mCurViewHolder.stopPlayImage.setVisibility(View.GONE); + } + } + + public boolean isCurVideoPlaying() { + return mCurViewHolder != null && mCurViewHolder.videoView.isPlaying(); + } + + public boolean needBackstagePlay() { + return mCurViewHolder != null && BACKSTAGE_PLAY_TAG.equals(mCurViewHolder.videoView.getTag()); + } + + public void pauseCurVideoView() { + if (mCurViewHolder != null) { + mCurViewHolder.videoView.pause(); + mCurViewHolder.loadingView.setVisibility(View.GONE); + } + } + + private void resetConfig() { + if (mCurViewHolder != null) { + mCurViewHolder.videoView.setRotation(0); + mCurViewHolder.videoView.setMirror(false); + mCurViewHolder.videoView.setDisplayAspectRatio(PLVideoTextureView.ASPECT_RATIO_PAVED_PARENT); + } + } + + public void stopCurVideoView() { + if (mCurViewHolder != null) { + resetConfig(); + mCurViewHolder.videoView.stopPlayback(); + mCurViewHolder.loadingView.setVisibility(View.GONE); + mCurViewHolder.coverImage.setVisibility(View.VISIBLE); + mCurViewHolder.stopPlayImage.setVisibility(View.VISIBLE); + } + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/MovieListFragment.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/MovieListFragment.java new file mode 100644 index 0000000..540ca6c --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/MovieListFragment.java @@ -0,0 +1,296 @@ +package com.qiniu.droid.niuplayer.fragment; + +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.Toast; + +import com.pili.pldroid.player.PLOnImageCapturedListener; +import com.pili.pldroid.player.widget.PLVideoTextureView; +import com.qiniu.droid.niuplayer.MainActivity; +import com.qiniu.droid.niuplayer.R; +import com.qiniu.droid.niuplayer.model.VideoItem; +import com.qiniu.droid.niuplayer.model.ModelFactory; +import com.qiniu.droid.niuplayer.utils.Config; +import com.qiniu.droid.niuplayer.widget.GestureControllerListener; +import com.qiniu.droid.niuplayer.widget.MediaController; +import com.qiniu.droid.niuplayer.widget.PlayConfigView; + +import java.util.ArrayList; + +import static com.qiniu.droid.niuplayer.utils.Config.MOVIE_PATH_PREFIX; +import static com.qiniu.droid.niuplayer.utils.Utils.savePhotoToSDCard; + +public class MovieListFragment extends Fragment implements FragmentLifecycle, View.OnClickListener, MovieListAdapter.OnFullScreenListener, View.OnTouchListener { + + private RecyclerView mVideoList; + private PLVideoTextureView mCurVideoView; + private FrameLayout mFullScreenGroup; + private ArrayList mItemList; + private MovieListAdapter mMovieListAdapter; + private ViewGroup mTitleBar; + private ViewGroup mCurViewHolder; + private PlayConfigView mPlayConfigView; + private MediaController mLandscapeMC; + private MediaController mPortraitMC; + private GestureDetector mGestureDetector; + private boolean mNeedRestart; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_movie_list, container, false); +// initMovieData(rootView); + return rootView; + } + + private void initView(View root) { + mVideoList = root.findViewById(R.id.video_list); + mTitleBar = root.findViewById(R.id.title_bar); + mFullScreenGroup = root.findViewById(R.id.full_screen_group); + mFullScreenGroup.setVisibility(View.GONE); + mLandscapeMC = root.findViewById(R.id.media_controller); + mPlayConfigView = root.findViewById(R.id.play_config_view); + + ImageButton imageButton = root.findViewById(R.id.more_image_btn); + imageButton.setOnClickListener(this); + imageButton = root.findViewById(R.id.path_image); + imageButton.setOnClickListener(this); + imageButton = root.findViewById(R.id.back_image_btn); + imageButton.setOnClickListener(this); + imageButton = root.findViewById(R.id.scan_image); + imageButton.setOnClickListener(this); + imageButton = root.findViewById(R.id.screen_short_image); + imageButton.setOnClickListener(this); + + mLandscapeMC.setOnTouchListener(this); + mPlayConfigView.setOnTouchListener(this); + + final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); + mVideoList.setLayoutManager(layoutManager); + mVideoList.setHasFixedSize(true); + mMovieListAdapter = new MovieListAdapter(mItemList); + mMovieListAdapter.setOnFullScreenListener(this); + mVideoList.setAdapter(mMovieListAdapter); + + mGestureDetector = new GestureDetector(new GestureControllerListener(getActivity())); + } + + private void initMovieData(final View rootView) { + ModelFactory.createVideoItemListByURL(MOVIE_PATH_PREFIX, new ModelFactory.OnResultListener() { + @Override + public void onSuccess(int statusCode, Object data) { + mItemList = (ArrayList) data; + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + initView(rootView); + } + }); + } + + @Override + public void onFailure() { + getMainActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(getActivity(), "获得长视频列表失败", Toast.LENGTH_LONG).show(); + } + }); + } + }); + } + + private void onPortraitChanged() { + if (mCurVideoView == null) { + return; + } + mFullScreenGroup.setVisibility(View.GONE); + mFullScreenGroup.removeAllViews(); + + mVideoList.setVisibility(View.VISIBLE); + mTitleBar.setVisibility(View.VISIBLE); + + mCurVideoView.setDisplayAspectRatio(PLVideoTextureView.ASPECT_RATIO_PAVED_PARENT); + mCurViewHolder.addView(mCurVideoView, -1); + mCurVideoView.setMediaController(mPortraitMC); + mPortraitMC.setAnchorView(mCurVideoView); + + getMainActivity().setTabViewVisible(true); + } + + private void onLandscapeChanged() { + if (mCurVideoView == null) { + return; + } + ViewGroup viewGroup = (ViewGroup) mCurVideoView.getParent(); + viewGroup.removeAllViews(); + mVideoList.setVisibility(View.GONE); + + mCurViewHolder = viewGroup; + + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + layoutParams.gravity = Gravity.CENTER; + mFullScreenGroup.addView(mCurVideoView, layoutParams); + mFullScreenGroup.setVisibility(View.VISIBLE); + mCurVideoView.setDisplayAspectRatio(PLVideoTextureView.ASPECT_RATIO_PAVED_PARENT); + mTitleBar.setVisibility(View.GONE); + getMainActivity().setTabViewVisible(false); + + mCurVideoView.setMediaController(mLandscapeMC); + mLandscapeMC.setOnShownListener(new MediaController.OnShownListener() { + @Override + public void onShown() { + if (mPlayConfigView.getVisibility() == View.VISIBLE) { + mPlayConfigView.setVisibility(View.GONE); + } + } + }); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + onPortraitChanged(); + } else { + onLandscapeChanged(); + } + } + + private void captureImage() { + if (!getMainActivity().isStoragePermissionOK()) { + return; + } + mCurVideoView.setOnImageCapturedListener(new PLOnImageCapturedListener() { + @Override + public void onImageCaptured(byte[] data) { + Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length); + if (savePhotoToSDCard(bmp, Config.DEFAULT_CACHE_DIR_PATH, Long.toString(System.currentTimeMillis()))) { + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(getContext().getApplicationContext(), "屏幕截取成功,已保存在 :" + Config.DEFAULT_CACHE_DIR_PATH, Toast.LENGTH_LONG).show(); + } + }); + } + } + }); + mCurVideoView.captureImage(100); + } + + public MainActivity getMainActivity() { + return (MainActivity) getActivity(); + } + + @Override + public void onFragmentPause() { + if (mMovieListAdapter != null) { + mMovieListAdapter.stopCurVideoView(); + } + } + + @Override + public void onFragmentResume() { + } + + @Override + public void onActivityPause() { + if (mMovieListAdapter != null && !mMovieListAdapter.needBackstagePlay()) { + mNeedRestart = mMovieListAdapter.isCurVideoPlaying(); + if (mNeedRestart) { + mMovieListAdapter.pauseCurVideoView(); + } else { + mMovieListAdapter.stopCurVideoView(); + } + } + } + + @Override + public void onActivityResume() { + if (mMovieListAdapter != null) { + if (mNeedRestart) { + mMovieListAdapter.restartCurVideoView(); + mNeedRestart = false; + } + } + } + + @Override + public void onActivityDestroy() { + if (mMovieListAdapter != null) { + mMovieListAdapter.stopCurVideoView(); + } + } + + @Override + public void onBackPressed() { + if (mFullScreenGroup.getVisibility() == View.VISIBLE) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } else { + getActivity().finish(); + } + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.more_image_btn: + mLandscapeMC.hide(); + mPlayConfigView.setVisibility(View.VISIBLE); + break; + case R.id.path_image: + getMainActivity().showPathDialog(); + break; + case R.id.back_image_btn: + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + break; + case R.id.scan_image: + getMainActivity().scanQrcode(); + break; + case R.id.screen_short_image: + captureImage(); + break; + } + } + + @Override + public void onFullScreen(PLVideoTextureView videoView, MediaController mediaController) { + if (videoView == null) { + return; + } + mCurVideoView = videoView; + mPortraitMC = mediaController; + mPlayConfigView.setVideoView(mCurVideoView); + + if (mFullScreenGroup.getVisibility() != View.VISIBLE) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } else { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + switch (view.getId()) { + case R.id.play_config_view: + return true; + case R.id.media_controller: + mGestureDetector.onTouchEvent(motionEvent); + return false; + } + return false; + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/ShortVideoListAdapter.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/ShortVideoListAdapter.java new file mode 100644 index 0000000..b378810 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/ShortVideoListAdapter.java @@ -0,0 +1,235 @@ +package com.qiniu.droid.niuplayer.fragment; + + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.pili.pldroid.player.PLOnErrorListener; +import com.pili.pldroid.player.PLOnInfoListener; +import com.pili.pldroid.player.widget.PLVideoTextureView; +import com.qiniu.droid.niuplayer.R; +import com.qiniu.droid.niuplayer.model.VideoItem; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +import static com.qiniu.droid.niuplayer.utils.Utils.createAVOptions; + +public class ShortVideoListAdapter extends RecyclerView.Adapter { + private ArrayList mItemList; + private ViewHolder mCurViewHolder; + private DisplayImageOptions mDisplayImageOptions; + private long startsystemtime; + private PLVideoTextureView mVideoView; + private int mCacheStartIndex = -1; + private int mCacheEndIndex = -1; + private final static int CACHE_RANGE = 5; + private HashSet mCacheIndexes = new HashSet(); + + + + public ShortVideoListAdapter(ArrayList arrayList, PLVideoTextureView videoView) { + mItemList = arrayList; + mVideoView = videoView; + mVideoView.setAVOptions(createAVOptions()); + + + mDisplayImageOptions = new DisplayImageOptions.Builder() + .showImageOnLoading(R.drawable.defualt_bg) //加载图片时的图片 + .showImageForEmptyUri(R.drawable.defualt_bg) //没有图片资源时的默认图片 + .showImageOnFail(R.drawable.defualt_bg) //加载失败时的图片 + .cacheInMemory(true) //启用内存缓存 + .cacheOnDisk(true) //启用外存缓存 + .considerExifParams(true) //启用EXIF和JPEG图像格式 + .build(); + } + + class ViewHolder extends RecyclerView.ViewHolder { + FrameLayout videoViewFrameLayout; + PLVideoTextureView videoView; + ImageView coverImage; + TextView nameText; + TextView detailText; + int index; + String videoPath; + String coverPath; + View topView; + ImageButton pausePlayImage; + View holderRootView; + PLOnErrorListener mPLOnErrorListener = new PLOnErrorListener() { + @Override + public boolean onError(int i) { + Log.d("aa", String.valueOf(i)); + return false; + } + }; + PLOnInfoListener mPLOnInfoListener = new PLOnInfoListener() { + @Override + public void onInfo(int i, int i1) { + if (i == PLOnInfoListener.MEDIA_INFO_VIDEO_RENDERING_START) { + coverImage.setVisibility(View.GONE); + Log.d("ShortVideoListAdapter", "first frame time:" + (System.currentTimeMillis() - startsystemtime) + "ms i1=" + i1 + "ms"); + changeCache(); + } + } + }; + + + + public ViewHolder(View itemView) { + super(itemView); + holderRootView = itemView; + videoViewFrameLayout = itemView.findViewById(R.id.video_texture_view); + coverImage = itemView.findViewById(R.id.cover_image); + nameText = itemView.findViewById(R.id.name_text); + detailText = itemView.findViewById(R.id.detail_text); + pausePlayImage = itemView.findViewById(R.id.image_pause_play); + topView = itemView.findViewById(R.id.top_view); + + + topView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (videoView.isPlaying()) { + videoView.pause(); + pausePlayImage.setVisibility(View.VISIBLE); + } else { + videoView.start(); + pausePlayImage.setVisibility(View.GONE); + } + } + }); + } + + public void onStop() { + videoViewFrameLayout.removeView(this.videoView); + } + + public void onStart(PLVideoTextureView videoView) { + this.videoView = videoView; + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + lp.gravity = Gravity.CENTER; + if (this.videoView.getParent() == null) { + videoViewFrameLayout.addView(this.videoView, lp); + } + this.videoView.setDisplayAspectRatio(PLVideoTextureView.ASPECT_RATIO_PAVED_PARENT); + + this.videoView.setLooping(true); + this.videoView.setOnInfoListener(mPLOnInfoListener); + this.videoView.setOnErrorListener(mPLOnErrorListener); + View loadingView = itemView.findViewById(R.id.loading_view); + this.videoView.setBufferingIndicator(loadingView); + } + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + Context context = parent.getContext(); + LayoutInflater inflater = LayoutInflater.from(context); + View contactView = inflater.inflate(R.layout.view_short_video, parent, false); + ViewHolder viewHolder = new ViewHolder(contactView); + return viewHolder; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + VideoItem videoItem = mItemList.get(position); + holder.videoPath = videoItem.getVideoPath(); + holder.coverPath = videoItem.getCoverPath(); + ImageLoader.getInstance().displayImage(holder.coverPath, holder.coverImage, mDisplayImageOptions); + holder.nameText.setText(videoItem.getName()); + holder.index = position; + holder.detailText.setText(videoItem.getTime()); + holder.holderRootView.setTag(position); + } + + @Override + public int getItemCount() { + return mItemList.size(); + } + + @Override + public void onViewAttachedToWindow(ViewHolder holder) { + mCurViewHolder = holder; + holder.pausePlayImage.setVisibility(View.GONE); + holder.coverImage.setVisibility(View.VISIBLE); + } + + @Override + public void onViewDetachedFromWindow(ViewHolder holder) { + holder.onStop(); + } + + public void setCurViewHolder(ViewHolder viewHolder) { + mCurViewHolder = viewHolder; + } + + public void startCurVideoView() { + if (mCurViewHolder != null) { + mCurViewHolder.onStart(mVideoView); + mVideoView.setVideoPath(mCurViewHolder.videoPath); + startsystemtime = System.currentTimeMillis(); + mVideoView.start(); + mCurViewHolder.pausePlayImage.setVisibility(View.GONE); + } + } + + private void changeCache() { + //第一次 + if (mCacheStartIndex == -1 && mCacheEndIndex == -1) { + for (int i = mCurViewHolder.index - 1; i <= mCurViewHolder.index + 3; i++) { + if (i >= 0 && i < mItemList.size() && !mCacheIndexes.contains(i)) { + mVideoView.addCache(mItemList.get(i).getVideoPath()); + mCacheIndexes.add(i); + } + } + mCacheStartIndex = 0; + } else { + //删除 出range的cache + int remove_index = mCurViewHolder.index - 2; + if (remove_index >= 0 && remove_index < mItemList.size() && mCacheIndexes.contains(remove_index)) { + mVideoView.delCache(mItemList.get(remove_index).getVideoPath()); + mCacheIndexes.remove(remove_index); + } + + remove_index = mCurViewHolder.index + 4; + if (remove_index >= 0 && remove_index < mItemList.size() && mCacheIndexes.contains(remove_index)) { + mVideoView.delCache(mItemList.get(remove_index).getVideoPath()); + mCacheIndexes.remove(remove_index); + } + + //增加新进range的cache + for (int i = mCurViewHolder.index - 1; i <= mCurViewHolder.index + 3; i++) { + if (i >= 0 && i < mItemList.size() && !mCacheIndexes.contains(i)) { + mVideoView.addCache(mItemList.get(i).getVideoPath()); + mCacheIndexes.add(i); + } + } + } + } + + public void pauseCurVideoView() { + if (mCurViewHolder != null) { + mVideoView.pause(); + } + } + + public void stopCurVideoView() { + if (mCurViewHolder != null) { + mVideoView.pause(); + mCurViewHolder.coverImage.setVisibility(View.VISIBLE); + } + } +} \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/ShortVideoListFragment.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/ShortVideoListFragment.java new file mode 100644 index 0000000..db844b0 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/fragment/ShortVideoListFragment.java @@ -0,0 +1,178 @@ +package com.qiniu.droid.niuplayer.fragment; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.PagerSnapHelper; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.pili.pldroid.player.widget.PLVideoTextureView; +import com.qiniu.droid.niuplayer.R; +import com.qiniu.droid.niuplayer.model.VideoItem; +import com.qiniu.droid.niuplayer.model.ModelFactory; + +import java.util.ArrayList; + +import static com.qiniu.droid.niuplayer.utils.Config.SHORT_VIDEO_PATH_PREFIX; + +public class ShortVideoListFragment extends Fragment implements FragmentLifecycle { + + private RecyclerView mVideoList; + private ArrayList mItemList; + private ShortVideoListAdapter mShortVideoListAdapter; + private PLVideoTextureView mVideoView; + private volatile boolean mShouldPlay; + private int mCurrentPosition = -1; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_short_video_list, container, false); + initShortVideoData(rootView); + return rootView; + } + + private void initShortVideoData(final View rootView) { + + ModelFactory.createVideoItemListByURL(SHORT_VIDEO_PATH_PREFIX, new ModelFactory.OnResultListener() { + @Override + public void onSuccess(int statusCode, Object data) { + mItemList = (ArrayList) data; + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + initView(rootView); + } + }); + } + + @Override + public void onFailure() { + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(getActivity(), "获得短视频列表失败", Toast.LENGTH_LONG).show(); + } + }); + } + }); + } + + private void initView(View root) { + mVideoList = root.findViewById(R.id.video_list); + LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); + mVideoList.setLayoutManager(layoutManager); + mVideoList.setHasFixedSize(true); + + PagerSnapHelper snapHelper = new PagerSnapHelper(); + snapHelper.attachToRecyclerView(mVideoList); + + mVideoView = new PLVideoTextureView(getContext()); + + mShortVideoListAdapter = new ShortVideoListAdapter(mItemList, mVideoView); + mVideoList.setAdapter(mShortVideoListAdapter); + mVideoList.addOnScrollListener(mOnScrollListener); + + if (mShouldPlay) { + mVideoList.post(new Runnable() { + @Override + public void run() { + startCurVideoView(); + mShouldPlay = false; + } + }); + } + } + + @Override + public void onPause() { + super.onPause(); + if (mShortVideoListAdapter != null) { + mShortVideoListAdapter.pauseCurVideoView(); + } + } + + @Override + public void onStop() { + super.onStop(); + if (mShortVideoListAdapter != null) { + mShortVideoListAdapter.stopCurVideoView(); + } + } + + @Override + public void onFragmentPause() { + if (mShortVideoListAdapter != null) { + mShortVideoListAdapter.stopCurVideoView(); + } + } + + @Override + public void onFragmentResume() { + if (mShortVideoListAdapter != null) { + mShortVideoListAdapter.startCurVideoView(); + } else { + mShouldPlay = true; + } + } + + @Override + public void onActivityPause() { + if (mShortVideoListAdapter != null) { + mShortVideoListAdapter.stopCurVideoView(); + } + } + + @Override + public void onActivityResume() { + if (mShortVideoListAdapter != null) { + mShortVideoListAdapter.startCurVideoView(); + } + } + + @Override + public void onActivityDestroy() { + if (mShortVideoListAdapter != null) { + mShortVideoListAdapter.stopCurVideoView(); + } + } + + @Override + public void onBackPressed() { + getActivity().finish(); + } + + private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + startCurVideoView(); + } + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + } + }; + + private void startCurVideoView() { + LinearLayoutManager layoutManager = (LinearLayoutManager) mVideoList.getLayoutManager(); + int visibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition(); + + if (visibleItemPosition >= 0 && mCurrentPosition != visibleItemPosition) { + mShortVideoListAdapter.stopCurVideoView(); + mCurrentPosition = visibleItemPosition; + View holderView = mVideoList.findViewWithTag(mCurrentPosition); + if (holderView != null) { + ShortVideoListAdapter.ViewHolder viewHolder = (ShortVideoListAdapter.ViewHolder) mVideoList.getChildViewHolder(holderView); + mShortVideoListAdapter.setCurViewHolder(viewHolder); + mShortVideoListAdapter.startCurVideoView(); + } + } + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/ModelFactory.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/ModelFactory.java new file mode 100644 index 0000000..aac50ae --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/ModelFactory.java @@ -0,0 +1,86 @@ +package com.qiniu.droid.niuplayer.model; + + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class ModelFactory { + public interface OnResultListener { + void onSuccess(int statusCode, Object data); + void onFailure(); + } + + public static void createVideoItemListByURL(String url, final OnResultListener listener) { + OkHttpClient client = new OkHttpClient(); + Request request = new Request.Builder() + .url(url) + .build(); + client.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + listener.onFailure(); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + ArrayList arrayList = new ArrayList<>(); + try { + JSONObject jsonObject = new JSONObject(response.body().string()); + JSONArray jsonItems = jsonObject.getJSONArray("Items"); + for (int i = 0; i < jsonItems.length(); i++) { + JSONObject jsonItem = jsonItems.getJSONObject(i); + VideoItem item = new VideoItem(); + item.setName(jsonItem.optString("key")); + item.setTime(jsonItem.optString("putTime")); + item.setSize(jsonItem.optString("fsize")); + arrayList.add(item); + } + listener.onSuccess(response.code(), arrayList); + } catch (JSONException e) { + e.printStackTrace(); + listener.onFailure(); + } + } + }); + } + + public static void createUpgradeInfoByURL(String url, final OnResultListener listener) { + OkHttpClient client = new OkHttpClient(); + Request request = new Request.Builder() + .url(url) + .build(); + client.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + listener.onFailure(); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + UpgradeInfo upgradeInfo = new UpgradeInfo(); + try { + JSONObject jsonObject = new JSONObject(response.body().string()); + upgradeInfo.setAppID(jsonObject.optString("AppId")); + upgradeInfo.setVersion(jsonObject.optString("Version")); + upgradeInfo.setDescription(jsonObject.optString("Description")); + upgradeInfo.setDownloadURL(jsonObject.optString("DownloadURL")); + upgradeInfo.setCreateTime(jsonObject.optString("CreateAt")); + listener.onSuccess(response.code(), upgradeInfo); + } catch (JSONException e) { + e.printStackTrace(); + listener.onFailure(); + } + } + }); + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/UpgradeInfo.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/UpgradeInfo.java new file mode 100644 index 0000000..3ea4a58 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/UpgradeInfo.java @@ -0,0 +1,50 @@ +package com.qiniu.droid.niuplayer.model; + + +public class UpgradeInfo { + private String mAppID; + private String mVersion; + private String mDescription; + private String mDownloadURL; + private String mCreateTime; + + public void setAppID(String appID) { + this.mAppID = appID; + } + + public void setVersion(String version) { + this.mVersion = version; + } + + public void setDescription(String description) { + this.mDescription = description; + } + + public void setDownloadURL(String downloadURL) { + this.mDownloadURL = downloadURL; + } + + public void setCreateTime(String createTime) { + this.mCreateTime = createTime; + } + + public String getAppID() { + return mAppID; + } + + public String getVersion() { + return mVersion; + } + + public String getDescription() { + return mDescription; + } + + public String getDownloadURL() { + return mDownloadURL; + } + + public String getCreateTime() { + return mCreateTime; + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/VideoItem.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/VideoItem.java new file mode 100644 index 0000000..ba58dba --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/model/VideoItem.java @@ -0,0 +1,72 @@ +package com.qiniu.droid.niuplayer.model; + +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static com.qiniu.droid.niuplayer.utils.Config.COVER_PATH_PREFIX; +import static com.qiniu.droid.niuplayer.utils.Config.COVER_PATH_SUFFIX; +import static com.qiniu.droid.niuplayer.utils.Config.VIDEO_PATH_PREFIX; + +public class VideoItem implements Serializable { + private String mName; + private String mSize; + private String mTime; + private String mVideoPath; + private String mCoverPath; + + public void setName(String name) { + this.mVideoPath = VIDEO_PATH_PREFIX + name; + this.mName = getVideoName(name); + this.mCoverPath = COVER_PATH_PREFIX + mName + COVER_PATH_SUFFIX; + } + + public void setSize(String size) { + this.mSize = size; + } + + public void setTime(String time) { + time = time.substring(0, 13); + mTime = stampToDate(time); + } + + public String getName() { + return mName; + } + + public String getSize() { + return mSize; + } + + public String getTime() { + return mTime; + } + + public String getVideoPath() { + return mVideoPath; + } + + public String getCoverPath() { + return mCoverPath; + } + + private static String stampToDate(String s) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + long lt = new Long(s); + Date date = new Date(lt); + return simpleDateFormat.format(date); + } + + private static String getVideoName(String name) { + int start = name.lastIndexOf("/"); + if (start != -1) { + String subString = name.substring(start + 1); + subString = subString.replace(".mp4", ""); + return subString; + } else { + return null; + } + } + + +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/Config.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/Config.java new file mode 100644 index 0000000..9cc7946 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/Config.java @@ -0,0 +1,17 @@ +package com.qiniu.droid.niuplayer.utils; + +import android.os.Environment; + +public class Config { + public static final String SDCARD_DIR = Environment.getExternalStorageDirectory().getAbsolutePath(); + public static final String DEFAULT_CACHE_DIR_NAME = "PLDroidPlayer"; + public static final String DEFAULT_CACHE_DIR_PATH = SDCARD_DIR + "/PLDroidPlayer"; + public static final String VIDEO_PATH_PREFIX = "http://demo-videos.qnsdk.com/"; + public static final String COVER_PATH_PREFIX = "http://demo-videos.qnsdk.com/snapshoot/"; + public static final String COVER_PATH_SUFFIX = ".jpg"; + public static final String MOVIE_PATH_PREFIX = "https://api-demo.qnsdk.com/v1/kodo/bucket/demo-videos?prefix=movies"; + public static final String SHORT_VIDEO_PATH_PREFIX = "https://api-demo.qnsdk.com/v1/kodo/bucket/demo-videos?prefix=shortvideo"; + public static final String LIVE_TEST_URL = "rtmp://live.hkstv.hk.lxdns.com/live/hks"; + public static final String UPGRADE_URL_PREFIX = "https://api-demo.qnsdk.com/v1/upgrade/app?appId="; + +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/DownloadService.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/DownloadService.java new file mode 100755 index 0000000..fc88112 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/DownloadService.java @@ -0,0 +1,162 @@ +package com.qiniu.droid.niuplayer.utils; + +import android.app.IntentService; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.support.v4.app.NotificationCompat.Builder; +import android.support.v4.content.FileProvider; +import android.util.Log; + +import com.nostra13.universalimageloader.utils.StorageUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +import static com.qiniu.droid.niuplayer.utils.Config.DEFAULT_CACHE_DIR_NAME; + +public class DownloadService extends IntentService { + private static final int BUFFER_SIZE = 10 * 1024; // 8k ~ 32K + private static final String TAG = "DownloadService"; + + private static final int NOTIFICATION_ID = 0; + + private NotificationManager mNotifyManager; + private Builder mBuilder; + + public DownloadService() { + super("DownloadService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + + mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mBuilder = new Builder(this); + + String appName = getString(getApplicationInfo().labelRes); + int icon = getApplicationInfo().icon; + + mBuilder.setContentTitle(appName).setSmallIcon(icon); + String urlStr = intent.getStringExtra("url"); + InputStream in = null; + FileOutputStream out = null; + try { + URL url = new URL(urlStr); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod("GET"); + urlConnection.setDoOutput(false); + urlConnection.setConnectTimeout(10 * 1000); + urlConnection.setReadTimeout(10 * 1000); + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Charset", "UTF-8"); + urlConnection.setRequestProperty("Accept-Encoding", "gzip, deflate"); + + urlConnection.connect(); + long bytetotal = urlConnection.getContentLength(); + long bytesum = 0; + int byteread = 0; + in = urlConnection.getInputStream(); +// File dir = StorageUtils.getCacheDirectory(this); + File dir = StorageUtils.getOwnCacheDirectory(this, DEFAULT_CACHE_DIR_NAME);//sdcard目录 + String apkName = urlStr.substring(urlStr.lastIndexOf("/") + 1, urlStr.length()); + File apkFile = new File(dir, apkName); + out = new FileOutputStream(apkFile); + byte[] buffer = new byte[BUFFER_SIZE]; + + int oldProgress = 0; + + while ((byteread = in.read(buffer)) != -1) { + bytesum += byteread; + out.write(buffer, 0, byteread); + + int progress = (int) (bytesum * 100L / bytetotal); + // 如果进度与之前进度相等,则不更新,如果更新太频繁,否则会造成界面卡顿 + + if (progress != oldProgress) { + updateProgress(progress); + } + oldProgress = progress; + } + // 下载完成 + + installAPk(this, apkFile); + + mNotifyManager.cancel(NOTIFICATION_ID); + + } catch (Exception e) { + Log.e(TAG, "download apk file error:" + e.getMessage()); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException ignored) { + + } + } + if (in != null) { + try { + in.close(); + } catch (IOException ignored) { + + } + } + } + } + + private void updateProgress(int progress) { + //"正在下载:" + progress + "%" + mBuilder.setContentText("正在下载 " + progress + "%").setProgress(100, progress, false); + //setContentInent如果不设置在4.0+上没有问题,在4.0以下会报异常 + PendingIntent pendingintent = PendingIntent.getActivity(this, 0, new Intent(), PendingIntent.FLAG_CANCEL_CURRENT); + mBuilder.setContentIntent(pendingintent); + mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build()); + } + + + private void installAPk(Context context, File apkFile) { + Intent installAPKIntent = getApkInStallIntent(context, apkFile); + startActivity(installAPKIntent); + } + + private Intent getApkInStallIntent(Context context, File apkFile) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".update.provider", apkFile); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(uri, "application/vnd.android.package-archive"); + } else { + Uri uri = getApkUri(apkFile); + intent.setDataAndType(uri, "application/vnd.android.package-archive"); + } + return intent; + } + + + private Uri getApkUri(File apkFile) { + Log.d(TAG, apkFile.toString()); + + //如果没有设置 SDCard 写权限,或者没有 SDCard,apk 文件保存在内存中,需要授予权限才能安装 + try { + String[] command = {"chmod", "777", apkFile.toString()}; + ProcessBuilder builder = new ProcessBuilder(command); + builder.start(); + } catch (IOException ignored) { + } + Uri uri = Uri.fromFile(apkFile); + Log.d(TAG, uri.toString()); + + return uri; + } + +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/GetPathFromUri.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/GetPathFromUri.java new file mode 100644 index 0000000..8614023 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/GetPathFromUri.java @@ -0,0 +1,132 @@ +package com.qiniu.droid.niuplayer.utils; + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; + +public class GetPathFromUri { + + @SuppressLint("NewApi") + public static String getPath(final Context context, final Uri uri) { + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + // ExternalStorageProvider + if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } + + // Good job vivo + if ("5D68-9217".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } + } + // DownloadsProvider + else if (isDownloadsDocument(uri)) { + + final String id = DocumentsContract.getDocumentId(uri); + final Uri contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + + return getDataColumn(context, contentUri, null, null); + } + // MediaProvider + else if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + + final String selection = "_id=?"; + final String[] selectionArgs = new String[]{split[1]}; + + return getDataColumn(context, contentUri, selection, selectionArgs); + } + } + // MediaStore (and general) + else if ("content".equalsIgnoreCase(uri.getScheme())) { + return getDataColumn(context, uri, null, null); + } + // File + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + + return null; + } + + /** + * Get the value of the data column for this Uri. This is useful for + * MediaStore Uris, and other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @param selection (Optional) Filter used in the query. + * @param selectionArgs (Optional) Selection arguments used in the query. + * @return The value of the _data column, which is typically a file path. + */ + public static String getDataColumn(Context context, Uri uri, String selection, + String[] selectionArgs) { + + Cursor cursor = null; + final String column = "_data"; + final String[] projection = {column}; + + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, + null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + public static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + public static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + public static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/PermissionChecker.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/PermissionChecker.java new file mode 100644 index 0000000..d45c25f --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/PermissionChecker.java @@ -0,0 +1,151 @@ +package com.qiniu.droid.niuplayer.utils; + +import android.Manifest; +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import android.os.Build; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.List; + +public class PermissionChecker { + private static final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124; + + private Activity mActivity; + + public PermissionChecker(Activity activity) { + mActivity = activity; + } + + /** + * Check that all given permissions have been granted by verifying that each entry in the + * given array is of the value {@link PackageManager#PERMISSION_GRANTED}. + * + * @see Activity#onRequestPermissionsResult(int, String[], int[]) + */ + private boolean verifyPermissions(int[] grantResults) { + // At least one result must be checked. + if (grantResults.length < 1) { + return false; + } + + // Verify that each required permission has been granted, otherwise return false. + for (int result : grantResults) { + if (result != PackageManager.PERMISSION_GRANTED) { + return false; + } + } + return true; + } + + @TargetApi(Build.VERSION_CODES.M) + public boolean isCameraPermissionOK() { + boolean ret = true; + + List permissionsNeeded = new ArrayList(); + final List permissionsList = new ArrayList(); + if (!addPermission(permissionsList, Manifest.permission.CAMERA)) { + permissionsNeeded.add("CAMERA"); + } + + if (permissionsNeeded.size() > 0) { + mActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), + REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); + ret = false; + } + + return ret; + } + + @TargetApi(Build.VERSION_CODES.M) + public boolean isStoragePermissionOK() { + boolean ret = true; + + List permissionsNeeded = new ArrayList(); + final List permissionsList = new ArrayList(); + if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + permissionsNeeded.add("Write external storage"); + } + + if (permissionsNeeded.size() > 0) { + mActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), + REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); + ret = false; + } + + return ret; + } + + @TargetApi(Build.VERSION_CODES.M) + public boolean checkPermission() { + boolean ret = true; + + List permissionsNeeded = new ArrayList(); + final List permissionsList = new ArrayList(); + if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + permissionsNeeded.add("Write external storage"); + } + if (!addPermission(permissionsList, Manifest.permission.CAMERA)) { + permissionsNeeded.add("CAMERA"); + } + + if (permissionsNeeded.size() > 0) { + // Need Rationale + String message = "You need to grant access to " + permissionsNeeded.get(0); + for (int i = 1; i < permissionsNeeded.size(); i++) { + message = message + ", " + permissionsNeeded.get(i); + } + // Check for Rationale Option + if (!mActivity.shouldShowRequestPermissionRationale(permissionsList.get(0))) { + showMessageOKCancel(message, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), + REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); + } + }); + } else { + mActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), + REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); + } + ret = false; + } + + return ret; + } + + private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) { + new AlertDialog.Builder(mActivity) + .setMessage(message) + .setPositiveButton("OK", okListener) + .setNegativeButton("Cancel", null) + .create() + .show(); + } + + @TargetApi(Build.VERSION_CODES.M) + private boolean addPermission(List permissionsList, String permission) { + boolean ret = true; + if (mActivity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { + permissionsList.add(permission); + ret = false; + } + return ret; + } + + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode == REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS) { + if (verifyPermissions(grantResults)) { + // all permissions granted + } else { + // some permissions denied + Toast.makeText(mActivity, "some permissions denied", Toast.LENGTH_SHORT).show(); + } + } + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/UpgradeApkFileProvider.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/UpgradeApkFileProvider.java new file mode 100644 index 0000000..42ed852 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/UpgradeApkFileProvider.java @@ -0,0 +1,6 @@ +package com.qiniu.droid.niuplayer.utils; + +import android.support.v4.content.FileProvider; + +public class UpgradeApkFileProvider extends FileProvider { +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/Utils.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/Utils.java new file mode 100644 index 0000000..104f74e --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/utils/Utils.java @@ -0,0 +1,142 @@ +package com.qiniu.droid.niuplayer.utils; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Build; +import android.support.design.widget.TabLayout; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import com.pili.pldroid.player.AVOptions; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +public class Utils { + public static boolean isNetworkAvailable(Context context) { + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo netInfo = cm.getActiveNetworkInfo(); + return netInfo != null && netInfo.isConnectedOrConnecting(); + } + + public static String getAppProcessName(Context context) { + int pid = android.os.Process.myPid(); + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + List infos = manager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo info : infos) { + if (info.pid == pid) + return info.processName; + } + return ""; + } + + public static int getAppVersionCode(Context context) { + int versioncode = 0; + try { + // ---get the package info--- + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0); + versioncode = pi.versionCode; + } catch (Exception e) { + Log.e("VersionInfo", "Exception", e); + } + return versioncode; + } + + public static void wrapTabIndicatorToTitle(TabLayout tabLayout, int externalMargin, int internalMargin) { + View tabStrip = tabLayout.getChildAt(0); + if (tabStrip instanceof ViewGroup) { + ViewGroup tabStripGroup = (ViewGroup) tabStrip; + int childCount = ((ViewGroup) tabStrip).getChildCount(); + for (int i = 0; i < childCount; i++) { + View tabView = tabStripGroup.getChildAt(i); + //set minimum width to 0 for instead for small texts, indicator is not wrapped as expected + tabView.setMinimumWidth(0); + // set padding to 0 for wrapping indicator as title + tabView.setPadding(0, tabView.getPaddingTop(), 0, tabView.getPaddingBottom()); + // setting custom margin between tabs + if (tabView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) tabView.getLayoutParams(); + if (i == 0) { + // left + setTabMargin(layoutParams, externalMargin, internalMargin); + } else if (i == childCount - 1) { + // right + setTabMargin(layoutParams, internalMargin, externalMargin); + } else { + // internal + setTabMargin(layoutParams, internalMargin, internalMargin); + } + } + } + + tabLayout.requestLayout(); + } + } + + private static void setTabMargin(ViewGroup.MarginLayoutParams layoutParams, int start, int end) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + layoutParams.setMarginStart(start); + layoutParams.setMarginEnd(end); + } else { + layoutParams.leftMargin = start; + layoutParams.rightMargin = end; + } + } + + public static boolean savePhotoToSDCard(Bitmap photoBitmap, String path, String photoName) { + boolean ret = false; + File dir = new File(path); + if (!dir.exists()) { + dir.mkdirs(); + } + + File photoFile = new File(path, photoName + ".jpg"); + FileOutputStream fileOutputStream = null; + try { + fileOutputStream = new FileOutputStream(photoFile); + if (photoBitmap != null) { + if (photoBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream)) { + fileOutputStream.flush(); + ret = true; + } + } + } catch (FileNotFoundException e) { + photoFile.delete(); + e.printStackTrace(); + } catch (IOException e) { + photoFile.delete(); + e.printStackTrace(); + } finally { + try { + fileOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return ret; + } + + public static AVOptions createAVOptions() { + AVOptions options = new AVOptions(); + // the unit of timeout is ms + options.setInteger(AVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000); + options.setInteger(AVOptions.KEY_LIVE_STREAMING, 0); +// options.setString(AVOptions.KEY_COMP_DRM_KEY, "test123"); + // 1 -> hw codec enable, 0 -> disable [recommended] + options.setInteger(AVOptions.KEY_MEDIACODEC, AVOptions.MEDIA_CODEC_SW_DECODE); + options.setInteger(AVOptions.KEY_PREFER_FORMAT, AVOptions.PREFER_FORMAT_MP4); + boolean disableLog = false; + options.setInteger(AVOptions.KEY_LOG_LEVEL, disableLog ? 5 : 0); + return options; + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/CircleImageView.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/CircleImageView.java new file mode 100644 index 0000000..7e02a25 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/CircleImageView.java @@ -0,0 +1,76 @@ + +package com.qiniu.droid.niuplayer.widget; + + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.support.v7.widget.AppCompatImageView; + +public class CircleImageView extends AppCompatImageView { + public CircleImageView(Context context) { + super(context); + } + + public CircleImageView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG); + private Bitmap mRawBitmap; + private BitmapShader mShader; + private Matrix mMatrix = new Matrix(); + + @Override + protected void onDraw(Canvas canvas) { + Bitmap rawBitmap = getBitmap(getDrawable()); + if (rawBitmap != null) { + int viewWidth = getWidth(); + int viewHeight = getHeight(); + int viewMinSize = Math.min(viewWidth, viewHeight); + float dstWidth = viewMinSize; + float dstHeight = viewMinSize; + if (mShader == null || !rawBitmap.equals(mRawBitmap)) { + mRawBitmap = rawBitmap; + mShader = new BitmapShader(mRawBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + } + if (mShader != null) { + mMatrix.setScale(dstWidth / rawBitmap.getWidth(), dstHeight / rawBitmap.getHeight()); + mShader.setLocalMatrix(mMatrix); + } + mPaintBitmap.setShader(mShader); + float radius = viewMinSize / 2.0f; + canvas.drawCircle(radius, radius, radius, mPaintBitmap); + } else { + super.onDraw(canvas); + } + } + + private Bitmap getBitmap(Drawable drawable) { + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } else if (drawable instanceof ColorDrawable) { + Rect rect = drawable.getBounds(); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + int color = ((ColorDrawable) drawable).getColor(); + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color)); + return bitmap; + } else { + return null; + } + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/CommomDialog.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/CommomDialog.java new file mode 100644 index 0000000..5d7a282 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/CommomDialog.java @@ -0,0 +1,121 @@ +package com.qiniu.droid.niuplayer.widget; + +import android.app.Activity; +import android.app.Dialog; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.Display; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.TextView; + +import com.qiniu.droid.niuplayer.R; + +public class CommomDialog extends Dialog implements View.OnClickListener { + private TextView mTitleTxt; + private TextView mSubmitTxt; + private TextView mCancelTxt; + private EditText mEditText; + + private Activity mActivity; + private OnCloseListener listener; + private String positiveName; + private String negativeName; + private String title; + + public CommomDialog(Activity activity) { + this(activity, R.style.dialog, null); + } + + public CommomDialog(Activity activity, int themeResId, String content) { + super(activity, themeResId); + this.mActivity = activity; + } + + public CommomDialog(Activity activity, int themeResId, String content, OnCloseListener listener) { + super(activity, themeResId); + this.mActivity = activity; + this.listener = listener; + } + + protected CommomDialog(Activity activity, boolean cancelable, OnCancelListener cancelListener) { + super(activity, cancelable, cancelListener); + this.mActivity = activity; + } + + public String getEditString() { + return mEditText.getText().toString(); + } + + public CommomDialog setTitle(String title) { + this.title = title; + return this; + } + + public CommomDialog setPositiveButton(String name) { + this.positiveName = name; + return this; + } + + public CommomDialog setNegativeButton(String name) { + this.negativeName = name; + return this; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.dialog_common); + setCanceledOnTouchOutside(false); + initView(); + } + + private void initView() { + mTitleTxt = (TextView) findViewById(R.id.title); + mSubmitTxt = (TextView) findViewById(R.id.submit); + mSubmitTxt.setOnClickListener(this); + mCancelTxt = (TextView) findViewById(R.id.cancel); + mEditText = findViewById(R.id.edit_text); + mCancelTxt.setOnClickListener(this); + + if (!TextUtils.isEmpty(positiveName)) { + mSubmitTxt.setText(positiveName); + } + + if (!TextUtils.isEmpty(negativeName)) { + mCancelTxt.setText(negativeName); + } + + if (!TextUtils.isEmpty(title)) { + mTitleTxt.setText(title); + } + + WindowManager windowManager = mActivity.getWindowManager(); + Display display = windowManager.getDefaultDisplay(); + WindowManager.LayoutParams lp = this.getWindow().getAttributes(); + lp.width = (int) (display.getWidth() * 0.75); + this.getWindow().setAttributes(lp); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.cancel: + if (listener != null) { + listener.onClick(this, false); + } + this.dismiss(); + break; + case R.id.submit: + if (listener != null) { + listener.onClick(this, true); + } + break; + } + } + + public interface OnCloseListener { + void onClick(Dialog dialog, boolean confirm); + } +} \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/GestureControllerListener.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/GestureControllerListener.java new file mode 100644 index 0000000..f090bb8 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/GestureControllerListener.java @@ -0,0 +1,97 @@ +package com.qiniu.droid.niuplayer.widget; + + +import android.app.Activity; +import android.content.Context; +import android.media.AudioManager; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.WindowManager; + +public class GestureControllerListener implements GestureDetector.OnGestureListener { + private float mCurVolume; + private Activity mActivity; + + public GestureControllerListener(Activity activity) { + mActivity = activity; + } + + @Override + public boolean onDown(MotionEvent motionEvent) { + return true; + } + + @Override + public void onShowPress(MotionEvent motionEvent) { + } + + @Override + public boolean onSingleTapUp(MotionEvent motionEvent) { + return true; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + float touchX = e1.getX(); + WindowManager wm = (WindowManager) mActivity + .getSystemService(Context.WINDOW_SERVICE); + int width = wm.getDefaultDisplay().getWidth(); + + final double FLING_MIN_DISTANCE = 0.5; + final double FLING_MIN_VELOCITY = 0.5; + if (e1.getY() - e2.getY() > FLING_MIN_DISTANCE + && Math.abs(distanceY) > FLING_MIN_VELOCITY) { + if (touchX > width / 2) { + setVolume(0.1f); + } else { + setBrightness(10); + } + } + if (e1.getY() - e2.getY() < FLING_MIN_DISTANCE + && Math.abs(distanceY) > FLING_MIN_VELOCITY) { + if (touchX > width / 2) { + setVolume(-0.1f); + } else { + setBrightness(-10); + } + } + return false; + } + + @Override + public void onLongPress(MotionEvent motionEvent) { + } + + @Override + public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) { + return false; + } + + public void setVolume(float volume) { + AudioManager audioManager = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE); + int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); // 获取系统最大音量 + + if (mCurVolume == 0) { + mCurVolume = (float) (audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)); + } + mCurVolume += volume; + if (mCurVolume > maxVolume) { + mCurVolume = maxVolume; + } else if (mCurVolume < 0) { + mCurVolume = 0; + } + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int) mCurVolume, 0); + } + + public void setBrightness(float brightness) { + WindowManager.LayoutParams lp = mActivity.getWindow().getAttributes(); + lp.screenBrightness = lp.screenBrightness + brightness / 255.0f; + if (lp.screenBrightness > 1) { + lp.screenBrightness = 1; + } else if (lp.screenBrightness < 0.01) { + lp.screenBrightness = (float) 0.01; + } + mActivity.getWindow().setAttributes(lp); + } +} + diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/MediaController.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/MediaController.java new file mode 100644 index 0000000..24fec43 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/MediaController.java @@ -0,0 +1,603 @@ +package com.qiniu.droid.niuplayer.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Rect; +import android.media.AudioManager; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.PopupWindow; +import android.widget.ProgressBar; +import android.widget.SeekBar; +import android.widget.TextView; + +import com.pili.pldroid.player.IMediaController; +import com.qiniu.droid.niuplayer.R; + +import java.util.Locale; + +/** + * You can write a custom MediaController instead of this class + * A MediaController widget must implement all the interface defined by com.pili.pldroid.player.IMediaController + */ +public class MediaController extends FrameLayout implements IMediaController { + + private static final String TAG = "PLMediaController"; + private IMediaController.MediaPlayerControl mPlayer; + private Context mContext; + private PopupWindow mWindow; + private int mAnimStyle; + private View mAnchor; + private View mRoot; + private ProgressBar mProgress; + private TextView mEndTime, mCurrentTime; + private long mDuration; + private boolean mShowing; + private boolean mDragging; + private boolean mInstantSeeking = true; + private static int sDefaultTimeout = 3000; + private static final int SEEK_TO_POST_DELAY_MILLIS = 200; + + private static final int FADE_OUT = 1; + private static final int SHOW_PROGRESS = 2; + private boolean mFromXml = false; + private ImageButton mPauseButton; + private ImageButton mFfwdButton; + private ImageButton mRewButton; + private ImageButton mNextButton; + private ImageButton mPrevButton; + + private boolean mUseFastForward; + + private static final int MEDIA_CONTROLLER_ID = Resources.getSystem().getIdentifier("media_controller", "layout", "android"); + private static final int PRV_BUTTON_ID = Resources.getSystem().getIdentifier("prev","id", "android"); + private static final int FFWD_BUTTON_ID = Resources.getSystem().getIdentifier("ffwd","id", "android"); + private static final int NEXT_BUTTON_ID = Resources.getSystem().getIdentifier("next","id", "android"); + private static final int REW_BUTTON_ID = Resources.getSystem().getIdentifier("rew","id", "android"); + + private static int PAUSE_BUTTON_ID = Resources.getSystem().getIdentifier("pause","id", "android"); + private static int MEDIACONTROLLER_PROGRESS_ID = Resources.getSystem().getIdentifier("mediacontroller_progress","id", "android"); + private static int END_TIME_ID = Resources.getSystem().getIdentifier("time","id", "android"); + private static int CURRENT_TIME_ID = Resources.getSystem().getIdentifier("time_current","id", "android"); + private static int IC_MEDIA_PAUSE_ID = Resources.getSystem().getIdentifier("ic_media_pause","drawable", "android"); + private static int IC_MEDIA_PLAY_ID = Resources.getSystem().getIdentifier("ic_media_play","drawable", "android"); + + private AudioManager mAM; + private Runnable mLastSeekBarRunnable; + private boolean mDisableProgress = false; + private OnClickSpeedAdjustListener mOnClickSpeedAdjustListener; + + public interface OnClickSpeedAdjustListener { + void onClickNormal(); + void onClickFaster(); + void onClickSlower(); + } + + public MediaController(Context context, AttributeSet attrs) { + super(context, attrs); + mRoot = this; + mFromXml = true; + initController(context); + } + + public MediaController(Context context) { + super(context); + if (!mFromXml && initController(context)) + initFloatingWindow(); + } + + public MediaController(Context context, boolean useFastForward, boolean disableProgressBar) { + this(context); + mUseFastForward = useFastForward; + mDisableProgress = disableProgressBar; + } + + public MediaController(Context context, boolean useFastForward) { + this(context); + mUseFastForward = useFastForward; + } + + public void refreshProgress() { + mProgress.setProgress(1000); + mCurrentTime.setText(generateTime(mDuration)); + } + + public void setOnClickSpeedAdjustListener(OnClickSpeedAdjustListener listener) { + mOnClickSpeedAdjustListener = listener; + } + + private boolean initController(Context context) { + mUseFastForward = true; + mContext = context.getApplicationContext(); + mAM = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + return true; + } + + @Override + public void onFinishInflate() { + if (mRoot != null) + initControllerView(mRoot); + super.onFinishInflate(); + } + + private void initFloatingWindow() { + mWindow = new PopupWindow(mContext); + mWindow.setFocusable(false); + mWindow.setBackgroundDrawable(null); + mWindow.setOutsideTouchable(true); + mAnimStyle = android.R.style.Animation; + } + + /** + * Create the view that holds the widgets that control playback. Derived + * classes can override this to create their own. + * + * @return The controller view. + */ + protected View makeControllerView() { + return ((LayoutInflater) mContext + .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(MEDIA_CONTROLLER_ID, this); + } + + private void initControllerView(View v) { + if (mFromXml){ + PAUSE_BUTTON_ID = R.id.controller_stop_play; + MEDIACONTROLLER_PROGRESS_ID = R.id.controller_progress_bar; + END_TIME_ID = R.id.controller_end_time; + CURRENT_TIME_ID = R.id.controller_current_time; + IC_MEDIA_PAUSE_ID = R.drawable.player_stop; + IC_MEDIA_PLAY_ID = R.drawable.play_player; + } + + // By default these are hidden. + mPrevButton = (ImageButton) v.findViewById(PRV_BUTTON_ID); + if (mPrevButton != null) { + mPrevButton.setVisibility(View.GONE); + } + mNextButton = (ImageButton) v.findViewById(NEXT_BUTTON_ID); + if (mNextButton != null) { + mNextButton.setVisibility(View.GONE); + } + + mFfwdButton = (ImageButton) v.findViewById(FFWD_BUTTON_ID); + if (mFfwdButton != null) { + mFfwdButton.setOnClickListener(mFfwdListener); + if (!mFromXml) { + mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE); + } + } + + mRewButton = (ImageButton) v.findViewById(REW_BUTTON_ID); + if (mRewButton != null) { + mRewButton.setOnClickListener(mRewListener); + if (!mFromXml) { + mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE); + } + } + mPauseButton = (ImageButton) v.findViewById(PAUSE_BUTTON_ID); + if (mPauseButton != null) { + mPauseButton.requestFocus(); + mPauseButton.setOnClickListener(mPauseListener); + } + + mProgress = (ProgressBar) v.findViewById(MEDIACONTROLLER_PROGRESS_ID); + if (mProgress != null) { + if (mProgress instanceof SeekBar) { + SeekBar seeker = (SeekBar) mProgress; + seeker.setOnSeekBarChangeListener(mSeekListener); + seeker.setThumbOffset(1); + } + mProgress.setMax(1000); + mProgress.setEnabled(!mDisableProgress); + } + + mEndTime = (TextView) v.findViewById(END_TIME_ID); + mCurrentTime = (TextView) v.findViewById(CURRENT_TIME_ID); + } + + /** + * Control the action when the seekbar dragged by user + * + * @param seekWhenDragging + * True the media will seek periodically + */ + public void setInstantSeeking(boolean seekWhenDragging) { + mInstantSeeking = seekWhenDragging; + } + + private void disableUnsupportedButtons() { + try { + if (mPauseButton != null && !mPlayer.canPause()) + mPauseButton.setEnabled(false); + } catch (IncompatibleClassChangeError ex) { + } + } + + /** + *

+ * Change the animation style resource for this controller. + *

+ * + *

+ * If the controller is showing, calling this method will take effect only + * the next time the controller is shown. + *

+ * + * @param animationStyle + * animation style to use when the controller appears and disappears. + * Set to -1 for the default animation, 0 for no animation, + * or a resource identifier for an explicit animation. + * + */ + public void setAnimationStyle(int animationStyle) { + mAnimStyle = animationStyle; + } + + + public interface OnShownListener { + public void onShown(); + } + + private OnShownListener mShownListener; + + public void setOnShownListener(OnShownListener l) { + mShownListener = l; + } + + public interface OnHiddenListener { + public void onHidden(); + } + + private OnHiddenListener mHiddenListener; + + public void setOnHiddenListener(OnHiddenListener l) { + mHiddenListener = l; + } + + @SuppressLint("HandlerLeak") + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + long pos; + switch (msg.what) { + case FADE_OUT: + hide(); + break; + case SHOW_PROGRESS: + if (!mPlayer.isPlaying()) { + return; + } + pos = setProgress(); + if (pos == -1) { + return; + } + if (!mDragging && mShowing) { + msg = obtainMessage(SHOW_PROGRESS); + sendMessageDelayed(msg, 1000 - (pos % 1000)); + updatePausePlay(); + } + break; + } + } + }; + + private long setProgress() { + if (mPlayer == null || mDragging) { + return 0; + } + + long position = mPlayer.getCurrentPosition(); + long duration = mPlayer.getDuration(); + if (mProgress != null) { + if (duration > 0) { + long pos = 1000L * position / duration; + mProgress.setProgress((int) pos); + } + int percent = mPlayer.getBufferPercentage(); + mProgress.setSecondaryProgress(percent * 10); + } + + mDuration = duration; + + if (mEndTime != null) + mEndTime.setText(generateTime(mDuration)); + if (mCurrentTime != null) + mCurrentTime.setText(generateTime(position)); + + return position; + } + + private static String generateTime(long position) { + int totalSeconds = (int) (position / 1000); + + int seconds = totalSeconds % 60; + int minutes = (totalSeconds / 60) % 60; + int hours = totalSeconds / 3600; + + if (hours > 0) { + return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, + seconds).toString(); + } else { + return String.format(Locale.US, "%02d:%02d", minutes, seconds) + .toString(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + show(sDefaultTimeout); + return true; + } + + @Override + public boolean onTrackballEvent(MotionEvent ev) { + show(sDefaultTimeout); + return false; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (event.getRepeatCount() == 0 + && (keyCode == KeyEvent.KEYCODE_HEADSETHOOK + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE)) { + doPauseResume(); + show(sDefaultTimeout); + if (mPauseButton != null) + mPauseButton.requestFocus(); + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) { + if (mPlayer.isPlaying()) { + mPlayer.pause(); + updatePausePlay(); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_BACK + || keyCode == KeyEvent.KEYCODE_MENU) { + hide(); + return true; + } else { + show(sDefaultTimeout); + } + return super.dispatchKeyEvent(event); + } + + private OnClickListener mPauseListener = new OnClickListener() { + public void onClick(View v) { + if (mOnClickSpeedAdjustListener != null) { + mOnClickSpeedAdjustListener.onClickNormal(); + } + doPauseResume(); + show(sDefaultTimeout); + } + }; + + private void updatePausePlay() { + if (mRoot == null || mPauseButton == null) + return; + + if (mPlayer.isPlaying()) + mPauseButton.setImageResource(IC_MEDIA_PAUSE_ID); + else + mPauseButton.setImageResource(IC_MEDIA_PLAY_ID); + } + + private void doPauseResume() { + if (mPlayer.isPlaying()) + mPlayer.pause(); + else + mPlayer.start(); + updatePausePlay(); + } + + private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() { + + public void onStartTrackingTouch(SeekBar bar) { + mDragging = true; + show(3600000); + mHandler.removeMessages(SHOW_PROGRESS); + if (mInstantSeeking) + mAM.setStreamMute(AudioManager.STREAM_MUSIC, true); + } + + public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) { + if (!fromuser) { + return; + } + + + final long newposition = (long) (mDuration * progress) / 1000; + String time = generateTime(newposition); + if (mInstantSeeking) { + mHandler.removeCallbacks(mLastSeekBarRunnable); + mLastSeekBarRunnable = new Runnable() { + @Override + public void run() { + mPlayer.seekTo(newposition); + } + }; + mHandler.postDelayed(mLastSeekBarRunnable, SEEK_TO_POST_DELAY_MILLIS); + } + if (mCurrentTime != null) + mCurrentTime.setText(time); + } + + public void onStopTrackingTouch(SeekBar bar) { + if (!mInstantSeeking) + mPlayer.seekTo(mDuration * bar.getProgress() / 1000); + + show(sDefaultTimeout); + mHandler.removeMessages(SHOW_PROGRESS); + mAM.setStreamMute(AudioManager.STREAM_MUSIC, false); + mDragging = false; + mHandler.sendEmptyMessageDelayed(SHOW_PROGRESS, 1000); + } + }; + + private OnClickListener mRewListener = new OnClickListener() { + public void onClick(View v) { + if (mOnClickSpeedAdjustListener != null) { + mOnClickSpeedAdjustListener.onClickSlower(); + } + show(sDefaultTimeout); + } + }; + + private OnClickListener mFfwdListener = new OnClickListener() { + public void onClick(View v) { + if (mOnClickSpeedAdjustListener != null) { + mOnClickSpeedAdjustListener.onClickFaster(); + } + show(sDefaultTimeout); + } + }; + + /** + * Set the view that acts as the anchor for the control view. + * + * - This can for example be a VideoView, or your Activity's main view. + * - AudioPlayer has no anchor view, so the view parameter will be null. + * + * @param view + * The view to which to anchor the controller when it is visible. + */ + @Override + public void setAnchorView(View view) { + mAnchor = view; + if (mAnchor == null) { + sDefaultTimeout = 0; // show forever + } + if (!mFromXml) { + removeAllViews(); + mRoot = makeControllerView(); + mWindow.setContentView(mRoot); + mWindow.setWidth(LayoutParams.MATCH_PARENT); + mWindow.setHeight(LayoutParams.WRAP_CONTENT); + } + initControllerView(mRoot); + } + + @Override + public void setMediaPlayer(MediaPlayerControl player) { + mPlayer = player; + updatePausePlay(); + } + + @Override + public void show() { + show(sDefaultTimeout); + } + + /** + * Show the controller on screen. It will go away automatically after + * 'timeout' milliseconds of inactivity. + * + * @param timeout + * The timeout in milliseconds. Use 0 to show the controller until hide() is called. + */ + @Override + public void show(int timeout) { + if (!mShowing) { + if (mAnchor != null && mAnchor.getWindowToken() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + mAnchor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } + if (mPauseButton != null) + mPauseButton.requestFocus(); + disableUnsupportedButtons(); + + if (mFromXml) { + setVisibility(View.VISIBLE); + } else { + int[] location = new int[2]; + + if (mAnchor != null) { + mAnchor.getLocationOnScreen(location); + Rect anchorRect = new Rect(location[0], location[1], + location[0] + mAnchor.getWidth(), location[1] + + mAnchor.getHeight()); + + mWindow.setAnimationStyle(mAnimStyle); + mRoot.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + mWindow.showAsDropDown(mAnchor, 0, -mRoot.getMeasuredHeight()); + } else { + Rect anchorRect = new Rect(location[0], location[1], + location[0] + mRoot.getWidth(), location[1] + + mRoot.getHeight()); + + mWindow.setAnimationStyle(mAnimStyle); + mWindow.showAtLocation(mRoot, Gravity.BOTTOM, + anchorRect.left, 0); + } + } + mShowing = true; + if (mShownListener != null) + mShownListener.onShown(); + } + updatePausePlay(); + mHandler.sendEmptyMessage(SHOW_PROGRESS); + + if (timeout != 0) { + mHandler.removeMessages(FADE_OUT); + mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_OUT), + timeout); + } + } + + @Override + public boolean isShowing() { + return mShowing; + } + + @Override + public void hide() { + if (mShowing) { + if (mAnchor != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + //mAnchor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + } + try { + mHandler.removeMessages(SHOW_PROGRESS); + if (mFromXml) + setVisibility(View.GONE); + else + mWindow.dismiss(); + } catch (IllegalArgumentException ex) { + Log.d(TAG, "MediaController already removed"); + } + mShowing = false; + if (mHiddenListener != null) + mHiddenListener.onHidden(); + } + } + + @Override + public void setEnabled(boolean enabled) { + if (mPauseButton != null) { + mPauseButton.setEnabled(enabled); + } + if (mFfwdButton != null) { + mFfwdButton.setEnabled(enabled); + } + if (mRewButton != null) { + mRewButton.setEnabled(enabled); + } + if (mProgress != null && !mDisableProgress) + mProgress.setEnabled(enabled); + disableUnsupportedButtons(); + super.setEnabled(enabled); + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/PlayConfigView.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/PlayConfigView.java new file mode 100644 index 0000000..bb46140 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/PlayConfigView.java @@ -0,0 +1,198 @@ +package com.qiniu.droid.niuplayer.widget; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.pili.pldroid.player.AVOptions; +import com.pili.pldroid.player.widget.PLVideoTextureView; +import com.qiniu.droid.niuplayer.R; +import com.qiniu.droid.niuplayer.utils.Config; + +import static com.qiniu.droid.niuplayer.utils.Utils.createAVOptions; + +public class PlayConfigView extends LinearLayout implements View.OnClickListener { + public static final String BACKSTAGE_PLAY_TAG = "backstage_play"; + + private PLVideoTextureView mVideoView; + private boolean mIsMirror; + private boolean mIsCache; + private boolean mIsBackstagePlay; + private int mRotation; + private TextView mCurSpeedText; + private TextView mCurDisplayText; + private TextView mCurSpeedTipText; + private TextView mSaveTip; + + public PlayConfigView(Context context) { + super(context); + } + + public PlayConfigView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + View view = LayoutInflater.from(context).inflate(R.layout.view_play_config, this); + ImageView imageView = view.findViewById(R.id.close_image); + imageView.setOnClickListener(this); + + ViewGroup viewGroup = view.findViewById(R.id.mirror_group); + viewGroup.setOnClickListener(this); + + viewGroup = view.findViewById(R.id.rotate_group); + viewGroup.setOnClickListener(this); + + viewGroup = view.findViewById(R.id.save_group); + viewGroup.setOnClickListener(this); + + viewGroup = view.findViewById(R.id.backstage_group); + viewGroup.setOnClickListener(this); + + mCurSpeedTipText = view.findViewById(R.id.speed_current_text); + + TextView textView = view.findViewById(R.id.render_default); + textView.setOnClickListener(this); + + mCurDisplayText = view.findViewById(R.id.render_full); + mCurDisplayText.setOnClickListener(this); + + textView = view.findViewById(R.id.render_16_9); + textView.setOnClickListener(this); + + textView = view.findViewById(R.id.render_4_3); + textView.setOnClickListener(this); + + textView = view.findViewById(R.id.speed_05); + textView.setOnClickListener(this); + + textView = view.findViewById(R.id.speed_075); + textView.setOnClickListener(this); + + mCurSpeedText = view.findViewById(R.id.speed_1); + mCurSpeedText.setOnClickListener(this); + + textView = view.findViewById(R.id.speed_125); + textView.setOnClickListener(this); + + textView = view.findViewById(R.id.speed_15); + textView.setOnClickListener(this); + + mSaveTip = view.findViewById(R.id.save_tip); + } + + public void setVideoView(PLVideoTextureView videoView) { + if (videoView.equals(mVideoView)) { + return; + } + mVideoView = videoView; + resetConfig(); + } + + private void resetConfig() { + mIsBackstagePlay = false; + mIsCache = false; + mIsMirror = false; + mRotation = 0; + + setPlayDisplayMode(PLVideoTextureView.ASPECT_RATIO_PAVED_PARENT, R.id.render_full); + setPlaySpeed(1f, R.id.speed_1); + setVisibility(GONE); + mVideoView.setMirror(mIsMirror); + mVideoView.setRotation(mRotation); + } + + @Override + public void onClick(View view) { + int viewId = view.getId(); + switch (viewId) { + case R.id.mirror_group: + mIsMirror = !mIsMirror; + mVideoView.setMirror(mIsMirror); + break; + case R.id.rotate_group: + mRotation += 90; + mVideoView.setRotation(mRotation); + break; + case R.id.save_group: + switchCacheEnable(); + break; + case R.id.backstage_group: + switchBackstagePlay(); + break; + case R.id.render_default: + setPlayDisplayMode(PLVideoTextureView.ASPECT_RATIO_ORIGIN, viewId); + break; + case R.id.render_full: + setPlayDisplayMode(PLVideoTextureView.ASPECT_RATIO_PAVED_PARENT, viewId); + break; + case R.id.render_16_9: + setPlayDisplayMode(PLVideoTextureView.ASPECT_RATIO_16_9, viewId); + break; + case R.id.render_4_3: + setPlayDisplayMode(PLVideoTextureView.ASPECT_RATIO_4_3, viewId); + break; + case R.id.speed_05: + setPlaySpeed(0.5f, viewId); + break; + case R.id.speed_075: + setPlaySpeed(0.75f, viewId); + break; + case R.id.speed_1: + setPlaySpeed(1f, viewId); + break; + case R.id.speed_125: + setPlaySpeed(1.25f, viewId); + break; + case R.id.speed_15: + setPlaySpeed(1.5f, viewId); + break; + case R.id.close_image: + setVisibility(GONE); + break; + } + } + + private void setPlayDisplayMode(int displayMode, int id) { + if (mCurDisplayText != null) { + mCurDisplayText.setTextColor(getResources().getColor(R.color.colorDefaultText)); + } + mCurDisplayText = findViewById(id); + mCurDisplayText.setTextColor(getResources().getColor(R.color.colorTextChoice)); + mVideoView.setDisplayAspectRatio(displayMode); + } + + private void setPlaySpeed(float speed, int id) { + if (mCurSpeedText != null) { + mCurSpeedText.setTextColor(getResources().getColor(R.color.colorDefaultText)); + } + mCurSpeedText = findViewById(id); + mCurSpeedText.setTextColor(getResources().getColor(R.color.colorTextChoice)); + mVideoView.setPlaySpeed(speed); + mCurSpeedTipText.setText(mCurSpeedText.getText()); + } + + private void switchCacheEnable() { + mIsCache = !mIsCache; + AVOptions options = createAVOptions(); + if (mIsCache) { + options.setString(AVOptions.KEY_CACHE_DIR, Config.DEFAULT_CACHE_DIR_PATH); + mSaveTip.setText("缓存已开"); + } else { + mSaveTip.setText("本地缓存"); + } + mVideoView.setAVOptions(options); + } + + private void switchBackstagePlay() { + mIsBackstagePlay = !mIsBackstagePlay; + Object tag = mIsBackstagePlay ? BACKSTAGE_PLAY_TAG : null; + String tip = mIsBackstagePlay ? "后台播放已经开启!" : "后台播放已经关闭!"; + mVideoView.setTag(tag); + Toast.makeText(this.getContext(), tip, Toast.LENGTH_LONG).show(); + } +} diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/ScrollEnableViewPager.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/ScrollEnableViewPager.java new file mode 100644 index 0000000..433d159 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/ScrollEnableViewPager.java @@ -0,0 +1,37 @@ +package com.qiniu.droid.niuplayer.widget; + +import android.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; + + +public class ScrollEnableViewPager extends ViewPager { + private boolean mScrollEnable = true; + + public ScrollEnableViewPager(Context context) { + super(context); + } + + public ScrollEnableViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setScrollEnable(boolean scrollEnable) { + mScrollEnable = scrollEnable; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return mScrollEnable && super.onTouchEvent(event); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mScrollEnable) { + return super.onInterceptTouchEvent(ev); + } else { + return false; + } + } +} \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/UpgradeDialog.java b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/UpgradeDialog.java new file mode 100755 index 0000000..88e5f22 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/java/com/qiniu/droid/niuplayer/widget/UpgradeDialog.java @@ -0,0 +1,55 @@ +package com.qiniu.droid.niuplayer.widget; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.text.Html; +import android.view.Display; +import android.view.WindowManager; + +import com.qiniu.droid.niuplayer.R; +import com.qiniu.droid.niuplayer.utils.DownloadService; + +public class UpgradeDialog { + + public static void show(final Context context, String content, final String downloadUrl) { + if (isContextValid(context)) { + AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.upgradeDialog); + builder.setTitle("发现新版本"); + builder.setMessage(Html.fromHtml(content)) + .setPositiveButton("立刻下载", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + goToDownload(context, downloadUrl); + } + }) + .setNegativeButton("以后再说", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + } + }); + + + AlertDialog dialog = builder.create(); + dialog.setCanceledOnTouchOutside(false); + dialog.show(); + + WindowManager m = ((Activity) context).getWindowManager(); + Display d = m.getDefaultDisplay(); + android.view.WindowManager.LayoutParams p = dialog.getWindow().getAttributes(); + p.width = (int) (d.getWidth() * 0.85); + dialog.getWindow().setAttributes(p); + } + } + + private static boolean isContextValid(Context context) { + return context instanceof Activity && !((Activity) context).isFinishing(); + } + + + private static void goToDownload(Context context, String downloadUrl) { + Intent intent = new Intent(context.getApplicationContext(), DownloadService.class); + intent.putExtra("url", downloadUrl); + context.startService(intent); + } +} diff --git a/NiuDroidPlayer/app/src/main/res/drawable-hdpi/thumbnail.png b/NiuDroidPlayer/app/src/main/res/drawable-hdpi/thumbnail.png new file mode 100644 index 0000000..31f093b Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-hdpi/thumbnail.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xhdpi/full_screen.png b/NiuDroidPlayer/app/src/main/res/drawable-xhdpi/full_screen.png new file mode 100644 index 0000000..0dc540f Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xhdpi/full_screen.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/back.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/back.png new file mode 100644 index 0000000..52959fc Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/back.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/background_play.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/background_play.png new file mode 100644 index 0000000..63af0f4 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/background_play.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/btn_brightness.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/btn_brightness.png new file mode 100755 index 0000000..4439e68 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/btn_brightness.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/close.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/close.png new file mode 100644 index 0000000..5e9a5ac Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/close.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/defualt_bg.xml b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/defualt_bg.xml new file mode 100644 index 0000000..d078e48 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/defualt_bg.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/dialog_bg.xml b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/dialog_bg.xml new file mode 100644 index 0000000..47f0c89 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/dialog_bg.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/dialog_edit_bg.xml b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/dialog_edit_bg.xml new file mode 100644 index 0000000..278c0cd --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/dialog_edit_bg.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/face_icon.jpg b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/face_icon.jpg new file mode 100644 index 0000000..f6d90dd Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/face_icon.jpg differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/failure.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/failure.png new file mode 100644 index 0000000..093d7b1 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/failure.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/go.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/go.png new file mode 100644 index 0000000..fca4df4 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/go.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/hks.jpg b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/hks.jpg new file mode 100644 index 0000000..28a357f Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/hks.jpg differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/launch_image.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/launch_image.png new file mode 100644 index 0000000..352e99d Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/launch_image.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/mirror_swtich.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/mirror_swtich.png new file mode 100644 index 0000000..1b12c15 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/mirror_swtich.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/more.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/more.png new file mode 100644 index 0000000..242809e Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/more.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/play.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/play.png new file mode 100644 index 0000000..2132ab1 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/play.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/play_player.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/play_player.png new file mode 100644 index 0000000..1848b86 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/play_player.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_back.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_back.png new file mode 100644 index 0000000..5d0cf1c Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_back.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_close.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_close.png new file mode 100644 index 0000000..9fa919f Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_close.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_stop.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_stop.png new file mode 100644 index 0000000..f59ee10 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/player_stop.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/rotate.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/rotate.png new file mode 100644 index 0000000..fc83e12 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/rotate.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/save.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/save.png new file mode 100644 index 0000000..868e077 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/save.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/scan.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/scan.png new file mode 100644 index 0000000..f0aa717 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/scan.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/screen_short.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/screen_short.png new file mode 100644 index 0000000..b95c52a Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/screen_short.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/stop.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/stop.png new file mode 100644 index 0000000..4729374 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/stop.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/tab_bottom_bg.xml b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/tab_bottom_bg.xml new file mode 100644 index 0000000..3f4cf07 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/tab_bottom_bg.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/top_title_bg.xml b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/top_title_bg.xml new file mode 100644 index 0000000..e89a31d --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/top_title_bg.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/url.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/url.png new file mode 100644 index 0000000..d2df8dd Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/url.png differ diff --git a/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/user.png b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/user.png new file mode 100644 index 0000000..79e4beb Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/drawable-xxhdpi/user.png differ diff --git a/NiuDroidPlayer/app/src/main/res/layout/activity_main.xml b/NiuDroidPlayer/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..e1b7dfb --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/activity_pl_video_texture.xml b/NiuDroidPlayer/app/src/main/res/layout/activity_pl_video_texture.xml new file mode 100644 index 0000000..75e8ba8 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/activity_pl_video_texture.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/activity_scan.xml b/NiuDroidPlayer/app/src/main/res/layout/activity_scan.xml new file mode 100644 index 0000000..49864de --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/activity_scan.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/dialog_common.xml b/NiuDroidPlayer/app/src/main/res/layout/dialog_common.xml new file mode 100644 index 0000000..fa920b5 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/dialog_common.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NiuDroidPlayer/app/src/main/res/layout/fragment_full_screen_video.xml b/NiuDroidPlayer/app/src/main/res/layout/fragment_full_screen_video.xml new file mode 100644 index 0000000..1571860 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/fragment_full_screen_video.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/fragment_live_video_list.xml b/NiuDroidPlayer/app/src/main/res/layout/fragment_live_video_list.xml new file mode 100644 index 0000000..5e46c36 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/fragment_live_video_list.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/fragment_movie_list.xml b/NiuDroidPlayer/app/src/main/res/layout/fragment_movie_list.xml new file mode 100644 index 0000000..b44cb60 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/fragment_movie_list.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/fragment_short_video_list.xml b/NiuDroidPlayer/app/src/main/res/layout/fragment_short_video_list.xml new file mode 100644 index 0000000..7ec6456 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/fragment_short_video_list.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/view_live_video.xml b/NiuDroidPlayer/app/src/main/res/layout/view_live_video.xml new file mode 100644 index 0000000..87410e0 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/view_live_video.xml @@ -0,0 +1,58 @@ + + + + ¬ + + + + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/view_media_controller.xml b/NiuDroidPlayer/app/src/main/res/layout/view_media_controller.xml new file mode 100644 index 0000000..2931f0d --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/view_media_controller.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/view_movie_video.xml b/NiuDroidPlayer/app/src/main/res/layout/view_movie_video.xml new file mode 100644 index 0000000..881511f --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/view_movie_video.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/view_play_config.xml b/NiuDroidPlayer/app/src/main/res/layout/view_play_config.xml new file mode 100644 index 0000000..a6cd07c --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/view_play_config.xml @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/layout/view_short_video.xml b/NiuDroidPlayer/app/src/main/res/layout/view_short_video.xml new file mode 100644 index 0000000..991d202 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/layout/view_short_video.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/mipmap-hdpi/ic_launcher.png b/NiuDroidPlayer/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/NiuDroidPlayer/app/src/main/res/mipmap-mdpi/ic_launcher.png b/NiuDroidPlayer/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/NiuDroidPlayer/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/NiuDroidPlayer/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/NiuDroidPlayer/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/NiuDroidPlayer/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/NiuDroidPlayer/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/NiuDroidPlayer/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/NiuDroidPlayer/app/src/main/res/mipmap-xxxhdpi/player_icon.png b/NiuDroidPlayer/app/src/main/res/mipmap-xxxhdpi/player_icon.png new file mode 100644 index 0000000..00811c8 Binary files /dev/null and b/NiuDroidPlayer/app/src/main/res/mipmap-xxxhdpi/player_icon.png differ diff --git a/NiuDroidPlayer/app/src/main/res/values-v21/styles.xml b/NiuDroidPlayer/app/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..dbbdd40 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,9 @@ + + + + diff --git a/NiuDroidPlayer/app/src/main/res/values-w820dp/dimens.xml b/NiuDroidPlayer/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..b9bcbd3 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,7 @@ + + + 64dp + 40dp + diff --git a/NiuDroidPlayer/app/src/main/res/values/colors.xml b/NiuDroidPlayer/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..b17ad40 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/values/colors.xml @@ -0,0 +1,16 @@ + + + #3F51B5 + #303F9F + #FF4081 + + #9B9B9B + #cccccc + #52ffffff + #52a7fe + #dfdfdf + #dfdfdf + #60000000 + #dff3f0 + #f4f4f4 + diff --git a/NiuDroidPlayer/app/src/main/res/values/dimens.xml b/NiuDroidPlayer/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..58fe034 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/values/dimens.xml @@ -0,0 +1,7 @@ + + + 16dp + 16dp + 16dp + 40dp + diff --git a/NiuDroidPlayer/app/src/main/res/values/strings.xml b/NiuDroidPlayer/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..be6e8e4 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/values/strings.xml @@ -0,0 +1,11 @@ + + 牛播放器 + + 二维码 + 相册 + 提示 + 取消 + 确定 + + + diff --git a/NiuDroidPlayer/app/src/main/res/values/styles.xml b/NiuDroidPlayer/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..89e27c4 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/values/styles.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + diff --git a/NiuDroidPlayer/app/src/main/res/xml/update_apk_paths.xml b/NiuDroidPlayer/app/src/main/res/xml/update_apk_paths.xml new file mode 100755 index 0000000..5722381 --- /dev/null +++ b/NiuDroidPlayer/app/src/main/res/xml/update_apk_paths.xml @@ -0,0 +1,4 @@ + + + + diff --git a/NiuDroidPlayer/build.gradle b/NiuDroidPlayer/build.gradle new file mode 100644 index 0000000..9c89fb3 --- /dev/null +++ b/NiuDroidPlayer/build.gradle @@ -0,0 +1,25 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:4.1.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + google() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/NiuDroidPlayer/gradle.properties b/NiuDroidPlayer/gradle.properties new file mode 100644 index 0000000..ded371d --- /dev/null +++ b/NiuDroidPlayer/gradle.properties @@ -0,0 +1,19 @@ +# 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. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# 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 +android.injected.testOnly=false diff --git a/NiuDroidPlayer/gradle/wrapper/gradle-wrapper.jar b/NiuDroidPlayer/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/NiuDroidPlayer/gradle/wrapper/gradle-wrapper.jar differ diff --git a/NiuDroidPlayer/gradle/wrapper/gradle-wrapper.properties b/NiuDroidPlayer/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..08b0aad --- /dev/null +++ b/NiuDroidPlayer/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon May 24 10:15:32 CST 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/NiuDroidPlayer/gradlew b/NiuDroidPlayer/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/NiuDroidPlayer/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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 +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# 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 + +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" ] ; 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 + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/NiuDroidPlayer/gradlew.bat b/NiuDroidPlayer/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/NiuDroidPlayer/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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 Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_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=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +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/NiuDroidPlayer/settings.gradle b/NiuDroidPlayer/settings.gradle new file mode 100644 index 0000000..9d495b3 --- /dev/null +++ b/NiuDroidPlayer/settings.gradle @@ -0,0 +1 @@ +include ':app' \ No newline at end of file diff --git a/PLDroidPlayerDemo/app/build.gradle b/PLDroidPlayerDemo/app/build.gradle index 24a9c93..4fce1fe 100644 --- a/PLDroidPlayerDemo/app/build.gradle +++ b/PLDroidPlayerDemo/app/build.gradle @@ -9,7 +9,7 @@ android { minSdkVersion 14 targetSdkVersion 27 versionCode 13 - versionName "2.2.0" + versionName "2.2.2" buildConfigField "long", "BUILD_TIMESTAMP", System.currentTimeMillis() + "L" } buildTypes { @@ -21,8 +21,9 @@ android { sourceSets { main { - if (!useLibrary) { - jniLibs.srcDirs = ['src/main/jniLibs'] + jniLibs.srcDirs += ['../../releases/openssl'] + if (useSo) { + jniLibs.srcDirs += ['../../releases/qplayer'] } } } @@ -30,10 +31,14 @@ android { dependencies { - if (useLibrary) { - implementation project(path: ':library') + if (!useJar) { + implementation project(path: ':PLDroidPlayer') } else { - implementation files('libs/pldroid-player-2.2.1.jar') + implementation files('../../releases/pldroid-player-2.2.2.jar') + } + + if (!useSo) { + implementation project(path: ':mediaEngine') } implementation 'com.android.support:appcompat-v7:27.1.0' diff --git a/PLDroidPlayerDemo/app/libs/pldroid-player-2.2.1.jar b/PLDroidPlayerDemo/app/libs/pldroid-player-2.2.1.jar deleted file mode 100644 index 27b864a..0000000 Binary files a/PLDroidPlayerDemo/app/libs/pldroid-player-2.2.1.jar and /dev/null differ diff --git a/PLDroidPlayerDemo/app/src/main/java/com/pili/pldroid/playerdemo/MainActivity.java b/PLDroidPlayerDemo/app/src/main/java/com/pili/pldroid/playerdemo/MainActivity.java index d8f631b..cc23313 100644 --- a/PLDroidPlayerDemo/app/src/main/java/com/pili/pldroid/playerdemo/MainActivity.java +++ b/PLDroidPlayerDemo/app/src/main/java/com/pili/pldroid/playerdemo/MainActivity.java @@ -31,12 +31,14 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final String DEFAULT_TEST_URL = "http://demo-videos.qnsdk.com/movies/qiniu.mp4"; +// private static final String DEFAULT_TEST_URL = "https://playback-cn.cloudlinks.cn/vas/playback/m3u8?ownerid=9223801851411955728&deviceid=429814557179925&startTime=1623910752&endTime=1623911216&sign=537a6c4b06b5e3e8cc3c091fbea01153726aa615"; private Spinner mActivitySpinner; private EditText mEditText; private RadioGroup mStreamingTypeRadioGroup; private RadioGroup mDecodeTypeRadioGroup; private CheckBox mVideoCacheCheckBox; + private CheckBox mVideoCacheFileNameEncodeCheckBox; private CheckBox mLoopCheckBox; private CheckBox mVideoDataCallback; private CheckBox mAudioDataCallback; @@ -75,6 +77,7 @@ protected void onCreate(Bundle savedInstanceState) { mActivitySpinner.setSelection(2); mVideoCacheCheckBox = findViewById(R.id.CacheCheckBox); + mVideoCacheFileNameEncodeCheckBox = findViewById(R.id.CacheFileNameEncodeCheckBox); mLoopCheckBox = findViewById(R.id.LoopCheckBox); mVideoDataCallback = findViewById(R.id.VideoCallback); mAudioDataCallback = findViewById(R.id.AudioCallback); @@ -207,6 +210,7 @@ public void jumpToPlayerActivity(String videoPath, boolean isList) { intent.putExtra("liveStreaming", 0); } intent.putExtra("cache", mVideoCacheCheckBox.isChecked()); + intent.putExtra("cache-filename-encode", mVideoCacheFileNameEncodeCheckBox.isChecked()); intent.putExtra("loop", mLoopCheckBox.isChecked()); intent.putExtra("video-data-callback", mVideoDataCallback.isChecked()); intent.putExtra("audio-data-callback", mAudioDataCallback.isChecked()); diff --git a/PLDroidPlayerDemo/app/src/main/java/com/pili/pldroid/playerdemo/PLVideoViewActivity.java b/PLDroidPlayerDemo/app/src/main/java/com/pili/pldroid/playerdemo/PLVideoViewActivity.java index ce87484..04172db 100644 --- a/PLDroidPlayerDemo/app/src/main/java/com/pili/pldroid/playerdemo/PLVideoViewActivity.java +++ b/PLDroidPlayerDemo/app/src/main/java/com/pili/pldroid/playerdemo/PLVideoViewActivity.java @@ -68,6 +68,9 @@ protected void onCreate(Bundle savedInstanceState) { if (!mIsLiveStreaming && cache) { options.setString(AVOptions.KEY_CACHE_DIR, Config.DEFAULT_CACHE_DIR); } + + boolean cache_filename_encode = getIntent().getBooleanExtra("cache-filename-encode", false); + options.setInteger(AVOptions.KEY_CACHE_FILE_NAME_ENCODE, cache_filename_encode ? 1: 0); boolean vcallback = getIntent().getBooleanExtra("video-data-callback", false); if (vcallback) { options.setInteger(AVOptions.KEY_VIDEO_DATA_CALLBACK, 1); @@ -260,7 +263,7 @@ public void onVideoSizeChanged(int width, int height) { private PLOnVideoFrameListener mOnVideoFrameListener = new PLOnVideoFrameListener() { @Override public void onVideoFrameAvailable(byte[] data, int size, int width, int height, int format, long ts) { - Log.i(TAG, "onVideoFrameAvailable: " + size + ", " + width + " x " + height + ", " + format + ", " + ts); + Log.i(TAG, "onVideoFrameAvailable: format=" + format + ", size=" + size + ", data-length="+ data.length + ", " + width + " x " + height + ", time=" + ts + " ,data=" + Arrays.toString(data)); if (format == PLOnVideoFrameListener.VIDEO_FORMAT_SEI && size > 0) { // If the RTMP stream is from Qiniu // Add &addtssei=true to the end of URL to enable SEI timestamp. @@ -279,8 +282,11 @@ public void onVideoFrameAvailable(byte[] data, int size, int width, int height, } while (data[index++] == (byte) 0xFF); String uuid = bytesToHex(Arrays.copyOfRange(data, index, index + 16)); int length = payloadSize - 16; - String content = new String(data, index + 16, length); - Log.i(TAG, " SEI data size:" + data.length + " content: " + content + " length = " + length); + int start_index = index + 16; + if (start_index >= 0 && length > 0 && (start_index + length) <= data.length) { + String content = new String(data, index + 16, length); + Log.i(TAG, " SEI data size:" + data.length + " content: " + content + " length = " + length); + } } } }; diff --git a/PLDroidPlayerDemo/app/src/main/jniLibs/arm64-v8a/libQPlayer.so b/PLDroidPlayerDemo/app/src/main/jniLibs/arm64-v8a/libQPlayer.so deleted file mode 100755 index 5d3edb8..0000000 Binary files a/PLDroidPlayerDemo/app/src/main/jniLibs/arm64-v8a/libQPlayer.so and /dev/null differ diff --git a/PLDroidPlayerDemo/app/src/main/jniLibs/x86/libQPlayer.so b/PLDroidPlayerDemo/app/src/main/jniLibs/x86/libQPlayer.so deleted file mode 100755 index 0c7d2ca..0000000 Binary files a/PLDroidPlayerDemo/app/src/main/jniLibs/x86/libQPlayer.so and /dev/null differ diff --git a/PLDroidPlayerDemo/app/src/main/res/layout/activity_main.xml b/PLDroidPlayerDemo/app/src/main/res/layout/activity_main.xml index a1b62bd..e2594fd 100644 --- a/PLDroidPlayerDemo/app/src/main/res/layout/activity_main.xml +++ b/PLDroidPlayerDemo/app/src/main/res/layout/activity_main.xml @@ -141,7 +141,18 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/cache" /> - + + + 自动
离线缓存(mp4点播) + 离线文件名加密 循环播放(点播) 视频数据回调 音频数据回调 diff --git a/PLDroidPlayerDemo/build.gradle b/PLDroidPlayerDemo/build.gradle index 79a7a99..d675aa0 100644 --- a/PLDroidPlayerDemo/build.gradle +++ b/PLDroidPlayerDemo/build.gradle @@ -26,5 +26,6 @@ task clean(type: Delete) { ext { - useLibrary = false + useJar = true + useSo = true } \ No newline at end of file diff --git a/PLDroidPlayerDemo/gradle.properties b/PLDroidPlayerDemo/gradle.properties index 1d3591c..a4904e9 100644 --- a/PLDroidPlayerDemo/gradle.properties +++ b/PLDroidPlayerDemo/gradle.properties @@ -15,4 +15,5 @@ # 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 \ No newline at end of file +# org.gradle.parallel=true +android.injected.testOnly=false \ No newline at end of file diff --git a/ReleaseNotes/release-notes-2.2.2.md b/ReleaseNotes/release-notes-2.2.2.md new file mode 100644 index 0000000..ada8870 --- /dev/null +++ b/ReleaseNotes/release-notes-2.2.2.md @@ -0,0 +1,19 @@ +# PLDroidPlayer Release Notes for 2.2.2 + +本次更新: + + +## 版本 + +- 发布 pldroid-player-2.2.2.jar +- 更新 libQPlayer.so + +- 新增缓存文件名加密开关,使用缓存功能时,存本地的文件名进行加密处理。 +- 新增H265编码的视频支持SEI数据回调。 +- 新增短视频demo: NiuDroidPlayer + +## 缺陷 + +- 修复SEI数据回调时,数组越界导致崩溃。 +- 修复视频流format改变时,且打开了视频数据回调,则有机率崩溃。 +- 修复视频流的NAL头格式不一致时,SEI数据没有回调。 \ No newline at end of file diff --git a/releases/arm64-v8a/libqcOpenSSL.so b/releases/arm64-v8a/libqcOpenSSL.so deleted file mode 100755 index 1813fbe..0000000 Binary files a/releases/arm64-v8a/libqcOpenSSL.so and /dev/null differ diff --git a/releases/armeabi-v7a/libQPlayer.so b/releases/armeabi-v7a/libQPlayer.so deleted file mode 100755 index b5192f1..0000000 Binary files a/releases/armeabi-v7a/libQPlayer.so and /dev/null differ diff --git a/releases/armeabi-v7a/libqcOpenSSL.so b/releases/armeabi-v7a/libqcOpenSSL.so deleted file mode 100755 index a073ab6..0000000 Binary files a/releases/armeabi-v7a/libqcOpenSSL.so and /dev/null differ diff --git a/PLDroidPlayerDemo/app/src/main/jniLibs/arm64-v8a/libqcOpenSSL.so b/releases/openssl/arm64-v8a/libqcOpenSSL.so similarity index 100% rename from PLDroidPlayerDemo/app/src/main/jniLibs/arm64-v8a/libqcOpenSSL.so rename to releases/openssl/arm64-v8a/libqcOpenSSL.so diff --git a/PLDroidPlayerDemo/app/src/main/jniLibs/armeabi-v7a/libqcOpenSSL.so b/releases/openssl/armeabi-v7a/libqcOpenSSL.so similarity index 100% rename from PLDroidPlayerDemo/app/src/main/jniLibs/armeabi-v7a/libqcOpenSSL.so rename to releases/openssl/armeabi-v7a/libqcOpenSSL.so diff --git a/PLDroidPlayerDemo/app/src/main/jniLibs/x86/libqcOpenSSL.so b/releases/openssl/x86/libqcOpenSSL.so similarity index 100% rename from PLDroidPlayerDemo/app/src/main/jniLibs/x86/libqcOpenSSL.so rename to releases/openssl/x86/libqcOpenSSL.so diff --git a/releases/pldroid-player-2.2.1.jar b/releases/pldroid-player-2.2.1.jar deleted file mode 100644 index 27b864a..0000000 Binary files a/releases/pldroid-player-2.2.1.jar and /dev/null differ diff --git a/releases/pldroid-player-2.2.2.jar b/releases/pldroid-player-2.2.2.jar new file mode 100644 index 0000000..d45358c Binary files /dev/null and b/releases/pldroid-player-2.2.2.jar differ diff --git a/releases/arm64-v8a/libQPlayer.so b/releases/qplayer/arm64-v8a/libQPlayer.so similarity index 67% rename from releases/arm64-v8a/libQPlayer.so rename to releases/qplayer/arm64-v8a/libQPlayer.so index 5d3edb8..6bbc8c3 100755 Binary files a/releases/arm64-v8a/libQPlayer.so and b/releases/qplayer/arm64-v8a/libQPlayer.so differ diff --git a/PLDroidPlayerDemo/app/src/main/jniLibs/armeabi-v7a/libQPlayer.so b/releases/qplayer/armeabi-v7a/libQPlayer.so similarity index 73% rename from PLDroidPlayerDemo/app/src/main/jniLibs/armeabi-v7a/libQPlayer.so rename to releases/qplayer/armeabi-v7a/libQPlayer.so index b5192f1..c95b6e2 100755 Binary files a/PLDroidPlayerDemo/app/src/main/jniLibs/armeabi-v7a/libQPlayer.so and b/releases/qplayer/armeabi-v7a/libQPlayer.so differ diff --git a/releases/x86/libQPlayer.so b/releases/qplayer/x86/libQPlayer.so similarity index 63% rename from releases/x86/libQPlayer.so rename to releases/qplayer/x86/libQPlayer.so index 0c7d2ca..b0081cd 100755 Binary files a/releases/x86/libQPlayer.so and b/releases/qplayer/x86/libQPlayer.so differ diff --git a/releases/x86/libqcOpenSSL.so b/releases/x86/libqcOpenSSL.so deleted file mode 100755 index 36a95a0..0000000 Binary files a/releases/x86/libqcOpenSSL.so and /dev/null differ