Ensure sample runs on Gingerbread. (DO NOT MERGE)

Unfortunately, the video does not play due to a MediaPlayer
exception but at least this version does not crash.

Change-Id: I0baf9239a8a12f92b4805bab226f21f46fa63693
(cherry picked from commit 854e16afda)
This commit is contained in:
Jeff Brown
2013-08-22 18:15:04 -07:00
parent 1edc4427a1
commit 9299c6cdd8
4 changed files with 466 additions and 348 deletions

View File

@@ -16,16 +16,14 @@
package com.example.android.supportv7.media; package com.example.android.supportv7.media;
import com.example.android.supportv7.R;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.view.Surface; import android.view.Surface;
import android.view.Gravity; import android.view.SurfaceHolder;
import android.graphics.SurfaceTexture;
import java.io.IOException; import java.io.IOException;
/** /**
@@ -37,7 +35,6 @@ public class MediaPlayerWrapper implements
MediaPlayer.OnCompletionListener, MediaPlayer.OnCompletionListener,
MediaPlayer.OnErrorListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnSeekCompleteListener,
OverlayDisplayWindow.OverlayWindowListener,
MediaSessionManager.Callback { MediaSessionManager.Callback {
private static final String TAG = "MediaPlayerWrapper"; private static final String TAG = "MediaPlayerWrapper";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
@@ -54,6 +51,7 @@ public class MediaPlayerWrapper implements
private int mState = STATE_IDLE; private int mState = STATE_IDLE;
private Callback mCallback; private Callback mCallback;
private Surface mSurface; private Surface mSurface;
private SurfaceHolder mSurfaceHolder;
private int mSeekToPos; private int mSeekToPos;
public MediaPlayerWrapper(Context context) { public MediaPlayerWrapper(Context context) {
@@ -140,21 +138,16 @@ public class MediaPlayerWrapper implements
} }
} }
//OverlayDisplayWindow listeners public void setSurface(Surface surface) {
@Override
public void onWindowCreated(Surface surface) {
if (DEBUG) {
Log.d(TAG, "onWindowCreated");
}
mSurface = surface; mSurface = surface;
mMediaPlayer.setSurface(surface); mSurfaceHolder = null;
updateSurface();
} }
@Override public void setSurface(SurfaceHolder surfaceHolder) {
public void onWindowDestroyed() { mSurface = null;
if (DEBUG) { mSurfaceHolder = surfaceHolder;
Log.d(TAG, "onWindowDestroyed"); updateSurface();
}
} }
//MediaPlayer Listeners //MediaPlayer Listeners
@@ -244,9 +237,7 @@ public class MediaPlayerWrapper implements
mMediaPlayer.setOnCompletionListener(this); mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnErrorListener(this); mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnSeekCompleteListener(this); mMediaPlayer.setOnSeekCompleteListener(this);
if (mSurface != null) { updateSurface();
mMediaPlayer.setSurface(mSurface);
}
mState = STATE_IDLE; mState = STATE_IDLE;
mSeekToPos = 0; mSeekToPos = 0;
} }
@@ -265,10 +256,32 @@ public class MediaPlayerWrapper implements
} }
} }
private void updateSurface() {
if (mSurface != null) {
// The setSurface API does not exist until V14+.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ICSMediaPlayer.setSurface(mMediaPlayer, mSurface);
} else {
throw new UnsupportedOperationException("MediaPlayer does not support "
+ "setSurface() on this version of the platform.");
}
} else if (mSurfaceHolder != null) {
mMediaPlayer.setDisplay(mSurfaceHolder);
} else {
mMediaPlayer.setDisplay(null);
}
}
public static abstract class Callback { public static abstract class Callback {
public void onError() {} public void onError() {}
public void onCompletion() {} public void onCompletion() {}
public void onStatusChanged() {} public void onStatusChanged() {}
public void onSizeChanged(int width, int height) {} public void onSizeChanged(int width, int height) {}
} }
private static final class ICSMediaPlayer {
public static final void setSurface(MediaPlayer player, Surface surface) {
player.setSurface(surface);
}
}
} }

View File

@@ -18,8 +18,10 @@ package com.example.android.supportv7.media;
import com.example.android.supportv7.R; import com.example.android.supportv7.R;
import android.content.Context; import android.content.Context;
import android.graphics.Point;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.os.Build;
import android.util.Log; import android.util.Log;
import android.view.Display; import android.view.Display;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
@@ -28,6 +30,8 @@ import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView; import android.view.TextureView;
import android.view.View; import android.view.View;
import android.view.Surface; import android.view.Surface;
@@ -38,68 +42,38 @@ import android.widget.TextView;
/** /**
* Manages an overlay display window, used for simulating remote playback. * Manages an overlay display window, used for simulating remote playback.
*/ */
public class OverlayDisplayWindow { public abstract class OverlayDisplayWindow {
private static final String TAG = "OverlayDisplayWindow"; private static final String TAG = "OverlayDisplayWindow";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private final float INITIAL_SCALE = 0.5f; private static final float WINDOW_ALPHA = 0.8f;
private final float MIN_SCALE = 0.3f; private static final float INITIAL_SCALE = 0.5f;
private final float MAX_SCALE = 1.0f; private static final float MIN_SCALE = 0.3f;
private final float WINDOW_ALPHA = 0.8f; private static final float MAX_SCALE = 1.0f;
// When true, disables support for moving and resizing the overlay. protected final Context mContext;
// The window is made non-touchable, which makes it possible to protected final String mName;
// directly interact with the content underneath. protected final int mWidth;
private final boolean DISABLE_MOVE_AND_RESIZE = false; protected final int mHeight;
protected final int mGravity;
protected OverlayWindowListener mListener;
private final Context mContext; protected OverlayDisplayWindow(Context context, String name,
private final int mWidth;
private final int mHeight;
private final int mGravity;
private OverlayWindowListener mListener;
private final String mTitle;
private final DisplayManager mDisplayManager;
private final WindowManager mWindowManager;
private final Display mDefaultDisplay;
private final DisplayMetrics mDefaultDisplayMetrics = new DisplayMetrics();
private View mWindowContent;
private WindowManager.LayoutParams mWindowParams;
private TextureView mTextureView;
private TextView mTitleTextView;
private GestureDetector mGestureDetector;
private ScaleGestureDetector mScaleGestureDetector;
private boolean mWindowVisible;
private int mWindowX;
private int mWindowY;
private float mWindowScale;
private float mLiveTranslationX;
private float mLiveTranslationY;
private float mLiveScale = 1.0f;
public OverlayDisplayWindow(Context context, String name,
int width, int height, int gravity) { int width, int height, int gravity) {
mContext = context; mContext = context;
mName = name;
mWidth = width; mWidth = width;
mHeight = height; mHeight = height;
mGravity = gravity; mGravity = gravity;
mTitle = name; }
mDisplayManager = (DisplayManager)context.getSystemService( public static OverlayDisplayWindow create(Context context, String name,
Context.DISPLAY_SERVICE); int width, int height, int gravity) {
mWindowManager = (WindowManager)context.getSystemService( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Context.WINDOW_SERVICE); return new JellybeanMr1Impl(context, name, width, height, gravity);
} else {
mDefaultDisplay = mWindowManager.getDefaultDisplay(); return new LegacyImpl(context, name, width, height, gravity);
updateDefaultDisplayInfo(); }
createWindow();
} }
public void setOverlayWindowListener(OverlayWindowListener listener) { public void setOverlayWindowListener(OverlayWindowListener listener) {
@@ -110,253 +84,378 @@ public class OverlayDisplayWindow {
return mContext; return mContext;
} }
public void show() { public abstract void show();
if (!mWindowVisible) {
mDisplayManager.registerDisplayListener(mDisplayListener, null);
if (!updateDefaultDisplayInfo()) {
mDisplayManager.unregisterDisplayListener(mDisplayListener);
return;
}
clearLiveState(); public abstract void dismiss();
updateWindowParams();
mWindowManager.addView(mWindowContent, mWindowParams);
mWindowVisible = true;
}
}
public void dismiss() { public abstract void updateAspectRatio(int width, int height);
if (mWindowVisible) {
mDisplayManager.unregisterDisplayListener(mDisplayListener);
mWindowManager.removeView(mWindowContent);
mWindowVisible = false;
}
}
public void relayout() {
if (mWindowVisible) {
updateWindowParams();
mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
}
}
public void updateAspectRatio(int width, int height) {
if (mWidth * height < mHeight * width) {
mTextureView.getLayoutParams().width = mWidth;
mTextureView.getLayoutParams().height = mWidth * height / width;
} else {
mTextureView.getLayoutParams().width = mHeight * width / height;
mTextureView.getLayoutParams().height = mHeight;
}
relayout();
}
private boolean updateDefaultDisplayInfo() {
mDefaultDisplay.getMetrics(mDefaultDisplayMetrics);
return true;
}
private void createWindow() {
LayoutInflater inflater = LayoutInflater.from(mContext);
mWindowContent = inflater.inflate(
R.layout.overlay_display_window, null);
mWindowContent.setOnTouchListener(mOnTouchListener);
mTextureView = (TextureView)mWindowContent.findViewById(
R.id.overlay_display_window_texture);
mTextureView.setPivotX(0);
mTextureView.setPivotY(0);
mTextureView.getLayoutParams().width = mWidth;
mTextureView.getLayoutParams().height = mHeight;
mTextureView.setOpaque(false);
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
mTitleTextView = (TextView)mWindowContent.findViewById(
R.id.overlay_display_window_title);
mTitleTextView.setText(mTitle);
mWindowParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
if (DISABLE_MOVE_AND_RESIZE) {
mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
}
mWindowParams.alpha = WINDOW_ALPHA;
mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
mWindowParams.setTitle(mTitle);
mGestureDetector = new GestureDetector(mContext, mOnGestureListener);
mScaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener);
// Set the initial position and scale.
// The position and scale will be clamped when the display is first shown.
mWindowX = (mGravity & Gravity.LEFT) == Gravity.LEFT ?
0 : mDefaultDisplayMetrics.widthPixels;
mWindowY = (mGravity & Gravity.TOP) == Gravity.TOP ?
0 : mDefaultDisplayMetrics.heightPixels;
Log.d(TAG, mDefaultDisplayMetrics.toString());
mWindowScale = INITIAL_SCALE;
// calculate and save initial settings
updateWindowParams();
saveWindowParams();
}
private void updateWindowParams() {
float scale = mWindowScale * mLiveScale;
scale = Math.min(scale, (float)mDefaultDisplayMetrics.widthPixels / mWidth);
scale = Math.min(scale, (float)mDefaultDisplayMetrics.heightPixels / mHeight);
scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
int width = (int)(mWidth * scale);
int height = (int)(mHeight * scale);
int x = (int)(mWindowX + mLiveTranslationX - width * offsetScale);
int y = (int)(mWindowY + mLiveTranslationY - height * offsetScale);
x = Math.max(0, Math.min(x, mDefaultDisplayMetrics.widthPixels - width));
y = Math.max(0, Math.min(y, mDefaultDisplayMetrics.heightPixels - height));
if (DEBUG) {
Log.d(TAG, "updateWindowParams: scale=" + scale
+ ", offsetScale=" + offsetScale
+ ", x=" + x + ", y=" + y
+ ", width=" + width + ", height=" + height);
}
mTextureView.setScaleX(scale);
mTextureView.setScaleY(scale);
mTextureView.setTranslationX(
(mWidth - mTextureView.getLayoutParams().width) * scale / 2);
mTextureView.setTranslationY(
(mHeight - mTextureView.getLayoutParams().height) * scale / 2);
mWindowParams.x = x;
mWindowParams.y = y;
mWindowParams.width = width;
mWindowParams.height = height;
}
private void saveWindowParams() {
mWindowX = mWindowParams.x;
mWindowY = mWindowParams.y;
mWindowScale = mTextureView.getScaleX();
clearLiveState();
}
private void clearLiveState() {
mLiveTranslationX = 0f;
mLiveTranslationY = 0f;
mLiveScale = 1.0f;
}
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
}
@Override
public void onDisplayChanged(int displayId) {
if (displayId == mDefaultDisplay.getDisplayId()) {
if (updateDefaultDisplayInfo()) {
relayout();
} else {
dismiss();
}
}
}
@Override
public void onDisplayRemoved(int displayId) {
if (displayId == mDefaultDisplay.getDisplayId()) {
dismiss();
}
}
};
private final SurfaceTextureListener mSurfaceTextureListener =
new SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
if (mListener != null) {
mListener.onWindowCreated(new Surface(surfaceTexture));
}
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
if (mListener != null) {
mListener.onWindowDestroyed();
}
return true;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
int width, int height) {
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
};
private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
// Work in screen coordinates.
final float oldX = event.getX();
final float oldY = event.getY();
event.setLocation(event.getRawX(), event.getRawY());
mGestureDetector.onTouchEvent(event);
mScaleGestureDetector.onTouchEvent(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
saveWindowParams();
break;
}
// Revert to window coordinates.
event.setLocation(oldX, oldY);
return true;
}
};
private final GestureDetector.OnGestureListener mOnGestureListener =
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
mLiveTranslationX -= distanceX;
mLiveTranslationY -= distanceY;
relayout();
return true;
}
};
private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mLiveScale *= detector.getScaleFactor();
relayout();
return true;
}
};
// Watches for significant changes in the overlay display window lifecycle. // Watches for significant changes in the overlay display window lifecycle.
public interface OverlayWindowListener { public interface OverlayWindowListener {
public void onWindowCreated(Surface surface); public void onWindowCreated(Surface surface);
public void onWindowCreated(SurfaceHolder surfaceHolder);
public void onWindowDestroyed(); public void onWindowDestroyed();
} }
/**
* Implementation for older versions.
*/
private static final class LegacyImpl extends OverlayDisplayWindow {
private final WindowManager mWindowManager;
private boolean mWindowVisible;
private SurfaceView mSurfaceView;
public LegacyImpl(Context context, String name,
int width, int height, int gravity) {
super(context, name, width, height, gravity);
mWindowManager = (WindowManager)context.getSystemService(
Context.WINDOW_SERVICE);
}
@Override
public void show() {
if (!mWindowVisible) {
mSurfaceView = new SurfaceView(mContext);
Display display = mWindowManager.getDefaultDisplay();
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
params.alpha = WINDOW_ALPHA;
params.gravity = Gravity.LEFT | Gravity.BOTTOM;
params.setTitle(mName);
int width = (int)(display.getWidth() * INITIAL_SCALE);
int height = (int)(display.getHeight() * INITIAL_SCALE);
if (mWidth > mHeight) {
height = mHeight * width / mWidth;
} else {
width = mWidth * height / mHeight;
}
params.width = width;
params.height = height;
mWindowManager.addView(mSurfaceView, params);
mWindowVisible = true;
mListener.onWindowCreated(mSurfaceView.getHolder());
}
}
@Override
public void dismiss() {
if (mWindowVisible) {
mListener.onWindowDestroyed();
mWindowManager.removeView(mSurfaceView);
mWindowVisible = false;
}
}
@Override
public void updateAspectRatio(int width, int height) {
}
}
/**
* Implementation for API version 17+.
*/
private static final class JellybeanMr1Impl extends OverlayDisplayWindow {
// When true, disables support for moving and resizing the overlay.
// The window is made non-touchable, which makes it possible to
// directly interact with the content underneath.
private static final boolean DISABLE_MOVE_AND_RESIZE = false;
private final DisplayManager mDisplayManager;
private final WindowManager mWindowManager;
private final Display mDefaultDisplay;
private final DisplayMetrics mDefaultDisplayMetrics = new DisplayMetrics();
private View mWindowContent;
private WindowManager.LayoutParams mWindowParams;
private TextureView mTextureView;
private TextView mNameTextView;
private GestureDetector mGestureDetector;
private ScaleGestureDetector mScaleGestureDetector;
private boolean mWindowVisible;
private int mWindowX;
private int mWindowY;
private float mWindowScale;
private float mLiveTranslationX;
private float mLiveTranslationY;
private float mLiveScale = 1.0f;
public JellybeanMr1Impl(Context context, String name,
int width, int height, int gravity) {
super(context, name, width, height, gravity);
mDisplayManager = (DisplayManager)context.getSystemService(
Context.DISPLAY_SERVICE);
mWindowManager = (WindowManager)context.getSystemService(
Context.WINDOW_SERVICE);
mDefaultDisplay = mWindowManager.getDefaultDisplay();
updateDefaultDisplayInfo();
createWindow();
}
@Override
public void show() {
if (!mWindowVisible) {
mDisplayManager.registerDisplayListener(mDisplayListener, null);
if (!updateDefaultDisplayInfo()) {
mDisplayManager.unregisterDisplayListener(mDisplayListener);
return;
}
clearLiveState();
updateWindowParams();
mWindowManager.addView(mWindowContent, mWindowParams);
mWindowVisible = true;
}
}
@Override
public void dismiss() {
if (mWindowVisible) {
mDisplayManager.unregisterDisplayListener(mDisplayListener);
mWindowManager.removeView(mWindowContent);
mWindowVisible = false;
}
}
@Override
public void updateAspectRatio(int width, int height) {
if (mWidth * height < mHeight * width) {
mTextureView.getLayoutParams().width = mWidth;
mTextureView.getLayoutParams().height = mWidth * height / width;
} else {
mTextureView.getLayoutParams().width = mHeight * width / height;
mTextureView.getLayoutParams().height = mHeight;
}
relayout();
}
private void relayout() {
if (mWindowVisible) {
updateWindowParams();
mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
}
}
private boolean updateDefaultDisplayInfo() {
mDefaultDisplay.getMetrics(mDefaultDisplayMetrics);
return true;
}
private void createWindow() {
LayoutInflater inflater = LayoutInflater.from(mContext);
mWindowContent = inflater.inflate(
R.layout.overlay_display_window, null);
mWindowContent.setOnTouchListener(mOnTouchListener);
mTextureView = (TextureView)mWindowContent.findViewById(
R.id.overlay_display_window_texture);
mTextureView.setPivotX(0);
mTextureView.setPivotY(0);
mTextureView.getLayoutParams().width = mWidth;
mTextureView.getLayoutParams().height = mHeight;
mTextureView.setOpaque(false);
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
mNameTextView = (TextView)mWindowContent.findViewById(
R.id.overlay_display_window_title);
mNameTextView.setText(mName);
mWindowParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
if (DISABLE_MOVE_AND_RESIZE) {
mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
}
mWindowParams.alpha = WINDOW_ALPHA;
mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
mWindowParams.setTitle(mName);
mGestureDetector = new GestureDetector(mContext, mOnGestureListener);
mScaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener);
// Set the initial position and scale.
// The position and scale will be clamped when the display is first shown.
mWindowX = (mGravity & Gravity.LEFT) == Gravity.LEFT ?
0 : mDefaultDisplayMetrics.widthPixels;
mWindowY = (mGravity & Gravity.TOP) == Gravity.TOP ?
0 : mDefaultDisplayMetrics.heightPixels;
Log.d(TAG, mDefaultDisplayMetrics.toString());
mWindowScale = INITIAL_SCALE;
// calculate and save initial settings
updateWindowParams();
saveWindowParams();
}
private void updateWindowParams() {
float scale = mWindowScale * mLiveScale;
scale = Math.min(scale, (float)mDefaultDisplayMetrics.widthPixels / mWidth);
scale = Math.min(scale, (float)mDefaultDisplayMetrics.heightPixels / mHeight);
scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
int width = (int)(mWidth * scale);
int height = (int)(mHeight * scale);
int x = (int)(mWindowX + mLiveTranslationX - width * offsetScale);
int y = (int)(mWindowY + mLiveTranslationY - height * offsetScale);
x = Math.max(0, Math.min(x, mDefaultDisplayMetrics.widthPixels - width));
y = Math.max(0, Math.min(y, mDefaultDisplayMetrics.heightPixels - height));
if (DEBUG) {
Log.d(TAG, "updateWindowParams: scale=" + scale
+ ", offsetScale=" + offsetScale
+ ", x=" + x + ", y=" + y
+ ", width=" + width + ", height=" + height);
}
mTextureView.setScaleX(scale);
mTextureView.setScaleY(scale);
mTextureView.setTranslationX(
(mWidth - mTextureView.getLayoutParams().width) * scale / 2);
mTextureView.setTranslationY(
(mHeight - mTextureView.getLayoutParams().height) * scale / 2);
mWindowParams.x = x;
mWindowParams.y = y;
mWindowParams.width = width;
mWindowParams.height = height;
}
private void saveWindowParams() {
mWindowX = mWindowParams.x;
mWindowY = mWindowParams.y;
mWindowScale = mTextureView.getScaleX();
clearLiveState();
}
private void clearLiveState() {
mLiveTranslationX = 0f;
mLiveTranslationY = 0f;
mLiveScale = 1.0f;
}
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
}
@Override
public void onDisplayChanged(int displayId) {
if (displayId == mDefaultDisplay.getDisplayId()) {
if (updateDefaultDisplayInfo()) {
relayout();
} else {
dismiss();
}
}
}
@Override
public void onDisplayRemoved(int displayId) {
if (displayId == mDefaultDisplay.getDisplayId()) {
dismiss();
}
}
};
private final SurfaceTextureListener mSurfaceTextureListener =
new SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
if (mListener != null) {
mListener.onWindowCreated(new Surface(surfaceTexture));
}
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
if (mListener != null) {
mListener.onWindowDestroyed();
}
return true;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
int width, int height) {
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
};
private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
// Work in screen coordinates.
final float oldX = event.getX();
final float oldY = event.getY();
event.setLocation(event.getRawX(), event.getRawY());
mGestureDetector.onTouchEvent(event);
mScaleGestureDetector.onTouchEvent(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
saveWindowParams();
break;
}
// Revert to window coordinates.
event.setLocation(oldX, oldY);
return true;
}
};
private final GestureDetector.OnGestureListener mOnGestureListener =
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
mLiveTranslationX -= distanceX;
mLiveTranslationY -= distanceY;
relayout();
return true;
}
};
private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mLiveScale *= detector.getScaleFactor();
relayout();
return true;
}
};
}
} }

View File

@@ -35,8 +35,10 @@ import android.support.v7.media.MediaRouter.ControlRequestCallback;
import android.support.v7.media.MediaRouteProviderDescriptor; import android.support.v7.media.MediaRouteProviderDescriptor;
import android.support.v7.media.MediaRouteDescriptor; import android.support.v7.media.MediaRouteDescriptor;
import android.util.Log; import android.util.Log;
import android.widget.Toast;
import android.view.Gravity; import android.view.Gravity;
import android.view.Surface;
import android.view.SurfaceHolder;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@@ -188,26 +190,39 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
setDescriptor(providerDescriptor); setDescriptor(providerDescriptor);
} }
private void showToast(String msg) {
Toast toast = Toast.makeText(getContext(),
"[provider] " + msg, Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP, 0, 100);
toast.show();
}
private final class SampleRouteController extends MediaRouteProvider.RouteController { private final class SampleRouteController extends MediaRouteProvider.RouteController {
private final String mRouteId; private final String mRouteId;
// Create an overlay display window (used for simulating the remote playback only) private final OverlayDisplayWindow mOverlay;
private final OverlayDisplayWindow mOverlay = new OverlayDisplayWindow(getContext(), private final MediaPlayerWrapper mMediaPlayer;
getContext().getResources().getString(R.string.sample_media_route_provider_remote), private final MediaSessionManager mSessionManager;
1024, 768, Gravity.CENTER);
private final MediaPlayerWrapper mMediaPlayer = new MediaPlayerWrapper(getContext());
private final MediaSessionManager mSessionManager = new MediaSessionManager();
public SampleRouteController(String routeId) { public SampleRouteController(String routeId) {
mRouteId = routeId; mRouteId = routeId;
mMediaPlayer = new MediaPlayerWrapper(getContext());
mSessionManager = new MediaSessionManager();
mSessionManager.setCallback(mMediaPlayer); mSessionManager.setCallback(mMediaPlayer);
mOverlay.setOverlayWindowListener(mMediaPlayer);
// Create an overlay display window (used for simulating the remote playback only)
mOverlay = OverlayDisplayWindow.create(getContext(),
getContext().getResources().getString(
R.string.sample_media_route_provider_remote),
1024, 768, Gravity.CENTER);
mOverlay.setOverlayWindowListener(new OverlayDisplayWindow.OverlayWindowListener() {
@Override
public void onWindowCreated(Surface surface) {
mMediaPlayer.setSurface(surface);
}
@Override
public void onWindowCreated(SurfaceHolder surfaceHolder) {
mMediaPlayer.setSurface(surfaceHolder);
}
@Override
public void onWindowDestroyed() {
}
});
mMediaPlayer.setCallback(new MediaPlayerCallback()); mMediaPlayer.setCallback(new MediaPlayerCallback());
Log.d(TAG, mRouteId + ": Controller created"); Log.d(TAG, mRouteId + ": Controller created");
} }

View File

@@ -31,7 +31,6 @@ import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener; import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaMetadataRetriever; import android.media.MediaMetadataRetriever;
import android.media.RemoteControlClient; import android.media.RemoteControlClient;
import android.media.RemoteControlClient.MetadataEditor;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
@@ -58,14 +57,12 @@ import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Display; import android.view.Display;
import android.view.Surface;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
@@ -78,8 +75,6 @@ import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.SeekBar.OnSeekBarChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.io.File; import java.io.File;
/** /**
@@ -898,7 +893,7 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
public void onFinish(boolean error) { public void onFinish(boolean error) {
MediaQueueItem item = mSessionManager.finish(error); MediaQueueItem item = mSessionManager.finish(error);
updateUi(); updateUi();
if (error) { if (error && item != null) {
showToast("Failed to play item " + item.getUri()); showToast("Failed to play item " + item.getUri());
} }
} }
@@ -908,13 +903,13 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
public void surfaceChanged(SurfaceHolder holder, int format, public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) { int width, int height) {
Log.d(TAG, "surfaceChanged "+width+"x"+height); Log.d(TAG, "surfaceChanged "+width+"x"+height);
mMediaPlayer.onWindowCreated(holder.getSurface()); mMediaPlayer.setSurface(holder);
} }
@Override @Override
public void surfaceCreated(SurfaceHolder holder) { public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "surfaceCreated"); Log.d(TAG, "surfaceCreated");
mMediaPlayer.onWindowCreated(holder.getSurface()); mMediaPlayer.setSurface(holder);
mLocalPlayer.updateSize(mVideoWidth, mVideoHeight); mLocalPlayer.updateSize(mVideoWidth, mVideoHeight);
} }
@@ -1007,7 +1002,7 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
}; };
private final class DemoPresentation extends Presentation { private final class DemoPresentation extends Presentation {
private SurfaceView mSurfaceView; private SurfaceView mPresentationSurfaceView;
public DemoPresentation(Context context, Display display) { public DemoPresentation(Context context, Display display) {
super(context, display); super(context, display);
@@ -1026,16 +1021,16 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
// Inflate the layout. // Inflate the layout.
setContentView(R.layout.sample_media_router_presentation); setContentView(R.layout.sample_media_router_presentation);
// Set up the surface view for visual interest. // Set up the surface view.
mSurfaceView = (SurfaceView)findViewById(R.id.surface_view); mPresentationSurfaceView = (SurfaceView)findViewById(R.id.surface_view);
SurfaceHolder holder = mSurfaceView.getHolder(); SurfaceHolder holder = mPresentationSurfaceView.getHolder();
holder.addCallback(mLocalPlayer); holder.addCallback(mLocalPlayer);
} }
public void updateSize(int width, int height) { public void updateSize(int width, int height) {
int surfaceHeight=getWindow().getDecorView().getHeight(); int surfaceHeight = getWindow().getDecorView().getHeight();
int surfaceWidth=getWindow().getDecorView().getWidth(); int surfaceWidth = getWindow().getDecorView().getWidth();
ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams(); ViewGroup.LayoutParams lp = mPresentationSurfaceView.getLayoutParams();
if (surfaceWidth * height < surfaceHeight * width) { if (surfaceWidth * height < surfaceHeight * width) {
lp.width = surfaceWidth; lp.width = surfaceWidth;
lp.height = surfaceWidth * height / width; lp.height = surfaceWidth * height / width;
@@ -1043,12 +1038,8 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
lp.width = surfaceHeight * width / height; lp.width = surfaceHeight * width / height;
lp.height = surfaceHeight; lp.height = surfaceHeight;
} }
Log.d(TAG, "video rect is "+lp.width+"x"+lp.height); Log.d(TAG, "video rect is " + lp.width + "x" + lp.height);
mSurfaceView.setLayoutParams(lp); mPresentationSurfaceView.setLayoutParams(lp);
}
public void clearContent() {
//TO-DO: clear surface view
} }
} }
} }