How to play youtube video on top of other fragment in android - android

I am trying to play youTube video's within an existing fragment on runtime using other fragment. I try to follow the fragment within other fragment. But I am not able to see videos only audio is playing with the previous fragment view. Please help me out. I queried about 200 pages but nothing worked for me. Any help will be appreciated.
Here is my my_fragment.xml code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentOpponents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:keepScreenOn="true">
<android.support.v7.widget.RecyclerView
android:id="#+id/opponents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:numColumns="3" />
<FrameLayout
android:id="#+id/child_fragment"
android:layout_width="250dp"
android:layout_height="250dp">
</FrameLayout>
</RelativeLayout>
Code for YoutubeFragment:
public class YoutubeFragment extends YouTubePlayerSupportFragment implements YouTubePlayer.OnInitializedListener {
private static final int RECOVERY_DIALOG_REQUEST = 1;
private static final String KEY_VIDEO_ID = "KEY_VIDEO_ID";
private String mVideoId;
//Empty constructor
public YoutubeFragment() {
}
/**
* Returns a new instance of this Fragment
*
* #param videoId The ID of the video to play
*/
public static YoutubeFragment newInstance(final String videoId) {
final YoutubeFragment youTubeFragment = new YoutubeFragment();
final Bundle bundle = new Bundle();
bundle.putString(KEY_VIDEO_ID, videoId);
youTubeFragment.setArguments(bundle);
return youTubeFragment;
}
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
final Bundle arguments = getArguments();
if (bundle != null && bundle.containsKey(KEY_VIDEO_ID)) {
mVideoId = bundle.getString(KEY_VIDEO_ID);
} else if (arguments != null && arguments.containsKey(KEY_VIDEO_ID)) {
mVideoId = arguments.getString(KEY_VIDEO_ID);
}
initialize(Consts.DEVELOPER_KEY, this);
}
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer, boolean restored) {
if (mVideoId != null) {
if (restored) {
youTubePlayer.play();
} else {
youTubePlayer.loadVideo(mVideoId);
}
}
}
#Override
public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult youTubeInitializationResult) {
if (youTubeInitializationResult.isUserRecoverableError()) {
youTubeInitializationResult.getErrorDialog(getActivity(), RECOVERY_DIALOG_REQUEST).show();
} else {
//Handle the failure
Toast.makeText(getActivity(), R.string.error_init_failure, Toast.LENGTH_LONG).show();
}
}
#Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putString(KEY_VIDEO_ID, mVideoId);
}
}
I am using this code to call a fragment on runtime,
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = YoutubeFragment.newInstance("q8_o4W9ZyZk");
fragmentTransaction.add(R.id.child_fragment, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

As you are using nested fragments, use getChildFragmentManger() method instead getSupportFragmentManager
Here and here there is more info
Hope it helps!

Related

initialize youtube player fragment within a fragment

I am trying to initialize youtubeplayer fragment within a fragment. I successfully implemented the fragment within an activity but getting problem to initialize it in a fragment.
the code snippet for activity is as below
public class MainActivity extends AppCompatActivity {
private YouTubePlayerSupportFragmentX youTubePlayerFragment;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeYoutubePlayer();
private void initializeYoutubePlayer() {
youTubePlayerFragment = (YouTubePlayerSupportFragmentX) getSupportFragmentManager().findFragmentById(R.id.youtube_player_fragment);
if (youTubePlayerFragment == null)
return;
youTubePlayerFragment.initialize(Constants.DEVELOPER_KEY, new YouTubePlayer.OnInitializedListener() {
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player,
boolean wasRestored) {
if (!wasRestored) {
youTubePlayer = player;
//set the player style default
youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.DEFAULT);
//cue the 1st video by default
youTubePlayer.cueVideo(youtubeVideoArrayList.get(0));
youTubePlayer.addFullscreenControlFlag(YouTubePlayer.FULLSCREEN_FLAG_ALWAYS_FULLSCREEN_IN_LANDSCAPE);
}
}
#Override
public void onInitializationFailure(YouTubePlayer.Provider arg0, YouTubeInitializationResult arg1) {
//print or show error if initialization failed
Log.e(TAG, "Youtube Player View initialization failed");
}
});
}
To convert this code into fragment, I tried this
public class Welcome extends Fragment {
private YouTubePlayerSupportFragmentX youTubePlayerFragment;
private YouTubePlayer youTubePlayer;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_welcome, container, false);
initializeYoutubePlayer();
private void initializeYoutubePlayer() {
youTubePlayerFragment = (YouTubePlayerSupportFragmentX) getSupportFragmentManager().findFragmentById(R.id.youtube_player_fragment);
if (youTubePlayerFragment == null)
return;
youTubePlayerFragment.initialize(Constants.DEVELOPER_KEY, new YouTubePlayer.OnInitializedListener() {
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player, boolean wasRestored) {
if (!wasRestored) {
youTubePlayer = player;
//set the player style default
youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.DEFAULT);
//cue the 1st video by default
youTubePlayer.cueVideo(youtubeVideoArrayList.get(0));
youTubePlayer.addFullscreenControlFlag(YouTubePlayer.FULLSCREEN_FLAG_ALWAYS_FULLSCREEN_IN_LANDSCAPE);
}
}
#Override
public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult youTubeInitializationResult) {
Log.e(TAG, "Youtube Player View initialization failed");
}
});
}
private void getSupportFragmentManager() {
}
}
but getting error in finding the fragment layout. Can somebody guide me how to do this in fragment? The layout for Welcome fragment is as follows
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FCFDFF"
tools:context="com.currentmedia.channelslayout.Welcome">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="32dp"
android:layout_alignParentTop="true"
android:layout_marginTop="0dp"
android:background="#FF0000"
app:title="Punjabi News Live"
app:titleTextColor="#FFFFFF" />
<!-- Youtube Player Fragment -->
<LinearLayout
android:id="#+id/linearlayout01"
android:layout_width="fill_parent"
android:layout_height="220dp"
android:layout_below="#id/adView"
android:layout_marginTop="2dp"
android:background="#ccc"
android:orientation="vertical">
<fragment
android:id="#+id/youtube_player_fragment"
android:name="com.google.android.youtube.player.YouTubePlayerSupportFragmentX"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
In your layout, instead of using a < fragment > you should use a < FragmentContainerView > and replace the fragment in your activity using transaction.
Here is a tutorial if you're new with manipulating fragment in 2020:
FragmentContainerView explanation

YouTubePlayerSupportFragment inside fragment

I have a fragment and I want to play YouTube video inside fragment. For this I have follow the following steps:
Created project on Google console
Generate Key
Download and install YouTubeAndroidPlayerApi.
Create a simple fragment
Add YouTubePlayerSupportFragment
Write the code in java file
When I run App, got an error message:
Caused by: android.view.InflateException: Binary XML file line #10: Error inflating class com.google.android.youtube.player.YouTubePlayerView
Caused by: java.lang.IllegalStateException: A YouTubePlayerView can only be created with an Activity which extends YouTubeBaseActivity as its context.
Code of XML file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.youtube.player.YouTubePlayerView
android:name="com.google.android.youtube.player.
YouTubePlayerSupportFragment"
android:id="#+id/YoutubePlayer"
android:layout_width="match_parent"
android:layout_height="240dp"
android:layout_marginTop="20dp"/>
</LinearLayout>
Code of Java File
public class TopicYoutubeFragment extends Fragment implements YouTubePlayer.OnInitializedListener {
private YouTubePlayer youtubeplayer;
private static final String YoutubeDeveloperKey = "AIzaSyAp8uMTh0VnYI";
private String YoutubeURL, youtubeID;
private String TopicName;
private static final int RECOVERY_REQUEST = 1;
private YouTubePlayerView ypv;
public TopicYoutubeFragment() {
}
public static TopicYoutubeFragment newInstance(String YoutubeURL, String TopicName) {
TopicYoutubeFragment fragment = new TopicYoutubeFragment();
Bundle args = new Bundle();
args.putString("YoutubeURL", YoutubeURL);
args.putString("TopicName", TopicName);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
YoutubeURL = getArguments().getString(YoutubeURL);
TopicName = getArguments().getString(TopicName);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.topicyoutubefragment, container, false);
ypv = view.findViewById(R.id.YoutubePlayer);
ypv.initialize(YoutubeDeveloperKey, this);
youtubeID = "F7xdbsjFGtU";
return view;
}
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer, boolean b) {
if (!b) {
if (youtubeID != null)
youTubePlayer.cueVideo(youtubeID); // Plays https://www.youtube.com/watch?v=F7xdbsjFGtU
else
Toast.makeText(getActivity(), "No Video is available for this Topic", Toast.LENGTH_LONG).show();
}
}
#Override
public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult errorReason) {
if (errorReason.isUserRecoverableError()) {
errorReason.getErrorDialog(getActivity(), RECOVERY_REQUEST).show();
} else {
String error = String.format(getString(R.string.player_error), errorReason.toString());
Toast.makeText(getActivity(), error, Toast.LENGTH_LONG).show();
}
}
}

How can I retrieve an instance of my fragment class so I can call a method on it?

I am learning how to use fragments, and in my MainActivity.java I have this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
A a = new A();
B b = new B(a);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new ContentFragment())
.commit();
ContentFragment frag = (ContentFragment) getSupportFragmentManager()
.findFragmentById(R.id.container);
frag.doInject(b);
}
}
If I try to inject the dependency directly into the constructor of the fragment, Android Studio barks at me. So I'm trying this approach, but when debugging I get to where I am setting frag, and it's just null and is always null. So, I guess what I need to know is, in the simplest way possible, how do I call my doInject(b) and inject b into my fragment?
Another thing too, which I'm not sure about, is that later if I have a listener in the same MainActivity.java file, and I want to update the fragment with new content, I was hoping to do something like this:
#Override
public void handleResponseData(String s) {
frag.updateTextbox(s);
}
But I have a feeling that I'm going to run into problems with that too. So I need some help understanding what it is that I'm trying to do. I've spent at least 5 hours trying to figure this out, and need some help, please!
Edit to show layout files:
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:ignore="MergeRootFrame" />
fragment_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextView
android:id="#+id/textbox"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello World" />
</FrameLayout>
If I try to inject the dependency directly into the constructor of the fragment
I'm not sure about that doInject method, but you should read Best practice for instantiating a new Android Fragment
For example,
ContentFragment frag = ContentFragment.newInstance(b);
getSupportFragmentManager().beginTransaction()
.add(R.id.container, frag, "TAG")
.commit();
// frag.doInject(b); // Can do, but not necessary
later if I have a listener in the same MainActivity.java file, and I want to update the fragment with new content, I was hoping to do something like this
Generally, this is not how you do that.
frag.updateTextbox(s);
I assume you have the Activity attached to the Fragment via a listener with a handleResponseData(String s), and you'd have to do
String s = "foo";
if (listener != null) {
listener.handleResponseData(s);
}
therefore, instead of that, you only need do this
String s = "foo";
updateTextbox(s);
/*
if (listener != null) {
listener.handleResponseData(s);
}
*/
instead of new ContentFragment() . create the fragment and give a tag for that.
Sample Example :
if (savedInstanceState == null) {
ContentFragment fragment = new ContentFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.container, fragment,"MyFragTag")
.commit();
ContentFragment frag = (ContentFragment) getSupportFragmentManager()
.findFragmentByTag("MyFragTag");
frag.doInject(b);
}
(This is the most common way to do it) Imagine this is your fragment :
public class FactoryMethodFragment extends Fragment {
public static final String ARG_MY_VALUE = "any value";
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Bundle arguments = getArguments();
String myValue = "empty";
if (arguments != null) {
myValue = arguments
.getString(ARG_MY_VALUE);
}
Toast.makeText(getContext(), myValue,
Toast.LENGTH_SHORT).show();
}
public static FactoryMethodFragment getInstance(String myValue){
FactoryMethodFragment factoryMethodFragment
= new FactoryMethodFragment();
Bundle arguments = new Bundle();
arguments.putString(ARG_MY_VALUE, myValue);
factoryMethodFragment.setArguments(arguments);
return factoryMethodFragment;
}
}
You have to create a factory method with arguments as shown in my example and use it like this :
getSupportFragmentManager().beginTransaction()
.add(R.id.container, FactoryMethodFragment.getInstance("myArgument"))
.commit();
And for the comunication between fragment and activity normally we use an interface as the following :
public class FactoryMethodFragment extends Fragment {
public static final String ARG_MY_VALUE = "any value";
public interface CommunicationChannel {
void onAction1(String argument);
void onAction2(String argument);
}
private CommunicationChannel mCallback;
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Bundle arguments = getArguments();
String myValue = "empty";
if (arguments != null) {
myValue = arguments
.getString(ARG_MY_VALUE);
}
Toast.makeText(getContext(), myValue,
Toast.LENGTH_SHORT).show();
//Here we will send data to the activity
mCallback.onAction1("data sent to activity");
mCallback.onAction2("Another data sent to activity");
}
public static FactoryMethodFragment getInstance(String myValue){
FactoryMethodFragment factoryMethodFragment
= new FactoryMethodFragment();
Bundle arguments = new Bundle();
arguments.putString(ARG_MY_VALUE, myValue);
factoryMethodFragment.setArguments(arguments);
return factoryMethodFragment;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Activity) {
mCallback = (CommunicationChannel) context;
}
}
}
And you implement it on your activity like this :
public class FatoryMethodActivity extends AppCompatActivity implements FactoryMethodFragment.CommunicationChannel {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.id.yourlayout);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, FactoryMethodFragment.getInstance("myArgument"))
.commit();
}
}
#Override
public void onAction1(String argument) {
//HERE YOU GET DATA FROM YOUR FRAGMENT
}
#Override
public void onAction2(String argument) {
//HERE YOU GET DATA FROM YOUR FRAGMENT
}
}
Thanks to everyone here I stumbled my way through this issue, and even though I've probably got some mistakes, I thought I'd post what I've got in case it might help somebody else.
In MainActivity.java:
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState == null) {
frag = ContentFragment.getInstance(request);
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, frag).commit();
}
}
In ContentFragment.java:
private PostRequest request;
private static ContentFragment myFragment = null;
public static ContentFragment getInstance(PostRequest request){
if( myFragment == null ){
myFragment = new ContentFragment();
myFragment.request = request;
}
return myFragment;
}
I've only been working with Android/Java for about 10 days, and for me it's a lot to take in. Anyways, thanks for the help!

java.lang.IllegalStateException: YouTubeServiceEntity not initialized error when using YouTubePlayerApi

I'm using YouTubePlayerAPI and YouTubePlayerSupportFragment in my app and I'm getting the following error, but I couldn't find out what is causing it. I've been looking for information but I haven't found anything useful.
java.lang.IllegalStateException: YouTubeServiceEntity not initialized
at android.os.Parcel.readException(Parcel.java:1433)
at android.os.Parcel.readException(Parcel.java:1379)
at com.google.android.youtube.player.internal.l$a$a.a(Unknown Source)
at com.google.android.youtube.player.internal.o.a(Unknown Source)
at com.google.android.youtube.player.internal.ad.a(Unknown Source)
at com.google.android.youtube.player.YouTubePlayerView.a(Unknown Source)
at com.google.android.youtube.player.YouTubePlayerView$1.a(Unknown Source)
at com.google.android.youtube.player.internal.r.g(Unknown Source)
at com.google.android.youtube.player.internal.r$c.a(Unknown Source)
at com.google.android.youtube.player.internal.r$b.a(Unknown Source)
at com.google.android.youtube.player.internal.r$a.handleMessage(Unknown Source)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5041)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
In the stackstrace there isn't any line number pointing to any of my classes or activities.
Any idea of it?
Thanks!
EDIT
My custom YoutubePlayerFragment Class: YouTubeVideoPlayerFragment.java
public class YouTubeVideoPlayerFragment extends YouTubePlayerSupportFragment {
private static final String ARG_URL = "url";
// ===========================================================
// Constructors
// ===========================================================
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public YouTubeVideoPlayerFragment() {
}
/**
* Factory method to generate a new instance of the fragment given a video URL.
*
* #param url The video url this fragment represents
* #return A new instance of this fragment with itemId extras
*/
public static YouTubeVideoPlayerFragment newInstance(String url) {
final YouTubeVideoPlayerFragment mFragment = new YouTubeVideoPlayerFragment();
// Set up extras
final Bundle args = new Bundle();
args.putString(ARG_URL, url);
mFragment.setArguments(args);
// Initialize YouTubePlayer
mFragment.init();
return mFragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
private void init(){
initialize(Constants.API_KEY, new YouTubePlayer.OnInitializedListener() {
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer, boolean wasRestored) {
if (!wasRestored) {
youTubePlayer.cueVideo(getArguments().getString(ARG_URL));
youTubePlayer.setShowFullscreenButton(false);
}
}
}
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="#color/black" >
<!-- For YoutubeFragment -->
<FrameLayout
android:id="#+id/youtube_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
calling method:
// Create a new instance of YouTubeVideoPlayerFragment providing video id
// and place it in the corresponding FrameLayout
final YouTubeVideoPlayerFragment youTubeVideoPlayerFragment = YouTubeVideoPlayerFragment.newInstance(VIDEO_ID);
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.replace(R.id.youtube_fragment, youTubeVideoPlayerFragment);
ft.commit();
EDIT
I've found out the origin of that error. This is the scenario:
The activity starts. In onCreate() it instantiates a new YouTubeVideoPlayerFragment and initializes YouTube object (which starts the YouTubeServiceEntity internally) in its newInstance() method. Then the YouTube fragment that was instantiated before, is attached with FragmentManager to the corresponding FrameLayout while video is loading.
Here is the issue: If user exits the activity before video had been loaded, the exception is thrown.
So if user want to exit from the activity in that case, what should I do and how? I don't really know what to do!
Once again, do NOT use fragment constructors or factory methods to work with lifecycle or context bound entities. Simply put, such entities can only be used after super.onCreate(...) has been called.
The question now is, when to call the init method?
Here's what YouTubePlayerFragment documentation says:
The YouTubePlayer associated with this fragment will be released whenever its onDestroyView() method is called. You will therefore have to re-call initialize(String, YouTubePlayer.OnInitializedListener) whenever the activity associated with this fragment is recreated, even if the fragment instance is retained across activity re-creation by setting setRetainInstance(boolean).
You may be tempted to put init() in onActivityCreated but that's too late, since onStart was already called and layout already performed.
Counterpart to onDestroyView is onViewCreated and that's the perfect candidate.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
init();
}
As suggested call setRetainInstance(true) in the fragment's constructor. When the activity is recreated the fragment will not be recreated, only its UI will go through lifecycle events.
I see the same error reported from a Huawei cellphone.
I see an explanation here:
https://github.com/youtube/yt-android-player/issues/23
Not sure if there is a way to catch the exception in our code.
The problem is the initialization of the Youtube fragment. YouTubePlayerSupportFragment has to be extended in a class of yours and overrides some methods. You have to control the screen orientation and the onSaveInstanceState.
public class YouTubePlayerFragment extends YouTubePlayerSupportFragment {
private YouTubePlayer mPlayer;
public static YouTubePlayerFragment newInstance() {
return new YouTubePlayerFragment();
}
#Override public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setRetainInstance(true);
}
#Override
public void initialize(String s, YouTubePlayer.OnInitializedListener onInitializedListener) {
super.initialize(s, new YouTubePlayer.OnInitializedListener() {
#Override public void onInitializationSuccess(YouTubePlayer.Provider provider,
YouTubePlayer youTubePlayer, boolean b) {
mPlayer = youTubePlayer;
onInitializedListener.onInitializationSuccess(provider, youTubePlayer, b);
}
#Override public void onInitializationFailure(YouTubePlayer.Provider provider,
YouTubeInitializationResult youTubeInitializationResult) {
onInitializedListener.onInitializationFailure(provider, youTubeInitializationResult);
}
});
}
#Override public void onDestroyView() {
if (mPlayer != null) {
mPlayer.release();
}
super.onDestroyView();
}
public YouTubePlayer getPlayer() {
return mPlayer;
}
}
YoutubeFragment.class
public class YoutubeFragment extends Fragment {
private static final String EXTRA_PLAYED_VIDEO = "EXTRA_PLAYED_VIDEO";
private static final String EXTRA_IS_PLAYING = "EXTRA_IS_PLAYING";
private static final String YOUTUBE_FRAGMENT = "YOUTUBE_FRAGMENT";
private static final String EXTRA_YOUTUBE_ID = "EXTRA_YOUTUBE_ID";
private RelativeLayout youtubeLayoutContainer;
private String youtubeId;
private int playedVideo;
private boolean isPlaying;
YouTubePlayer.OnInitializedListener onInitializedListener =
new YouTubePlayer.OnInitializedListener() {
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player,
boolean wasRestored) {
if (!wasRestored) {
setYouTubePlayer(player);
}
}
#Override public void onInitializationFailure(YouTubePlayer.Provider provider,
YouTubeInitializationResult error) {
}
};
public static YoutubeFragment newInstance(String youtubeId) {
YoutubeFragment youtubeElements = new YoutubeFragment();
Bundle bundle = new Bundle();
bundle.putString(EXTRA_YOUTUBE_ID, youtubeId);
youtubeElements.setArguments(bundle);
return youtubeElements;
}
#Override public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Nullable #Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.view_youtube_elements_item, container, false);
initViews(mView);
initYoutubeFragment();
return mView;
}
private void initViews(View view) {
youtubeLayoutContainer = (RelativeLayout) view.findViewById(R.id.youtubeLayoutContainer);
youtubeLayoutContainer.getViewTreeObserver()
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#TargetApi(Build.VERSION_CODES.JELLY_BEAN) #Override public void onGlobalLayout() {
FrameLayout.LayoutParams lp =
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.MATCH_PARENT);
youtubeLayoutContainer.setLayoutParams(lp);
if (AndroidSdkVersion.hasJellyBean16()) {
youtubeLayoutContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
}
private void initYoutubeFragment() {
try {
YouTubePlayerFragment youTubePlayerFragment2 = YouTubePlayerFragment.newInstance();
youTubePlayerFragment2.initialize(BuildConfig.YOUTUBE_DEVELOPER_KEY, onInitializedListener);
if (this.getActivity() != null && !this.getActivity().isFinishing()) {
getChildFragmentManager().beginTransaction()
.replace(R.id.youtubePlayerFragmentContent, youTubePlayerFragment2, YOUTUBE_FRAGMENT)
.commitAllowingStateLoss();
}
} catch (Exception ignored) {
}
}
public void setYouTubePlayer(final YouTubePlayer player) {
try {
if (player == null) {
return;
}
player.setShowFullscreenButton(true);
player.setPlayerStyle(YouTubePlayer.PlayerStyle.DEFAULT);
if (playedVideo >= 0) {
if (playedVideo == 0 || isPlaying) {
player.loadVideo(youtubeId, playedVideo);
} else {
player.cueVideo(youtubeId, playedVideo);
}
}
} catch (Exception ignored) {
}
}
#Override public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState != null) {
playedVideo = savedInstanceState.getInt(EXTRA_PLAYED_VIDEO);
isPlaying = savedInstanceState.getBoolean(EXTRA_IS_PLAYING);
}
}
#Override public void onSaveInstanceState(Bundle outState) {
try {
YouTubePlayerFragment youTubePlayerSupportFragment =
(YouTubePlayerFragment) getChildFragmentManager().findFragmentByTag(YOUTUBE_FRAGMENT);
YouTubePlayer mPlayer = youTubePlayerSupportFragment.getPlayer();
if (mPlayer != null) {
outState.putInt(EXTRA_PLAYED_VIDEO, mPlayer.getCurrentTimeMillis());
outState.putBoolean(EXTRA_IS_PLAYING, mPlayer.isPlaying());
}
} catch (Exception ignored) {
}
super.onSaveInstanceState(outState);
}
}
Activity containing Youtube Fragment
public class YoutubeContentDataActivity extends BaseActivity {
private static final String EXTRA_YOUTUBE_VIDEO_ID = "EXTRA_YOUTUBE_VIDEO_ID";
private static final String TAG_RETAINED_FRAGMENT = "TAG_RETAINED_FRAGMENT";
public static void open(Context context, String videoId) {
Intent intent = new Intent(context, YoutubeContentDataActivity.class);
intent.putExtra(EXTRA_YOUTUBE_VIDEO_ID, videoId);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_youtube_main_container_layout);
FragmentManager fm = getSupportFragmentManager();
YoutubeFragment youtubeElementsFragment =
(YoutubeFragment) fm.findFragmentByTag(TAG_RETAINED_FRAGMENT);
// create the fragment and data the first time
if (youtubeElementsFragment == null) {
String videoId = getIntent().getStringExtra(EXTRA_YOUTUBE_VIDEO_ID);
// videoId = "17uHCHfgs60";//"ikO91fQBsTQ";
youtubeElementsFragment = YoutubeFragment.newInstance(videoId);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.youtube_main_container, youtubeElementsFragment, TAG_RETAINED_FRAGMENT)
.commit();
}
}
#Override public void onPause() {
super.onPause();
if (isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
YoutubeFragment youtubeElementsFragment =
(YoutubeFragment) fm.findFragmentByTag(TAG_RETAINED_FRAGMENT);
fm.beginTransaction().remove(youtubeElementsFragment).commit();
}
}
}
I could fix this error by using a ProgressDialog to wait until the player is loaded.

IllegalStateException: Can not perform this action after onSaveInstanceState - after screen rotating

I'm also getting it in a precise context and the solution given here (IllegalStateException: Can not perform this action after onSaveInstanceState with ViewPager) don't work.
Here is the code: Should be a working code for test; I hope.
MainActivity.java
public class MainActivity extends FragmentActivity {
final static int INIT_NETWORK_DONE = 1;
final static int EXIT_APPLICATION = -1;
private Site site = new Site(this);
private WifiManager wifi = null;
Handler mHandler = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
site.setUrls();
if (savedInstanceState == null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.replace(R.id.frame_container, new Fragment_init(site)).commit();
}
}
. . .
#Override
public void onSaveInstanceState(Bundle saveInstanceState) {
//super.onSaveInstanceState(saveInstanceState);
}
}
Fragment_init.java
public class Fragment_init extends Fragment {
Fragment fragment = null;
private InitTask mInitTask = null;
// Taille maximale du téléchargement
public final static int MAX_SIZE = 100;
// Identifiant de la boîte de dialogue
public final static int ID_DIALOG = 0;
public final static int DO_INIT_WIFI = 1;
private Site site = null;
public Fragment_init() {
}
public Fragment_init(Site _site) {
site = _site;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_init, container, false);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
Animation animation = AnimationUtils.loadAnimation(getActivity().getApplicationContext(), R.animator.welcome_anim);
ImageView logoSite = (ImageView)getActivity().findViewById(R.id.imageAvenArmand);
logoSite.startAnimation(animation);
// Do the init
mInitTask = new InitTask(Fragment_init.this, site, getFragmentManager());
// On l'exécute
mInitTask.execute(0);
}
}
// L'AsyncTask est bien une classe interne statique
static class InitTask extends AsyncTask<Integer, Integer, Integer> {
// Référence faible à l'activité
private Fragment_init mActivity = null;
private Site site = null;
Context context = null;
private FragmentManager fragmentManager = null;
public InitTask (Fragment_init pActivity, Site pSite, FragmentManager _fragmentManager) {
mActivity = pActivity;
context = mActivity.getActivity();
site = pSite;
fragmentManager = _fragmentManager;
}
#Override
protected void onPreExecute () {
}
#Override
protected void onPostExecute (Integer result) {
if(result != 1) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mActivity.getActivity());
alertDialog.setTitle(R.string.label_titleAlertInit);
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.replace(R.id.frame_container, new Fragment_selectLanguage(site)).commitAllowingStateLoss();
}
}
#Override
protected Integer doInBackground (Integer... arg0) {
URL url = null;
BufferedInputStream buf;
ArrayList<Language> languages = null;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
return 1;
}
#Override
protected void onProgressUpdate (Integer... prog) {
}
#Override
protected void onCancelled () {
}
private int processStream(InputStream inputStream) {
// Création du parser XML
XmlPullParserFactory factory;
int lineNumber = 0;
return (1);
}
}
#Override
public void onSaveInstanceState(Bundle saveInstanceState) {
//super.onSaveInstanceState(saveInstanceState);
}
}
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:screenOrientation="portrait"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.cabbonline.ndguidelt.MainActivity" >
</FrameLayout>
fragment_init.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragmentInit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.cabbonline.ndguidelt.MainActivity" >
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="15dp"
android:src="#drawable/launcher_icon" />
</RelativeLayout>
Anyway, I think that not calling super.onSaveInstanceState() should cause problem on context saving no?
so if you rotate the screen when the image is fading, you should get IllegalStateException on call on commit()
So my workaround is to prevent the screen rotation during this transitional screen. Ok that's ok for me but I doubt it could be an answer for most of you. anyway, it could help.
So I call this in onCreateView() in fragment_init().
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
And I then call this in onCreateView() in the next fragment:
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
:-/
Any other idea?
Use commitAllowingStateLoss() instead of commit()
if (savedInstanceState == null) {
FragmentTransaction fragmentTransaction =getSupportFragmentManager().beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.replace(R.id.frame_container, new Fragment_init(site)).commitAllowingStateLoss();
}
You should see this blog about on how to avoid that exception: http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
So I solved my problem using the wonderfull message handler implementation explained here:
How to handle Handler messages when activity/fragment is paused
Thx to Akagami which pointed me on the post.
Regards,

Categories

Resources