android - Getting Google Cast v3 to work on unsupported devices -


the cast v3 framework has features try make possible run on devices without google play services required work, ran issues when testing.

  1. on kindle google api returns service_invalid isuserresolvable() true.
  2. on devices onactivityresult returning connectionresult.success after upgrade, castcontext.getsharedinstance() can throw runtimeerror.
  3. as side-effect of 2), xml inflate of items containing minicontrollerfragment fail.

some errors found were

java.lang.runtimeexception: unable start activity componentinfo{##########.mainactivity}: android.view.inflateexception: binary xml file line #42: error inflating class fragment  caused by: java.lang.runtimeexception:    com.google.android.gms.dynamite.dynamitemodule$zzc: remote load failed. no local fallback found.     @ com.google.android.gms.internal.zzauj.zzan(unknown source)     @ com.google.android.gms.internal.zzauj.zza(unknown source)     @ com.google.android.gms.cast.framework.castcontext.<init>(unknown source)     @ com.google.android.gms.cast.framework.castcontext.getsharedinstance(unknown source)     @ com.google.android.gms.cast.framework.media.uicontroller.uimediacontroller.<init>(unknown source)     @ com.google.android.gms.cast.framework.media.widget.minicontrollerfragment.oncreateview(unknown source) 

this caused inflation of minicontrollerfragment, on device castcontroller code wasn't installed. similar question asked so : cast v3 crashing on devices below 5.0. answer provided kamil Ślesiński helped in investigation.

and

java.lang.runtimeexception: failure delivering result resultinfo{who=null, request=123, result=0, data=null} activity ##### 

when had implemented viewstub, still crashing in pre-release test machines, returning success, didn't have castcontext available. fix this, needed test check if castcontext creatable.

you need singleton / code in application below....

boolean gcastable = false; boolean gcasttested = false; public boolean iscastavailable(activity act, int resultcode ){     if( gcasttested == true ){         return gcastable;     }      googleapiavailability castapi = googleapiavailability.getinstance();     int castresult = castapi.isgoogleplayservicesavailable(act);     switch( castresult ) {         case connectionresult.success:             gcastable = true;             gcasttested = true;             return true;      /*  code needed, user doesn't        *       *  device incompatible "ok"        *       * message, isn't "user actionable"       */         case connectionresult.service_invalid: // result amazon kindle - perhaps check if kindle first??             gcastable = false;             gcasttested = true;             return false;       ////////////////////////////////////////////////////////////////         default:             if (castapi.isuserresolvableerror(castresult)) {                 castapi.geterrordialog(act, castresult, resultcode, new dialoginterface.oncancellistener() {                     @override                     public void oncancel(dialoginterface dialog) {                         gcastable = false;                         gcasttested = false;                         return;                     }                 }).show();             } else {                 gcasttested = true;                 gcastable = false;                 return false;             }     }     return gcastable; }  public void setcastok(activity mainactivity, boolean result ) {     gcasttested = true;     gcastable = result; } 

and helper function check if know state of cast.

public boolean iscastavailableknown() {     return gcastable; } 

however cope devices return success, needed following code in app / singleton.

public boolean oncastresultreceived( activity act, int result ) {     boolean wasok = false;     if( result == connectionresult.success ){         try {             castcontext ctx = castcontext.getsharedinstance(act );             wasok = true;         } catch ( runtimeexception e ){             wasok = false;         }     }     if( wasok ) {         setcastok(act, true);         return true;     }else {         setcastok(act, false );         return false;     } } 

the inflation of mini controller disabled using viewstub , fragment...

fragment mini_controller_fragment.xml

<?xml version="1.0" encoding="utf-8"?> <fragment     xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     android:id="@+id/cast_mini_controller"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:layout_alignparentbottom="true"     android:visibility="gone"     app:castshowimagethumbnail="true"     class="com.google.android.gms.cast.framework.media.widget.minicontrollerfragment" /> 

with usage this....

    <viewstub         android:id="@+id/cast_mini_controller"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_alignparentbottom="true"         android:layout="@layout/mini_controller_fragment"         /> 

activity

an activity's interaction cast components looks this...

/* called when have found out cast compatible. */ private void oncastavailable() {     viewstub minicontrollerstub = (viewstub) findviewbyid(r.id.cast_mini_controller);     minicontrollerstub.inflate();    // inflated if cast compatible.     mcaststatelistener = new caststatelistener() {         @override         public void oncaststatechanged(int newstate) {             if (newstate != caststate.no_devices_available) {                 showintroductoryoverlay();             }             if (mqueuemenuitem != null) {                 mqueuemenuitem.setvisible(                         (mcastsession != null) && mcastsession.isconnected());             }         }     };     mcastcontext = castcontext.getsharedinstance(this);     if (mcastsession == null) {         mcastsession = mcastcontext.getsessionmanager()                 .getcurrentcastsession();     }     if (mqueuemenuitem != null) {         mqueuemenuitem.setvisible(                 (mcastsession != null) && mcastsession.isconnected());     } }   private void showintroductoryoverlay() {     if (moverlay != null) {         moverlay.remove();     }     if ((mediaroutemenuitem != null) && mediaroutemenuitem.isvisible()) {         new handler().post(new runnable() {             @override             public void run() {                 moverlay = new introductoryoverlay.builder(                         mainactivity.this, mediaroutemenuitem)                         .settitletext(getstring(r.string.introducing_cast))                         .setoverlaycolor(r.color.primary)                         .setsingletime()                         .setonoverlaydismissedlistener(                                 new introductoryoverlay.onoverlaydismissedlistener() {                                     @override                                     public void onoverlaydismissed() {                                         moverlay = null;                                     }                                 })                         .build();                 moverlay.show();             }         });     }  } 

oncreate modified below...

protected void oncreate(bundle savedinstancestate) {     super.oncreate(savedinstancestate);      setcontentview(r.layout.activity_main);     mapp = (myapplication)getapplication();     if( mapp.iscastavailable( (activity)this, gps_result )) {         oncastavailable();     }      ... } 

onactivityresult needs cope result google play services upgrade...

protected void onactivityresult(int requestcode, int resultcode, intent data) {     if( requestcode == gps_result ) {         if(mapp.oncastresultreceived( this, resultcode ) ){             oncastavailable();         } 

onresume

protected void onresume() {     if( mcastcontext != null && mcaststatelistener != null ) {         mcastcontext.addcaststatelistener(mcaststatelistener);         mcastcontext.getsessionmanager().addsessionmanagerlistener(                 msessionmanagerlistener, castsession.class);         if (mcastsession == null) {             mcastsession = castcontext.getsharedinstance(this).getsessionmanager()                     .getcurrentcastsession();         }         if (mqueuemenuitem != null) {             mqueuemenuitem.setvisible(                     (mcastsession != null) && mcastsession.isconnected());         }     }     super.onresume(); } 

onpause

protected void onpause() {     super.onpause();     if( mcastcontext != null && mcaststatelistener != null ) {         mcastcontext.removecaststatelistener(mcaststatelistener);         mcastcontext.getsessionmanager().removesessionmanagerlistener(                 msessionmanagerlistener, castsession.class);     } } 

the session manager listener in class...

private final sessionmanagerlistener<castsession> msessionmanagerlistener =         new mysessionmanagerlistener(); private class mysessionmanagerlistener implements sessionmanagerlistener<castsession> {      @override     public void onsessionended(castsession session, int error) {         if (session == mcastsession) {             mcastsession = null;         }         invalidateoptionsmenu();     }      @override     public void onsessionresumed(castsession session, boolean wassuspended) {         mcastsession = session;         invalidateoptionsmenu();     }      @override     public void onsessionstarted(castsession session, string sessionid) {         mcastsession = session;         invalidateoptionsmenu();     }      @override     public void onsessionstarting(castsession session) {     }      @override     public void onsessionstartfailed(castsession session, int error) {     }      @override     public void onsessionending(castsession session) {     }      @override     public void onsessionresuming(castsession session, string sessionid) {     }      @override     public void onsessionresumefailed(castsession session, int error) {     }      @override     public void onsessionsuspended(castsession session, int reason) {     } } 

ui interaction

finally change ui when cast available calling "known" function in application...

    int visibility = view.gone;     if( mapplication.iscastavailableknown( ) ) {         castsession castsession = castcontext.getsharedinstance(mapplication).getsessionmanager()                 .getcurrentcastsession();         if( castsession != null && castsession.isconnected() ){             visibility = view.visible;         }     }     viewholder.mmenu.setvisibility( visibility); 

Comments

Popular posts from this blog

ubuntu - PHP script to find files of certain extensions in a directory, returns populated array when run in browser, but empty array when run from terminal -

php - How can i create a user dashboard -

javascript - How to detect toggling of the fullscreen-toolbar in jQuery Mobile? -