안녕하세요. 낙타 2020입니다.
요즘 Youtube를 이용한 앱이 많이 출시되고 있습니다.
부득이하게 Youtube Api를 사용하게 되는데요.
워낙 오래된 구식 Api이다 보니 androidx 에 대한 지원이나 원활하게 사용하기가 쉽지 않습니다.
그래서 그걸 대체할 만한 라이브러리를 이용해서 Youtube Player를 만들어보겠습니다.
먼저 라이브러리를 다운받습니다.
https://github.com/PierfrancescoSoffritti/android-youtube-player
PierfrancescoSoffritti/android-youtube-player
YouTube Player library for Android and Chromecast, stable and customizable. - PierfrancescoSoffritti/android-youtube-player
github.com
라이브러리를 다운받으면 chromecast,core 버전으로 나누어져 있습니다.
이걸 샘플 그대로 쓰셔도 되지만 저희 입맛에 맞게 쓸 수 있게 조금 수정해 보겠습니다.
프로젝트를 하나 생성합니다. Empty Activity로요.
그리고 youtubetest 프로젝트로 생성합니다.
프로젝트를 생성하고 난후 github에서 다운받은 core와 셋팅 파일들을 붙여줍니다.
그리고 build.grade 프로젝트 부분에 아래 내용으로 복사해주세요.
내용은 dependencies 부분하고 kotlin 사용 라이브러리 추가가 되겠습니다.
apply from: './dependencies.gradle'
buildscript {
apply from: './dependencies.gradle'
repositories {
jcenter()
google()
}
dependencies {
classpath "com.android.tools.build:gradle:${versions.gradlePlugin}"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
classpath "com.novoda:bintray-release:$versions.bintrayPlugin"
}
}
allprojects {
repositories {
jcenter()
google()
}
tasks.withType(Javadoc).all {
enabled = false
}
}
app단 build.grade도 수정해 줍니다.
apply plugin: 'com.android.application'
apply from: '../dependencies.gradle'
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.compileSdk
versionCode versions.publishVersionCode_core
versionName versions.publishVersion_core
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// this is needed because I'm importing 2 modules called "core" (core and com.psoffritti.librarysampleapptemplate:core) (https://discuss.kotlinlang.org/t/disable-meta-inf-generation-in-gradle-android-project/3830)
packagingOptions {
exclude 'META-INF/core_release.kotlin_module'
}
}
dependencies {
testImplementation "junit:junit:$versions.junit"
androidTestImplementation "androidx.test:runner:$versions.runner"
androidTestImplementation "androidx.test.espresso:espresso-core:$versions.espressoCore"
implementation project(':core')
implementation "androidx.appcompat:appcompat:$versions.androidxCore"
implementation "androidx.recyclerview:recyclerview:$versions.androidxRecyclerView"
implementation "androidx.constraintlayout:constraintlayout:$versions.androidxConstraintLayout"
implementation "com.google.android.material:material:$versions.androidxMaterial"
implementation "androidx.mediarouter:mediarouter:$versions.androidxMediarouter"
implementation "io.reactivex.rxjava2:rxjava:$versions.rxJava"
implementation "io.reactivex.rxjava2:rxandroid:$versions.rxAndroid"
implementation("com.google.api-client:google-api-client-android:$versions.googleApiClientAndroid") {
exclude group: 'com.google.guava'
exclude group: 'org.apache.httpcomponents'
exclude group: 'com.google.code.findbugs'
exclude group: 'com.google.http-client', module: 'google-http-client-jackson2'
}
implementation("com.google.apis:google-api-services-youtube:$versions.googleApiServicesYoutube") {
exclude group: 'com.google.guava'
exclude group: 'org.apache.httpcomponents'
exclude group: 'com.google.code.findbugs'
exclude group: 'com.google.http-client', module: 'google-http-client-jackson2'
}
implementation "com.psoffritti.librarysampleapptemplate:core:$versions.sampleAppTemplate"
}
/*
configurations {
custom
}
dependencies {
custom 'com.google.apis:google-api-services-youtube:v3-rev198-1.23.0'
}
*/
그리고 소스부분에는 github 모듈에서 utils 부분을 추가해줘야 합니다.
utils에는 FullScreenHelper와 NetworkUtils, VideosProvider, VideoInfo, YouTubeDataEndpoint 가 포함되어 있습니다.
마지막으로 MainActivity.java 부분입니다. Listener부분과 재생관련 함수들이 포함되어 있습니다.
package com.apprichkorea.myapplication;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import android.annotation.SuppressLint;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.Toast;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.YouTubePlayerUtils;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerFullScreenListener;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.ui.PlayerUiController;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.ui.menu.MenuItem;
import com.apprichkorea.myapplication.utils.VideoIdsProvider;
import com.apprichkorea.myapplication.utils.VideoInfo;
import com.apprichkorea.myapplication.utils.FullScreenHelper;
import com.apprichkorea.myapplication.utils.YouTubeDataEndpoint;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class MainActivity extends AppCompatActivity {
private YouTubePlayerView youTubePlayerView;
private FullScreenHelper fullScreenHelper = new FullScreenHelper(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
youTubePlayerView = findViewById(R.id.youtube_player_view);
initYouTubePlayerView();
}
@Override
public void onConfigurationChanged(Configuration newConfiguration) {
super.onConfigurationChanged(newConfiguration);
youTubePlayerView.getPlayerUiController().getMenu().dismiss();
}
@Override
public void onBackPressed() {
if (youTubePlayerView.isFullScreen())
youTubePlayerView.exitFullScreen();
else
super.onBackPressed();
}
private void initYouTubePlayerView() {
initPlayerMenu();
// The player will automatically release itself when the activity is destroyed.
// The player will automatically pause when the activity is stopped
// If you don't add YouTubePlayerView as a lifecycle observer, you will have to release it manually.
getLifecycle().addObserver(youTubePlayerView);
youTubePlayerView.addYouTubePlayerListener(new AbstractYouTubePlayerListener() {
@Override
public void onReady(@NonNull YouTubePlayer youTubePlayer) {
YouTubePlayerUtils.loadOrCueVideo(
youTubePlayer,
getLifecycle(),
VideoIdsProvider.getNextVideoId(),
0f
);
addFullScreenListenerToPlayer();
setPlayNextVideoButtonClickListener(youTubePlayer);
}
@Override
public void onStateChange(@NonNull YouTubePlayer youTubePlayer,@NonNull PlayerConstants.PlayerState state) {
if(state.toString() == "ENDED") {
YouTubePlayerUtils.loadOrCueVideo(
youTubePlayer,
getLifecycle(),
VideoIdsProvider.getNextVideoId(),
0f
);
addFullScreenListenerToPlayer();
setPlayNextVideoButtonClickListener(youTubePlayer);
}
}
});
}
/**
* Shows the menu button in the player and adds an item to it.
*/
private void initPlayerMenu() {
youTubePlayerView.getPlayerUiController()
.showMenuButton(true)
.getMenu()
.addItem(new MenuItem("menu item1", R.drawable.ic_android_black_24dp,
view -> Toast.makeText(this, "item1 clicked", Toast.LENGTH_SHORT).show())
).addItem(new MenuItem("menu item2", R.drawable.ic_mood_black_24dp,
view -> Toast.makeText(this, "item2 clicked", Toast.LENGTH_SHORT).show())
).addItem(new MenuItem("menu item no icon",
view -> Toast.makeText(this, "item no icon clicked", Toast.LENGTH_SHORT).show()));
}
private void addFullScreenListenerToPlayer() {
youTubePlayerView.addFullScreenListener(new YouTubePlayerFullScreenListener() {
@Override
public void onYouTubePlayerEnterFullScreen() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
fullScreenHelper.enterFullScreen();
addCustomActionsToPlayer();
}
@Override
public void onYouTubePlayerExitFullScreen() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
fullScreenHelper.exitFullScreen();
removeCustomActionsFromPlayer();
}
});
}
/**
* This method adds a new custom action to the player.
* Custom actions are shown next to the Play/Pause button in the middle of the player.
*/
private void addCustomActionsToPlayer() {
Drawable customAction1Icon = ContextCompat.getDrawable(this, R.drawable.ic_fast_rewind_white_24dp);
Drawable customAction2Icon = ContextCompat.getDrawable(this, R.drawable.ic_fast_forward_white_24dp);
assert customAction1Icon != null;
assert customAction2Icon != null;
youTubePlayerView.getPlayerUiController().setCustomAction1(customAction1Icon, view ->
Toast.makeText(this, "custom action1 clicked", Toast.LENGTH_SHORT).show());
youTubePlayerView.getPlayerUiController().setCustomAction2(customAction2Icon, view ->
Toast.makeText(this, "custom action1 clicked", Toast.LENGTH_SHORT).show());
}
private void removeCustomActionsFromPlayer() {
youTubePlayerView.getPlayerUiController().showCustomAction1(false);
youTubePlayerView.getPlayerUiController().showCustomAction2(false);
}
/**
* Set a click listener on the "Play next video" button
*/
private void setPlayNextVideoButtonClickListener(final YouTubePlayer youTubePlayer) {
Button playNextVideoButton = findViewById(R.id.next_video_button);
playNextVideoButton.setOnClickListener(view ->
YouTubePlayerUtils.loadOrCueVideo(
youTubePlayer, getLifecycle(),
VideoIdsProvider.getNextVideoId(),0f
));
// youTubePlayer.play();
}
/**
* This method is here just for reference, it is not being used because the IFrame player already shows the title of the video.
*
* This method is called every time a new video is being loaded/cued.
* It uses the YouTube Data APIs to fetch the video title from the video ID.
* The YouTube Data APIs are nothing more then a wrapper over the YouTube REST API.
* You can learn more at the following urls:
* https://developers.google.com/youtube/v3/docs/videos/list
* https://developers.google.com/apis-explorer/#p/youtube/v3/youtube.videos.list?part=snippet&id=6JYIGclVQdw&fields=items(snippet(title))&_h=9&
*
* This method does network operations, therefore it cannot be executed on the main thread.
* For simplicity I have used RxJava to implement the asynchronous logic. You can use whatever you want: Threads, AsyncTask ecc.
*/
@SuppressLint("CheckResult")
private void setVideoTitle(PlayerUiController playerUiController, String videoId) {
Single<VideoInfo> observable = YouTubeDataEndpoint.getVideoInfoFromYouTubeDataAPIs(videoId);
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
videoInfo -> playerUiController.setVideoTitle(videoInfo.getVideoTitle()),
error -> { Log.e(getClass().getSimpleName(), "Can't retrieve video title, are you connected to the internet?"); }
);
}
}
위와같이 작업을 하고 실행을 시켜보면 유튜브 화면과 함께 랜덤 플레이 버튼이 나옵니다.
버튼을 클릭하면 랜덤으로 다음 유튜브 화면이 재생이 됩니다.
수정을 하실려면 리스너 부분이나 재생관련 부분 함수를 수정해서 쓰시면 원하시는 형태의 Youtube Player로 사용하실수 있습니다.
그럼 수고하세요.
'안드로이드개발' 카테고리의 다른 글
안드로이드 Unity Ads 적용하기 (0) | 2020.09.14 |
---|---|
안드로이드 Navigation Drawer with Tabs (2/2) (0) | 2020.08.28 |
안드로이드 Navigation Drawer with Tabs (1/2) (0) | 2020.08.28 |