You need a singleton / code in the Application something like 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;
/* This code is needed, so that the user doesn't get a
*
* your device is incompatible "OK"
*
* message, it isn't really "user actionable"
*/
case ConnectionResult.SERVICE_INVALID: // Result from 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 a helper function to check if we know the state of the cast.
public boolean isCastAvailableKnown() {
return gCastable;
}
However to cope with devices which return SUCCESS, I also needed the following code in the App / singleton.
When the Activity receives the cast result, we create a CastContext. The "hope" is, if the Application can create the CastContext, then the framework will succeed in the same way (the cause of the crash).
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 the mini controller is disabled by using a ViewStub and a 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 something like 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 with the cast components looks something like this...
/* called when we have found out that cast is compatible. */
private void onCastAvailable() {
ViewStub miniControllerStub = (ViewStub) findViewById(R.id.cast_mini_controller);
miniControllerStub.inflate(); // only inflated if Cast is 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 as 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 to cope with the result from the 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 the 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 I could change the UI when cast was available by calling the "known" function in my 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);