allow fine control on where to show the cast button

This commit is contained in:
Domingos Lopes 2016-04-21 03:11:13 -04:00
parent a7848f0576
commit 1a33830991
6 changed files with 191 additions and 32 deletions

View File

@ -4,16 +4,19 @@ import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.CallSuper;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import com.google.android.gms.cast.ApplicationMetadata;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.cast.CastConsumer;
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
import de.danoeh.antennapod.core.cast.SwitchableMediaRouteActionProvider;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@ -27,10 +30,8 @@ public abstract class CastEnabledActivity extends AppCompatActivity
public static final String TAG = "CastEnabledActivity";
protected CastManager castManager;
private final Object UI_COUNTER_LOCK = new Object();
private volatile boolean isResumed = false;
protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
protected volatile boolean isCastEnabled = false;
private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -41,7 +42,7 @@ public abstract class CastEnabledActivity extends AppCompatActivity
castManager = CastManager.getInstance();
castManager.addCastConsumer(castConsumer);
isCastEnabled = UserPreferences.isCastEnabled();
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
onCastConnectionChanged(castManager.isConnected());
}
@ -54,41 +55,34 @@ public abstract class CastEnabledActivity extends AppCompatActivity
}
@Override
@CallSuper
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.cast_enabled, menu);
castButtonVisibilityManager.setMenu(menu);
return true;
}
@Override
@CallSuper
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
mediaRouteActionProvider = castManager
.addMediaRouterButton(menu.findItem(R.id.media_route_menu_item));
mediaRouteActionProvider.setEnabled(isCastEnabled);
mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
return true;
}
@Override
protected void onResume() {
super.onResume();
synchronized (UI_COUNTER_LOCK) {
isResumed = true;
if (isCastEnabled) {
castManager.incrementUiCounter();
}
}
castButtonVisibilityManager.setResumed(true);
}
@Override
protected void onPause() {
super.onPause();
synchronized (UI_COUNTER_LOCK) {
isResumed = false;
if (isCastEnabled) {
castManager.decrementUiCounter();
}
}
castButtonVisibilityManager.setResumed(false);
}
@Override
@ -96,19 +90,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
boolean newValue = UserPreferences.isCastEnabled();
Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
synchronized (UI_COUNTER_LOCK) {
if (isCastEnabled != newValue && isResumed) {
if (newValue) {
castManager.incrementUiCounter();
} else {
castManager.decrementUiCounter();
}
}
isCastEnabled = newValue;
}
mediaRouteActionProvider.setEnabled(isCastEnabled);
castButtonVisibilityManager.setPrefEnabled(newValue);
// PlaybackService has its own listener, so if it's active we don't have to take action here.
if (!isCastEnabled && !PlaybackService.isRunning) {
if (!newValue && !PlaybackService.isRunning) {
CastManager.getInstance().disconnect();
}
}
@ -133,4 +117,86 @@ public abstract class CastEnabledActivity extends AppCompatActivity
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
}
/**
* Should be called by any activity or fragment for which the cast button should be shown.
*
* @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
*/
public final void requestCastButton(int showAsAction) {
castButtonVisibilityManager.requestCastButton(showAsAction);
}
private class CastButtonVisibilityManager {
private volatile boolean prefEnabled = false;
private volatile boolean viewRequested = false;
private volatile boolean resumed = false;
private Menu menu;
public synchronized void setPrefEnabled(boolean newValue) {
if (prefEnabled != newValue && resumed && viewRequested) {
if (newValue) {
castManager.incrementUiCounter();
} else {
castManager.decrementUiCounter();
}
}
prefEnabled = newValue;
if (mediaRouteActionProvider != null) {
mediaRouteActionProvider.setEnabled(prefEnabled && viewRequested);
}
}
public synchronized void setResumed(boolean newValue) {
if (resumed == newValue) {
Log.e(TAG, "resumed should never change to the same value");
return;
}
resumed = newValue;
if (prefEnabled && viewRequested) {
if (resumed) {
castManager.incrementUiCounter();
} else {
castManager.decrementUiCounter();
}
}
}
public synchronized void setViewRequested(boolean newValue) {
if (viewRequested != newValue && resumed && prefEnabled) {
if (newValue) {
castManager.incrementUiCounter();
} else {
castManager.decrementUiCounter();
}
}
viewRequested = newValue;
if (mediaRouteActionProvider != null) {
mediaRouteActionProvider.setEnabled(prefEnabled && viewRequested);
}
}
public synchronized boolean shouldEnable() {
return prefEnabled && viewRequested;
}
public void setMenu(Menu menu) {
setViewRequested(false);
this.menu = menu;
}
public void requestCastButton(int showAsAction) {
setViewRequested(true);
if (menu == null) {
Log.e(TAG, "Cast button requested without a menu");
return;
}
MenuItem item = menu.findItem(R.id.media_route_menu_item);
if (item == null) {
Log.e(TAG, "Cast button requested, but not inflated");
return;
}
MenuItemCompat.setShowAsAction(menu.findItem(R.id.media_route_menu_item), showAsAction);
}
}
}

View File

@ -20,6 +20,7 @@ import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
@ -503,6 +504,26 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
Glide.get(this).clearMemory();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean retVal = super.onCreateOptionsMenu(menu);
switch (getLastNavFragment()) {
case QueueFragment.TAG:
case EpisodesFragment.TAG:
requestCastButton(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return retVal;
case DownloadsFragment.TAG:
case PlaybackHistoryFragment.TAG:
case AddFeedFragment.TAG:
case SubscriptionFragment.TAG:
return retVal;
default:
requestCastButton(MenuItem.SHOW_AS_ACTION_NEVER);
return retVal;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (drawerToggle.onOptionsItemSelected(item)) {

View File

@ -281,6 +281,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mediaplayer, menu);
return true;

View File

@ -39,6 +39,7 @@ import org.apache.commons.lang3.ArrayUtils;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.event.DownloadEvent;
@ -311,6 +312,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if(!isAdded() || item == null) {
return;
}
((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
inflater.inflate(R.menu.feeditem_options, menu);
popupMenu = menu;
if (item.hasMedia()) {

View File

@ -5,7 +5,6 @@
<item
android:id="@+id/media_route_menu_item"
android:title="@string/cast_media_route_menu_title"
android:enabled="false"
custom:actionProviderClass="de.danoeh.antennapod.core.cast.SwitchableMediaRouteActionProvider"
custom:showAsAction="always"/>
custom:showAsAction="ifRoom"/>
</menu>

View File

@ -1,7 +1,15 @@
package de.danoeh.antennapod.core.cast;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.MediaRouteActionProvider;
import android.support.v7.app.MediaRouteChooserDialogFragment;
import android.support.v7.app.MediaRouteControllerDialogFragment;
import android.support.v7.media.MediaRouter;
import android.util.Log;
/**
* <p>Action Provider that extends {@link MediaRouteActionProvider} and allows the client to
@ -11,7 +19,12 @@ import android.support.v7.app.MediaRouteActionProvider;
* <code>setEnabled(true)</code>.</p>
*/
public class SwitchableMediaRouteActionProvider extends MediaRouteActionProvider {
public static final String TAG = "SwitchblMediaRtActProv";
private static final String CHOOSER_FRAGMENT_TAG =
"android.support.v7.mediarouter:MediaRouteChooserDialogFragment";
private static final String CONTROLLER_FRAGMENT_TAG =
"android.support.v7.mediarouter:MediaRouteControllerDialogFragment";
private boolean enabled;
public SwitchableMediaRouteActionProvider(Context context) {
@ -33,4 +46,61 @@ public class SwitchableMediaRouteActionProvider extends MediaRouteActionProvider
public boolean isVisible() {
return enabled && super.isVisible();
}
@Override
public boolean onPerformDefaultAction() {
if (!super.onPerformDefaultAction()) {
// there is no button, but we should still show the dialog if it's the case.
if (!isVisible()) {
return false;
}
FragmentManager fm = getFragmentManager();
if (fm == null) {
return false;
}
MediaRouter.RouteInfo route = MediaRouter.getInstance(getContext()).getSelectedRoute();
if (route.isDefault() || !route.matchesSelector(getRouteSelector())) {
if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) {
Log.w(TAG, "showDialog(): Route chooser dialog already showing!");
return false;
}
MediaRouteChooserDialogFragment f =
getDialogFactory().onCreateChooserDialogFragment();
f.setRouteSelector(getRouteSelector());
f.show(fm, CHOOSER_FRAGMENT_TAG);
} else {
if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) {
Log.w(TAG, "showDialog(): Route controller dialog already showing!");
return false;
}
MediaRouteControllerDialogFragment f =
getDialogFactory().onCreateControllerDialogFragment();
f.show(fm, CONTROLLER_FRAGMENT_TAG);
}
return true;
} else {
return true;
}
}
private FragmentManager getFragmentManager() {
Activity activity = getActivity();
if (activity instanceof FragmentActivity) {
return ((FragmentActivity)activity).getSupportFragmentManager();
}
return null;
}
private Activity getActivity() {
// Gross way of unwrapping the Activity so we can get the FragmentManager
Context context = getContext();
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
return (Activity)context;
}
context = ((ContextWrapper)context).getBaseContext();
}
return null;
}
}