Use YoutubePlayerView on fragment section - android

I found some similar situation, like this and this, but none of then solved my problem.
I have wanna have a screen with youtube video and some text information like this:
I'm using a activity and a fragment, both with base class that extended YouTubeBaseActivity and YouTubePlayerFragment.
But when I'm trying to open the fragment it show a npe but it don't show where. Since I'm getting the layout and view right, I don't now what is going on.
Hope that it don't get downvotes because is a npe question, but is different from usual, this API call don't show me where the NPE happen and I saw the other people has problems like this
Obs: I'm using this extend base concept because more places will have this videos behaviour and I'm trying to avoid code repeat.
Logcat
XML
<android.support.constraint.ConstraintLayout
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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".screens.mine.fragments.MineStepsFragment">
<com.google.android.youtube.player.YouTubePlayerView
android:id="#+id/mine_steps_youtube_player"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#color/background_white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
</com.google.android.youtube.player.YouTubePlayerView>
</android.support.constraint.ConstraintLayout>
Activity
public class MineAccidentActivity extends BaseYoutubeActivity {
#Override
protected void initializeActionBar() {
actionbarLeftBtn.setVisibility(View.VISIBLE);
actionbarTitle.setVisibility(View.VISIBLE);
}
#Override
protected int getActionbarTitle() {
return R.string.mine_accident;
}
#Override
protected int getContentView() {
return R.layout.mine_accident_activity;
}
#Override
protected void assignViews() {
MineAccidentController.getInstance().attachToActivity(this, R.id.mine_container);
}
#Override
protected void prepareViews() {}
/////////////////// BACK ////////////////////
#Override
public void onBackPressed() {
if (!MineAccidentController.getInstance().isFirstFragmentShown()) {
MineAccidentController.getInstance().showPreviousFragment();
} else {
super.onBackPressed();
}
}
/////////////////// LIFE CYCLE ////////////////////
#Override
protected void onDestroy() {
MineAccidentController.getInstance().onDestroy();
super.onDestroy();
}
}
Fragment Controller
public class MineAccidentController extends BaseYoutubeController {
private String accidentType;
#Override
protected ArrayList<android.app.Fragment> initFragments() {
ArrayList<android.app.Fragment> fragments = new ArrayList<>();
fragments.add(new MineStepsFragment());
return fragments;
}
//create Class
public static MineAccidentController getInstance() {
if (null == instance) {
synchronized (MineAccidentController.class) {
if (null == instance) {
setInstance(new MineAccidentController());
}
}
}
return (MineAccidentController) instance;
}
public String getAccidentType() {
return accidentType;
}
public void setAccidentType(String accidentType) {
this.accidentType = accidentType;
}
}
Fragment
public class MineStepsFragment extends BaseYoutubeFragment {
//Not in Layout
private String videoUrl;
////////////// IMPLEMENT_METHODS //////////////
#Override
protected int getFragmentContentView() {
return R.layout.mine_steps_fragment;
}
#Override
protected int getYoutubePlayerView() {
return R.id.mine_steps_youtube_player;
}
#Override
protected void assignViews() {
}
#Override
protected void prepareViews() {
}
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer, boolean wasRestored) {
if(!wasRestored){
checkType();
youTubePlayer.cueVideo(videoUrl);
}
}
#Override
public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult youTubeInitializationResult) {
showErrorToast(getActivity(), R.string.error_initialize_video);
}
////////////// FUNCTIONS //////////////
private void checkType() {
if(MineAccidentController.getInstance().getAccidentType().equals(Parameters.ACCIDENT_PERSONAL)){
videoUrl = Properties.MINE_PERSONAL_VIDEO;
}
else {
videoUrl = Properties.MINE_WORK_VIDEO;
}
}
}
Base Fragment
public abstract class BaseYoutubeFragment extends YouTubePlayerFragment implements YouTubePlayer.OnInitializedListener {
protected View fragmentView = null;
protected YouTubePlayerView youTubePlayerView;
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
assignViews();
prepareViews();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
fragmentView = inflater.inflate(getFragmentContentView(), container, false);
youTubePlayerView = fragmentView.findViewById(getYoutubePlayerView());
youTubePlayerView.initialize(com.can_apps.eva_ngo.properties.Properties.API_KEY, this);
return fragmentView;
}
/////////////////// ABSTRACT METHODS ////////////////////
protected abstract int getFragmentContentView(); //Get Layout R.layout.name_file
protected abstract int getYoutubePlayerView(); //Get Container for YOutube Video
protected abstract void assignViews(); //Used for findById the params
protected abstract void prepareViews(); //Used for start the values of params
/////////////////// SHOW MESSAGES ////////////////////
public void showErrorToast(Context context, final int message) {
Toast toast = Toast.makeText(context, getString(message), Toast.LENGTH_SHORT);
View view = toast.getView();
view.setBackgroundResource(R.color.background_red_transparent);
toast.show();
}
/////////////////// SNACK BAR ////////////////////
public void showSnackBar(final int text) {
Snackbar.make(Objects.requireNonNull(getView()), getString(text), Snackbar.LENGTH_SHORT).show();
}
public void showSnackBar(final int mainTextStringId, final int actionStringId, View.OnClickListener listener) {
Snackbar.make(Objects.requireNonNull(getView()),
getString(mainTextStringId),
Snackbar.LENGTH_LONG)
.setAction(getString(actionStringId), listener).show();
}
}

Looking at the logcat it looks like your YouTubePlayerView is null when you are calling one of its methods.
The YouTube Player API is quite buggy and difficult to use correctly. To solve this problems (and others) I have built an alternative player Android-YouTube-Player, it's open source and you can do whatever you want with it.
In your case, you won't have to meddle with Fragments and transactions, since my YouTubePlayerView is just a regular view and requires no special Fragments or Activities. You can drop it wherever you want.
Hope it could be useful to you as well!

Related

Why does pressing back from detail activity after landscape-to-portrait-switch show an empty screen?

Below is the MainActivity class that I'm using. The code checks to see if the phone is in landscape or portrait. If it's in portrait, it will show the main fragment in the main activity only (the main fragment is a static fragment in the main_activity.xml file). Then if a "Recipe" is clicked it will open a detail activity with its own fragment. If the phone is in landscape mode, it will show the main fragment and the detail fragment side by side. Everything works perfectly fine however when I follow the procedure below I get a white screen instead of the main activity:
Procedure:
Switch to landscape
Switch back to portrait
Choose an item and wait for the detail activity to open
Press back
Here instead of the main activity window I get a white screen
If I don't switch to landscape and just start with the portrait mode everything is fine. It seems like switching to landscape does something that causes the problem and I can't figure out what. Any tip on what's going on or where to look would be much appreciated.
public class MainActivity extends AppCompatActivity implements RecipesFragment.OnRecipeClickListener {
private String RECIPE_PARCEL_KEY;
private boolean mTwoPane;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RECIPE_PARCEL_KEY = getString(R.string.ParcelKey_RecipeParcel);
if (findViewById(R.id.linearLayoutTwoPane) != null) {
mTwoPane = true;
if (savedInstanceState == null) {
RecipeFragment recipeFragment = new RecipeFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.recipeFrameForTwoPane, recipeFragment)
.commit();
}
} else {
mTwoPane = false;
}
}
#Override
public void OnRecipeClick(Recipe recipe) {
if (mTwoPane) {
RecipeFragment recipeFragment = new RecipeFragment();
recipeFragment.setRecipe(recipe);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.recipeFrameForTwoPane, recipeFragment)
.commit();
} else {
Class destinationClass = DetailActivity.class;
Intent intentToStartDetailActivity = new Intent(this, destinationClass);
intentToStartDetailActivity.putExtra(RECIPE_PARCEL_KEY, recipe);
startActivity(intentToStartDetailActivity);
}
}
}
EDIT:
Adding RecipeFragment's code below:
public class RecipeFragment extends Fragment {
private Recipe mRecipe;
#BindView(R.id.tv_recipeName) TextView recipeNameTextView;
public RecipeFragment(){
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.recipe_fragment,container,false);
ButterKnife.bind(this,view);
if(mRecipe!=null) {
recipeNameTextView.setText(mRecipe.getName());
}else{
recipeNameTextView.setText(getString(R.string.messageSelectARecipe));
}
return view;
}
public void setRecipe(Recipe recipe){
mRecipe = recipe;
}
}
EDIT:
I followed #mt0s's advice and created different background colors for the fragments and activities and finally narrowed down the problem to a line in my recyclerview adapter code. My adapter code is below. Inside loadInBackground() on line URL url = new URL(getString(R.string.URL_RecipeJSON)); I get a Fragment RecipesFragment{96e9b6a} not attached to Activity exception. I don't understand why I'm getting this exception and what the best way to resolve this is. Have I placed the right code in the right fragment methods (ie OnCreate vs OnActivityCreated vs OnCreateView vs etc)?
public class RecipesFragment extends Fragment
implements RecipeAdapter.RecipeAdapterOnClickHandler,
LoaderManager.LoaderCallbacks<ArrayList<Recipe>> {
#BindView(R.id.rv_recipes) RecyclerView mRecyclerView;
private RecipeAdapter mRecipeAdapter;
private static final int LOADER_ID = 1000;
private static final String TAG = "RecipesFragment";
private OnRecipeClickListener mOnRecipeClickListener;
public RecipesFragment(){
}
public interface OnRecipeClickListener {
void OnRecipeClick(Recipe recipe);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.recipes_fragment, container, false);
ButterKnife.bind(this, view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mRecipeAdapter = new RecipeAdapter(this);
mRecyclerView.setAdapter(mRecipeAdapter);
return view;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(LOADER_ID, null, this);
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void OnClick(Recipe recipe) {
mOnRecipeClickListener.OnRecipeClick(recipe);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
mOnRecipeClickListener = (OnRecipeClickListener) context;
} catch (ClassCastException e){
Log.e(TAG, "onAttach: Host activity class must implement OnRecipeClickListener.");
}
}
#Override
public Loader<ArrayList<Recipe>> onCreateLoader(int i, Bundle bundle) {
return new AsyncTaskLoader<ArrayList<Recipe>>(getActivity()) {
#Override
protected void onStartLoading() {
super.onStartLoading();
forceLoad();
}
#Override
public ArrayList<Recipe> loadInBackground() {
String response;
ArrayList<Recipe> recipes = null;
try {
URL url = new URL(getString(R.string.URL_RecipeJSON)); //***I get an exception here***
response = NetworkUtils.getResponseFromHttpUrl(url, getActivity());
recipes = RecipeJsonUtils.getRecipeFromJson(getActivity(), response);
} catch (Exception e) {
Log.e(TAG, "loadInBackground: " + e.getMessage());
}
return recipes;
}
};
}
#Override
public void onLoadFinished(Loader<ArrayList<Recipe>> loader, ArrayList<Recipe> recipes) {
mRecipeAdapter.setRecipeData(recipes);
}
#Override
public void onLoaderReset(Loader<ArrayList<Recipe>> loader) {
}
}
I finally figured out the problem and the solution. The problem is that onStartLoading() in the AsyncTaskLoader anonymous class in RecipesFragment class gets called every time the fragment is resumed whether the enclosing Loader is called or not. This causes the problem. I need to have control over when onStartLoading() is being called and I only want it to be called if and only if the enclosing Loader is being initialized or restarted. As such, I destroyed the loader in onPause() of the fragment and restarted it in onResume(). Hence, I added the following code to the RecipesFragment class:
#Override
public void onPause() {
super.onPause();
getLoaderManager().destroyLoader(LOADER_ID);
}
#Override
public void onResume() {
super.onResume();
getLoaderManager().restartLoader(LOADER_ID, null, this);
}
I also removed initLoader() from onCreate(). This way, every time the fragment is resumed (or created) onStartLoading() will be called. I tried this and it solves my problem.
When you switch from the landscape to portrait or the opposite the Android OS destroy your activity and recreate it again. this what probably trigger your problem

Maintaining list items positions on device rotation in an android fragment

In a fragment I am trying to save the scroll state of the RecyclerView list, but somehow it is not saving the state. As it is a fragment, I am overriding the onSaveInstanceState() and onActivityCreated() methods to save the scroll position. Even tried implementing in onViewStateRestored() method. I saw related some posts on saving the scroll state but it ain't working. Kindly let me know where am I failing. Below is my code:
public class RecipeListFragment extends Fragment
implements RecipeListContract.View {
#BindView(R.id.recipe_list_recycler_view)
RecyclerView mRecipeListRecyclerView;
#BindView(R.id.recipe_list_progress_bar)
ProgressBar mRecipeListProgressBar;
#BindInt(R.integer.grid_column_count)
int mGridColumnCount;
#BindString(R.string.recipe_list_sync_completed)
String mRecipeListSyncCompleted;
#BindString(R.string.recipe_list_connection_error)
String mRecipeListConnectionError;
GridLayoutManager gridLayoutManager;
Parcelable savedRecyclerLayoutState;
Unbinder unbinder;
private static final String SAVED_LAYOUT_MANAGER
= "com.learnwithme.buildapps.bakingapp.ui.recipelist.fragment";
private RecipeListContract.Presenter mRecipeListPresenter;
private RecipeListAdapter mRecipeListAdapter;
public RecipeListFragment() { }
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recipe_list, container,
false);
unbinder = ButterKnife.bind(this, view);
mRecipeListAdapter = new RecipeListAdapter(
getContext(),
new ArrayList<>(0),
recipeId -> mRecipeListPresenter.loadRecipeDetails(recipeId)
);
mRecipeListAdapter.setHasStableIds(true);
gridLayoutManager = new GridLayoutManager(getContext(),
mGridColumnCount);
mRecipeListRecyclerView.setLayoutManager(gridLayoutManager);
mRecipeListRecyclerView.setHasFixedSize(true);
mRecipeListRecyclerView.setAdapter(mRecipeListAdapter);
return view;
}
#Override
public void onPause() {
super.onPause();
mRecipeListPresenter.unsubscribe();
}
#Override
public void onResume() {
super.onResume();
mRecipeListPresenter.subscribe();
}
#Override
public void onSaveInstanceState() { }
#Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
if(bundle != null) {
bundle.putParcelable(SAVED_LAYOUT_MANAGER,
mRecipeListRecyclerView
.getLayoutManager()
.onSaveInstanceState());
Timber.d("instance state=>",
mRecipeListRecyclerView.getLayoutManager().onSaveInstanceState());
}
}
#Override
public void onViewStateRestored(#Nullable Bundle bundle) {
super.onViewStateRestored(bundle);
if(bundle != null) {
savedRecyclerLayoutState =
bundle.getParcelable(SAVED_LAYOUT_MANAGER);
Timber.d("onViewStateRestored savedRecyclerLayoutState=>",
savedRecyclerLayoutState);
mRecipeListRecyclerView
.getLayoutManager()
.onRestoreInstanceState(savedRecyclerLayoutState);
}
}
#Override
public void onActivityCreated(#Nullable Bundle bundle) {
super.onActivityCreated(bundle);
if(bundle != null) {
savedRecyclerLayoutState =
bundle.getParcelable(SAVED_LAYOUT_MANAGER);
Timber.d("onViewStateRestored savedRecyclerLayoutState=>",
savedRecyclerLayoutState);
mRecipeListRecyclerView
.getLayoutManager()
.onRestoreInstanceState(savedRecyclerLayoutState);
}
}
#Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
public static RecipeListFragment newInstance() {
return new RecipeListFragment();
}
#Override
public void setPresenter(RecipeListContract.Presenter recipeListPresenter) {
this.mRecipeListPresenter = recipeListPresenter;
}
#Override
public void showRecipeList(List<Recipe> recipeList) {
mRecipeListAdapter.refreshRecipes(recipeList);
}
#Override
public void loadProgressBar(boolean show) {
setViewVisibility(mRecipeListRecyclerView, !show);
setViewVisibility(mRecipeListProgressBar, show);
}
#Override
public void displayCompletedMessage() {
Toast.makeText(getContext(), mRecipeListSyncCompleted,
Toast.LENGTH_SHORT).show();
}
#Override
public void displayErrorMessage() {
Toast.makeText(getContext(), mRecipeListConnectionError,
Toast.LENGTH_SHORT).show();
}
#Override
public void displayRecipeDetails(int recipeId) {
startActivity(RecipeDetailsActivity.prepareIntent(getContext(),
recipeId));
}
private void setViewVisibility(View view, boolean visible) {
if (visible) {
view.setVisibility(View.VISIBLE);
} else {
view.setVisibility(View.INVISIBLE);
}
}
}
I have resolved the issue myself. The problem was to save the scroll position of the device in onSaveInstanceState() and restoring the same in onViewRestored() method.
private Parcelable mRecipeListParcelable;
private int mScrollPosition = -1;
#Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
int scrollPosition = ((GridLayoutManager)
mRecipeListRecyclerView.getLayoutManager())
.findFirstCompletelyVisibleItemPosition();
mRecipeListParcelable = gridLayoutManager.onSaveInstanceState();
bundle.putParcelable(KEY_LAYOUT, mRecipeListParcelable);
bundle.putInt(POSITION, scrollPosition);
}
#Override
public void onViewStateRestored(#Nullable Bundle bundle) {
super.onViewStateRestored(bundle);
if(bundle != null) {
mRecipeListParcelable = bundle.getParcelable(KEY_LAYOUT);
mScrollPosition = bundle.getInt(POSITION);
}
}
Also, in the loadProgress() method I had to set the scrollToPosition() with the scroll position saved.
#Override
public void loadProgressBar(boolean show) {
setViewVisibility(mRecipeListRecyclerView, !show);
setViewVisibility(mRecipeListProgressBar, show);
mRecipeListRecyclerView.scrollToPosition(mScrollPosition);
}
Also, one more thing to remember is that no need to restore anything in onResume() method as the presenter callbacks would get called and the view is reset anyway.

Only the original thread that created a view hierarchy can touch its views. - Strange behavior

This is a very strange behavior and I don't know how to fix it.
I have an Activity as a Presenter (In a MVP Architecture).
When the activity starts, I attach a Fragment as a View. The fragment itself is very simple.
public class CurrentSaleFragment extends BaseFragment {
private MainMVP.SalesPresenterOps salesPresenterOps;
private SaleAdapter adapter;
private ListView lv;
#BindView(R.id.btn_sell)
FloatingActionButton btnAdd;
public static CurrentSaleFragment newInstance(){
CurrentSaleFragment fragment = new CurrentSaleFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_quick_sale );
fragment.setArguments(arguments);
return fragment;
}
#Override
protected void init() {
super.init();
lv = (ListView)view.findViewById(R.id.lv_sale);
}
#OnClick(R.id.btn_sell)
public void addToSale(View view){
mPresenter.moveToFragment(SellProductFragment.newInstance());
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
salesPresenterOps = (MainMVP.SalesPresenterOps)context;
}
#Override
public void onDetach() {
salesPresenterOps = null;
super.onDetach();
}
}
The BaseFragment from which this fragmend extends :
public class BaseFragment extends Fragment implements MainMVP.RequiredViewOps, View.OnClickListener,
LoaderRequiredOps{
protected View view;
protected MainMVP.PresenterOps mPresenter;
protected final static String LAYOUT_RES_ID = "layout_res_id";
#Override
public void showOperationResult(String message, final long rowId) {
Snackbar.make(view, message, Snackbar.LENGTH_LONG).setAction(
R.string.see, new View.OnClickListener() {
#Override
public void onClick(View v) {
onOperationResultClick(rowId);
}
}
).show();
}
#Override
public void showSnackBar(String msg) {
Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show();
}
#Override
public void showAlert(String msg) {}
protected void onOperationResultClick(long rowId){}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mPresenter = (MainMVP.PresenterOps)context;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
this.view = inflater.inflate(getArguments().getInt(LAYOUT_RES_ID), null);
init();
return view;
}
protected void addToClickListener(View ... params){
for (View v : params){
v.setOnClickListener(this);
}
}
protected void init() {
if (view != null){
ButterKnife.bind(this, view);
}
}
#Override
public void onDetach() {
mPresenter = null;
Log.d(getClass().getSimpleName(), "Fragment was detached");
super.onDetach();
}
#Override
public void onClick(View v) {}
#Override
public void onPreLoad() {
Dialogs.buildLoadingDialog(getContext(), "Loading...").show();
}
#Override
public void onLoad() {}
#Override
public void onDoneLoading() {
Dialogs.dismiss();
}
}
When I enter the method 'moveToFragment()' I just replace CurrentSaleFragment for a new Fragment:
protected void addFragment(BaseFragment fragment){
mView = fragment;
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_holder,
fragment, null).addToBackStack(null).commit();
}
Then the new fragment is attached:
public class SellProductFragment extends BaseFragment{
private ListView listView;
private ProductListAdapter adapter;
private MainMVP.SalesPresenterOps mSalesPresenter;
public static SellProductFragment newInstance(){
SellProductFragment fragment = new SellProductFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_inventory);
fragment.setArguments(arguments);
return fragment;
}
private void reload(){
final Loader loader = new Loader(this);
loader.execute();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mSalesPresenter = (MainMVP.SalesPresenterOps)context;
}
#Override
protected void init() {
super.init();
listView = (ListView)view.findViewById(R.id.lv_inventory);
reload();
FloatingActionButton button = (FloatingActionButton)view.findViewById(R.id.btn_add);
addToClickListener(button);
}
#Override
public void onLoad() {
adapter = new ProductListAdapter(getActivity().getApplicationContext(), R.layout.row_product_item,
mSalesPresenter.getProducts());
try{
updateListView();
}catch (Exception e){
Log.w(getClass().getSimpleName(), e.getMessage());
}
}
private void updateListView(){
if (adapter != null && listView != null){
listView.setAdapter(adapter);
}else{
throw new RuntimeException();
}
}
}
See that This fragment also extends from BaseFragment and implements LoaderRequiredOps. The interface is used to 'load' any data. It adds a dialog and updated the adapter when the loading is done:
public class Loader extends AsyncTask<Void, Void, Void> {
private LoaderRequiredOps presenter;
public Loader(LoaderRequiredOps presenter){
this.presenter = presenter;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
presenter.onPreLoad();
}
#Override
protected Void doInBackground(Void... params) {
presenter.onLoad();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
presenter.onDoneLoading();
presenter = null;
}
}
Now, when I try to execute the method reload() from the SellProductFragment i get the 'Only the original thread that created a view hierarchy can touch its views.'
This does not happen if the SellProductFragment is attached first instead of CurrentSaleFragment.
What is happening here?
Your Async Loader class calls the presenters method onLoad() from a background thread during doInBackground().
My guess is that in the onLoad() method of the presenter, a view is referenced.
In order to change the view at this point, post the view logic as a Runnable to the UI thread (you said your presenter is the activity, so this should be possible from the onLoad method).
#Override
public void onLoad() {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Your ui code here...
}
});
// Rest of your code here...
}
For an unknown reason, an unidentified configuration allows to execute the setting of an adapter for a ListView on the doInBackground() method.
Moved it to onPostExecute() and now it's working

Why do I get an error of cycling inheritance involving MyFragment?

I am trying to send data from activity to fragment and vice versa using interfaces but getting an error of cycling inheritance involving MyFragment.
Implementing interface created in MyFragment:
public class MyActivity implements OnSendFromMyFragListener {
OnSendFromMyActivityListener mCallback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCallback.sendFromMyActivity(2);
}
#Override
public void sendFromMyFrag (int a) {
//do something
}
public interface OnSendFromMyActivityListener {
public void sendFromMyActivity(int b);
}
}
Implementing interface created in MyActivity:
public class MyFragment extends Fragment implements OnSendFromMyActivityListener {
OnSendFromMyFragListener mCallback;
public interface OnSendFromMyFragListener {
void sendFromMyFrag(int a);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (OnSendFromMyFragListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString());
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_my, container, false);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCallback.sendFromMyFrag(1);
}
}
});
return view;
}
#Override
public void sendFromMyActivity(int b) {
//do something
}
}
Well the reason you are getting this error is because your Activity depends on your Fragment and Fragment depends on your Activity. Don't Agree?
Let me show you. Imagine you are a compiler in your work you stumbled across:
class A implements B.A {
interface B {
void foo1();
}
#Override
public void foo2()
{
// do something;
}
}
Now you know that class A depends on (implements) B.A, so before you go further into class A you move on to class B:
class B implements A.B {
interface A {
void foo2();
}
#Override
public void foo1()
{
// Do Something;
}
}
Now you see that class B depends on (implements) class A specifically class A.B! What do you (compiler) do? Go back to class A? But that depends on class B. So you see this becomes an unending cycle thus causing a cyclic dependency where both your class' definitions depend on each other.
As an alternative you could either create a member event listener or an anonymous one. Or if you don't like either of those options, you could also create a separate java interface class file for any one of the two interfaces.
Maybe you have to do this for Activity
public class MyActivity extends AppCompatActivity implements MyFragment.OnSendFromMyFragListener {
public interface OnSendFromMyActivityListener {
void sendFromMyActivity(int b);
}
OnSendFromMyActivityListener mCallback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyFragment myFragment = MyFragment.newInstance();
//Do the transaction.....
//And after this
mCallback.sendFromMyActivity(0);
}
public void setOnSendFromMyActivityListener(OnSendFromMyActivityListener mCallback){
this.mCallback = mCallback;
}
#Override
public void sendFromMyFrag(int a) {
//do something
}}
And this for Fragment
public class MyFragment extends Fragment implements OnSendFromMyActivityListener {
OnSendFromMyFragListener mCallback;
public interface OnSendFromMyFragListener {
void sendFromMyFrag(int a);
}
public static MyFragment newInstance() {
Bundle args = new Bundle();
MyFragment fragment = new MyFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (OnSendFromMyFragListener) getActivity();
((MyActivity) getActivity()).setOnSendFromMyActivityListener(this);
} catch (ClassCastException e) {
throw new ClassCastException(context.toString());
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_place_suggest, container, false);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCallback.sendFromMyFrag(1);
}
});
return view;
}
#Override
public void sendFromMyActivity(int b) {
//do something
}
if you want you can put setters of the interface also to the Fragment for better abstraction.
Something you have to watch out is to look for nulls interfaces
Thanks

Making a perfect Chromeless Youtube Player in Android

What i created while making CHROMELESS youtube player view is here. It is loading and playing perfectly but the buffering circle plays continuously even when video gets completed. Can anyone help me to remove buffering progress when it is not required and make a perfect youtube player?
MainActivity.java
package com.rocky.youtubedemo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import com.google.android.youtube.player.YouTubeBaseActivity;
import com.google.android.youtube.player.YouTubeInitializationResult;
import com.google.android.youtube.player.YouTubePlayer;
import com.google.android.youtube.player.YouTubePlayerView;
public class MainActivity extends YouTubeBaseActivity implements YouTubePlayer.OnInitializedListener {
private static final int RECOVERY_REQUEST = 1;
private static String YOUTUBE_API_KEY = "";
private YouTubePlayerView youTubeView;
private Context context;
private MyPlayerStateChangeListener playerStateChangeListener;
private MyPlaybackEventListener playbackEventListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
playerStateChangeListener = new MyPlayerStateChangeListener();
playbackEventListener = new MyPlaybackEventListener();
YOUTUBE_API_KEY = "PLACE_YOUR_API_KEY_HERE";
youTubeView = (YouTubePlayerView) findViewById(R.id.youtube_view);
youTubeView.initialize(YOUTUBE_API_KEY, this);
}
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, final YouTubePlayer player, boolean wasRestored) {
player.setPlayerStateChangeListener(playerStateChangeListener);
player.setPlaybackEventListener(playbackEventListener);
if (!wasRestored) {
player.loadVideo("fhWaJi1Hsfo"); // Plays https://www.youtube.com/watch?v=fhWaJi1Hsfo
player.setPlayerStyle(YouTubePlayer.PlayerStyle.CHROMELESS);
}
}
#Override
public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult errorReason) {
if (errorReason.isUserRecoverableError()) {
Toast.makeText(context, errorReason.toString(), Toast.LENGTH_LONG).show();
// errorReason.getErrorDialog(this, RECOVERY_REQUEST).show();
} else {
String error = errorReason.toString();
Toast.makeText(context, error, Toast.LENGTH_LONG).show();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RECOVERY_REQUEST) {
// Retry initialization if user performed a recovery action
getYouTubePlayerProvider().initialize(YOUTUBE_API_KEY, this);
}
}
private void showMessage(String message) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
protected YouTubePlayer.Provider getYouTubePlayerProvider() {
return youTubeView;
}
private final class MyPlaybackEventListener implements YouTubePlayer.PlaybackEventListener {
#Override
public void onPlaying() {
// Called when playback starts, either due to user action or call to play().
showMessage("Playing");
}
#Override
public void onPaused() {
// Called when playback is paused, either due to user action or call to pause().
showMessage("Paused");
}
#Override
public void onStopped() {
// Called when playback stops for a reason other than being paused.
showMessage("Stopped");
}
#Override
public void onBuffering(boolean b) {
showMessage("buffer");
}
#Override
public void onSeekTo(int i) {
// Called when a jump in playback position occurs, either
// due to user scrubbing or call to seekRelativeMillis() or seekToMillis()
}
}
private final class MyPlayerStateChangeListener implements YouTubePlayer.PlayerStateChangeListener {
#Override
public void onLoading() {
showMessage("loading");
}
#Override
public void onLoaded(String s) {
showMessage("loaded");
}
#Override
public void onAdStarted() {
// Called when playback of an advertisement starts.
}
#Override
public void onVideoStarted() {
// Called when playback of the video starts.
showMessage("started");
}
#Override
public void onVideoEnded() {
// Called when the video reaches its end.
}
#Override
public void onError(YouTubePlayer.ErrorReason errorReason) {
// Called when an error occurs.
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".MainActivity">
<com.google.android.youtube.player.YouTubePlayerView
android:id="#+id/youtube_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
In your onInitializationSuccess(), set a PlaybackEventListener on the player. Override the onBuffering() and do something like this:
ViewGroup ytView = youTubeView; // if you are using YouTubePlayerView
ViewGroup ytView = (ViewGroup)ytPlayerFragment.getView(); // if you are using YouTubePlayerFragment
ProgressBar progressBar;
try {
// As of 2016-02-16, the ProgressBar is at position 0 -> 3 -> 2 in the view tree of the Youtube Player Fragment/View
ViewGroup child1 = (ViewGroup)ytView.getChildAt(0);
ViewGroup child2 = (ViewGroup)child1.getChildAt(3);
progressBar = (ProgressBar)child2.getChildAt(2);
} catch (Throwable t) {
// As its position may change, we fallback to looking for it
progressBar = findProgressBar(ytView);
// I recommend reporting this problem so that you can update the code in the try branch: direct access is more efficient than searching for it
}
int visibility = isBuffering ? View.VISIBLE : View.INVISIBLE;
if (progressBar != null) {
progressBar.setVisibility(visibility);
// Note that you could store the ProgressBar instance somewhere from here, and use that later instead of accessing it again.
}
Make method like this one:
private ProgressBar findProgressBar(View view) {
if (view instanceof ProgressBar) {
return (ProgressBar)view;
} else if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup)view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
ProgressBar res = findProgressBar(viewGroup.getChildAt(i));
if (res != null) return res;
}
}
return null
}
By this way you can enable progress when it is buffering and disable it when it is not.

Categories

Resources