| /* |
| * Copyright (C) 2008 Esmertec AG. |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.mms.ui; |
| |
| import java.io.IOException; |
| import java.util.Comparator; |
| import java.util.Map; |
| import java.util.TreeMap; |
| |
| import android.content.Context; |
| import android.graphics.Bitmap; |
| import android.graphics.BitmapFactory; |
| import android.media.MediaPlayer; |
| import android.net.Uri; |
| import android.text.method.HideReturnsTransformationMethod; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.view.Gravity; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.widget.AbsoluteLayout; |
| import android.widget.FrameLayout; |
| import android.widget.ImageView; |
| import android.widget.LinearLayout; |
| import android.widget.MediaController; |
| import android.widget.ScrollView; |
| import android.widget.TextView; |
| import android.widget.VideoView; |
| |
| import com.android.mms.R; |
| import com.android.mms.layout.LayoutManager; |
| |
| /** |
| * A basic view to show the contents of a slide. |
| */ |
| public class SlideView extends AbsoluteLayout implements |
| AdaptableSlideViewInterface { |
| private static final String TAG = "SlideView"; |
| private static final boolean DEBUG = false; |
| private static final boolean LOCAL_LOGV = false; |
| // FIXME: Need getHeight from mAudioInfoView instead of constant AUDIO_INFO_HEIGHT. |
| private static final int AUDIO_INFO_HEIGHT = 82; |
| |
| private View mAudioInfoView; |
| private ImageView mImageView; |
| private VideoView mVideoView; |
| private ScrollView mScrollText; |
| private TextView mTextView; |
| private OnSizeChangedListener mSizeChangedListener; |
| private MediaPlayer mAudioPlayer; |
| private boolean mIsPrepared; |
| private boolean mStartWhenPrepared; |
| private int mSeekWhenPrepared; |
| private boolean mStopWhenPrepared; |
| private ScrollView mScrollViewPort; |
| private LinearLayout mViewPort; |
| // Indicates whether the view is in MMS conformance mode. |
| private boolean mConformanceMode; |
| private MediaController mMediaController; |
| |
| MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() { |
| public void onPrepared(MediaPlayer mp) { |
| mIsPrepared = true; |
| if (mSeekWhenPrepared > 0) { |
| mAudioPlayer.seekTo(mSeekWhenPrepared); |
| mSeekWhenPrepared = 0; |
| } |
| if (mStartWhenPrepared) { |
| mAudioPlayer.start(); |
| mStartWhenPrepared = false; |
| displayAudioInfo(); |
| } |
| if (mStopWhenPrepared) { |
| mAudioPlayer.stop(); |
| mAudioPlayer.release(); |
| mAudioPlayer = null; |
| mStopWhenPrepared = false; |
| hideAudioInfo(); |
| } |
| } |
| }; |
| |
| public SlideView(Context context) { |
| super(context); |
| } |
| |
| public SlideView(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| } |
| |
| public void setImage(String name, Bitmap bitmap) { |
| if (mImageView == null) { |
| mImageView = new ImageView(mContext); |
| mImageView.setPadding(0, 5, 0, 5); |
| addView(mImageView, new LayoutParams( |
| LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0, 0)); |
| if (DEBUG) { |
| mImageView.setBackgroundColor(0xFFFF0000); |
| } |
| } |
| try { |
| if (null == bitmap) { |
| bitmap = BitmapFactory.decodeResource(getResources(), |
| R.drawable.ic_missing_thumbnail_picture); |
| } |
| mImageView.setVisibility(View.VISIBLE); |
| mImageView.setImageBitmap(bitmap); |
| } catch (java.lang.OutOfMemoryError e) { |
| Log.e(TAG, "setImage: out of memory: ", e); |
| } |
| } |
| |
| public void setImageRegion(int left, int top, int width, int height) { |
| // Ignore any requirement of layout change once we are in MMS conformance mode. |
| if (mImageView != null && !mConformanceMode) { |
| mImageView.setLayoutParams(new LayoutParams(width, height, left, top)); |
| } |
| } |
| |
| public void setImageRegionFit(String fit) { |
| // TODO Auto-generated method stub |
| } |
| |
| public void setVideo(String name, Uri video) { |
| if (mVideoView == null) { |
| mVideoView = new VideoView(mContext); |
| addView(mVideoView, new LayoutParams( |
| LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0)); |
| if (DEBUG) { |
| mVideoView.setBackgroundColor(0xFFFF0000); |
| } |
| } |
| |
| if (LOCAL_LOGV) { |
| Log.v(TAG, "Changing video source to " + video); |
| } |
| mVideoView.setVisibility(View.VISIBLE); |
| mVideoView.setVideoURI(video); |
| } |
| |
| public void setMediaController(MediaController mediaController) { |
| mMediaController = mediaController; |
| } |
| |
| private void initAudioInfoView(String name) { |
| if (null == mAudioInfoView) { |
| LayoutInflater factory = LayoutInflater.from(getContext()); |
| mAudioInfoView = factory.inflate(R.layout.playing_audio_info, null); |
| int height = mAudioInfoView.getHeight(); |
| if (mConformanceMode) { |
| mViewPort.addView(mAudioInfoView, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, |
| AUDIO_INFO_HEIGHT)); |
| } else { |
| addView(mAudioInfoView, new LayoutParams( |
| LayoutParams.MATCH_PARENT, AUDIO_INFO_HEIGHT, |
| 0, getHeight() - AUDIO_INFO_HEIGHT)); |
| if (DEBUG) { |
| mAudioInfoView.setBackgroundColor(0xFFFF0000); |
| } |
| } |
| } |
| TextView audioName = (TextView) mAudioInfoView.findViewById(R.id.name); |
| audioName.setText(name); |
| mAudioInfoView.setVisibility(View.GONE); |
| } |
| |
| private void displayAudioInfo() { |
| if (null != mAudioInfoView) { |
| mAudioInfoView.setVisibility(View.VISIBLE); |
| } |
| } |
| |
| private void hideAudioInfo() { |
| if (null != mAudioInfoView) { |
| mAudioInfoView.setVisibility(View.GONE); |
| } |
| } |
| |
| public void setAudio(Uri audio, String name, Map<String, ?> extras) { |
| if (audio == null) { |
| throw new IllegalArgumentException("Audio URI may not be null."); |
| } |
| |
| if (LOCAL_LOGV) { |
| Log.v(TAG, "Changing audio source to " + audio); |
| } |
| |
| if (mAudioPlayer != null) { |
| mAudioPlayer.reset(); |
| mAudioPlayer.release(); |
| mAudioPlayer = null; |
| } |
| |
| // Reset state variables |
| mIsPrepared = false; |
| mStartWhenPrepared = false; |
| mSeekWhenPrepared = 0; |
| mStopWhenPrepared = false; |
| |
| try { |
| mAudioPlayer = new MediaPlayer(); |
| mAudioPlayer.setOnPreparedListener(mPreparedListener); |
| mAudioPlayer.setDataSource(mContext, audio); |
| mAudioPlayer.prepareAsync(); |
| } catch (IOException e) { |
| Log.e(TAG, "Unexpected IOException.", e); |
| mAudioPlayer.release(); |
| mAudioPlayer = null; |
| } |
| initAudioInfoView(name); |
| } |
| |
| public void setText(String name, String text) { |
| if (!mConformanceMode) { |
| if (null == mScrollText) { |
| mScrollText = new ScrollView(mContext); |
| mScrollText.setScrollBarStyle(SCROLLBARS_OUTSIDE_INSET); |
| addView(mScrollText, new LayoutParams( |
| LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0)); |
| if (DEBUG) { |
| mScrollText.setBackgroundColor(0xFF00FF00); |
| } |
| } |
| if (null == mTextView) { |
| mTextView = new TextView(mContext); |
| mTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); |
| mScrollText.addView(mTextView); |
| } |
| mScrollText.requestFocus(); |
| } |
| mTextView.setVisibility(View.VISIBLE); |
| mTextView.setText(text); |
| // Let the text in Mms can be selected. |
| mTextView.setTextIsSelectable(true); |
| } |
| |
| public void setTextRegion(int left, int top, int width, int height) { |
| // Ignore any requirement of layout change once we are in MMS conformance mode. |
| if (mScrollText != null && !mConformanceMode) { |
| mScrollText.setLayoutParams(new LayoutParams(width, height, left, top)); |
| } |
| } |
| |
| public void setVideoRegion(int left, int top, int width, int height) { |
| if (mVideoView != null && !mConformanceMode) { |
| mVideoView.setLayoutParams(new LayoutParams(width, height, left, top)); |
| } |
| } |
| |
| public void setImageVisibility(boolean visible) { |
| if (mImageView != null) { |
| if (mConformanceMode) { |
| mImageView.setVisibility(visible ? View.VISIBLE : View.GONE); |
| } else { |
| mImageView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); |
| } |
| } |
| } |
| |
| public void setTextVisibility(boolean visible) { |
| if (mConformanceMode) { |
| if (mTextView != null) { |
| mTextView.setVisibility(visible ? View.VISIBLE : View.GONE); |
| } |
| } else if (mScrollText != null) { |
| mScrollText.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); |
| } |
| } |
| |
| public void setVideoVisibility(boolean visible) { |
| if (mVideoView != null) { |
| if (mConformanceMode) { |
| mVideoView.setVisibility(visible ? View.VISIBLE : View.GONE); |
| } else { |
| mVideoView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); |
| } |
| } |
| } |
| |
| public void startAudio() { |
| if ((mAudioPlayer != null) && mIsPrepared) { |
| mAudioPlayer.start(); |
| mStartWhenPrepared = false; |
| displayAudioInfo(); |
| } else { |
| mStartWhenPrepared = true; |
| } |
| } |
| |
| public void stopAudio() { |
| if ((mAudioPlayer != null) && mIsPrepared) { |
| mAudioPlayer.stop(); |
| mAudioPlayer.release(); |
| mAudioPlayer = null; |
| hideAudioInfo(); |
| } else { |
| mStopWhenPrepared = true; |
| } |
| } |
| |
| public void pauseAudio() { |
| if ((mAudioPlayer != null) && mIsPrepared) { |
| if (mAudioPlayer.isPlaying()) { |
| mAudioPlayer.pause(); |
| } |
| } |
| mStartWhenPrepared = false; |
| } |
| |
| public void seekAudio(int seekTo) { |
| if ((mAudioPlayer != null) && mIsPrepared) { |
| mAudioPlayer.seekTo(seekTo); |
| } else { |
| mSeekWhenPrepared = seekTo; |
| } |
| } |
| |
| public void startVideo() { |
| if (mVideoView != null) { |
| if (LOCAL_LOGV) { |
| Log.v(TAG, "Starting video playback."); |
| } |
| mVideoView.start(); |
| } |
| } |
| |
| public void stopVideo() { |
| if ((mVideoView != null)) { |
| if (LOCAL_LOGV) { |
| Log.v(TAG, "Stopping video playback."); |
| } |
| mVideoView.stopPlayback(); |
| } |
| } |
| |
| public void pauseVideo() { |
| if (mVideoView != null) { |
| if (LOCAL_LOGV) { |
| Log.v(TAG, "Pausing video playback."); |
| } |
| mVideoView.pause(); |
| } |
| } |
| |
| public void seekVideo(int seekTo) { |
| if (mVideoView != null) { |
| if (seekTo > 0) { |
| if (LOCAL_LOGV) { |
| Log.v(TAG, "Seeking video playback to " + seekTo); |
| } |
| mVideoView.seekTo(seekTo); |
| } |
| } |
| } |
| |
| public void reset() { |
| if (null != mScrollText) { |
| mScrollText.setVisibility(View.GONE); |
| } |
| |
| if (null != mImageView) { |
| mImageView.setVisibility(View.GONE); |
| } |
| |
| if (null != mAudioPlayer) { |
| stopAudio(); |
| } |
| |
| if (null != mVideoView) { |
| stopVideo(); |
| mVideoView.setVisibility(View.GONE); |
| } |
| |
| if (null != mTextView) { |
| mTextView.setVisibility(View.GONE); |
| } |
| |
| if (mScrollViewPort != null) { |
| mScrollViewPort.scrollTo(0, 0); |
| mScrollViewPort.setLayoutParams( |
| new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0, 0)); |
| } |
| |
| } |
| |
| public void setVisibility(boolean visible) { |
| // TODO Auto-generated method stub |
| } |
| |
| @Override |
| protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
| super.onSizeChanged(w, h, oldw, oldh); |
| |
| if (mSizeChangedListener != null) { |
| if (LOCAL_LOGV) { |
| Log.v(TAG, "new size=" + w + "x" + h); |
| } |
| mSizeChangedListener.onSizeChanged(w, h - AUDIO_INFO_HEIGHT); |
| } |
| } |
| |
| public void setOnSizeChangedListener(OnSizeChangedListener l) { |
| mSizeChangedListener = l; |
| } |
| |
| private class Position { |
| public Position(int left, int top) { |
| mTop = top; |
| mLeft = left; |
| } |
| public int mTop; |
| public int mLeft; |
| } |
| |
| /** |
| * Makes the SlideView working on MMSConformance Mode. The view will be |
| * re-layout to the linear view. |
| * <p> |
| * This is Chinese requirement about mms conformance. |
| * The most popular Mms service in China is newspaper which is MMS conformance, |
| * normally it mixes the image and text and has a number of slides. The |
| * AbsoluteLayout doesn't have good user experience for this kind of message, |
| * for example, |
| * |
| * 1. AbsoluteLayout exactly follows the smil's layout which is not optimized, |
| * and actually, no other MMS applications follow the smil's layout, they adjust |
| * the layout according their screen size. MMS conformance doc also allows the |
| * implementation to adjust the layout. |
| * |
| * 2. The TextView is fixed in the small area of screen, and other part of screen |
| * is empty once there is no image in the current slide. |
| * |
| * 3. The TextView is scrollable in a small area of screen and the font size is |
| * small which make the user experience bad. |
| * |
| * The better UI for the MMS conformance message could be putting the image/video |
| * and text in a linear layout view and making them scrollable together. |
| * |
| * Another reason for only applying the LinearLayout to the MMS conformance message |
| * is that the AbsoluteLayout has ability to play image and video in a same screen. |
| * which shouldn't be broken. |
| */ |
| public void enableMMSConformanceMode(int textLeft, int textTop, |
| int imageLeft, int imageTop) { |
| mConformanceMode = true; |
| if (mScrollViewPort == null) { |
| mScrollViewPort = new ScrollView(mContext) { |
| private int mBottomY; |
| @Override |
| protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
| super.onLayout(changed, left, top, right, bottom); |
| if (getChildCount() > 0) { |
| int childHeight = getChildAt(0).getHeight(); |
| int height = getHeight(); |
| mBottomY = height < childHeight ? childHeight - height : 0; |
| } |
| } |
| @Override |
| protected void onScrollChanged(int l, int t, int oldl, int oldt) { |
| // Shows MediaController when the view is scrolled to the top/bottom of itself. |
| if (t == 0 || t >= mBottomY){ |
| if (mMediaController != null |
| && !((SlideshowActivity) mContext).isFinishing()) { |
| mMediaController.show(); |
| } |
| } |
| } |
| }; |
| mScrollViewPort.setScrollBarStyle(SCROLLBARS_INSIDE_OVERLAY); |
| mViewPort = new LinearLayout(mContext); |
| mViewPort.setOrientation(LinearLayout.VERTICAL); |
| mViewPort.setGravity(Gravity.CENTER); |
| mViewPort.setOnClickListener(new OnClickListener() { |
| public void onClick(View v) { |
| if (mMediaController != null) { |
| mMediaController.show(); |
| } |
| } |
| }); |
| mScrollViewPort.addView(mViewPort, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, |
| LayoutParams.WRAP_CONTENT)); |
| addView(mScrollViewPort); |
| } |
| // Layout views to fit the LinearLayout from left to right, then top to |
| // bottom. |
| TreeMap<Position, View> viewsByPosition = new TreeMap<Position, View>(new Comparator<Position>() { |
| public int compare(Position p1, Position p2) { |
| int l1 = p1.mLeft; |
| int t1 = p1.mTop; |
| int l2 = p2.mLeft; |
| int t2 = p2.mTop; |
| int res = t1 - t2; |
| if (res == 0) { |
| res = l1 - l2; |
| } |
| if (res == 0) { |
| // A view will be lost if return 0. |
| return -1; |
| } |
| return res; |
| } |
| }); |
| if (textLeft >=0 && textTop >=0) { |
| mTextView = new TextView(mContext); |
| mTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); |
| mTextView.setTextSize(18); |
| mTextView.setPadding(5, 5, 5, 5); |
| viewsByPosition.put(new Position(textLeft, textTop), mTextView); |
| } |
| |
| if (imageLeft >=0 && imageTop >=0) { |
| mImageView = new ImageView(mContext); |
| mImageView.setPadding(0, 5, 0, 5); |
| viewsByPosition.put(new Position(imageLeft, imageTop), mImageView); |
| // According MMS Conformance Document, the image and video should use the same |
| // region. So, put the VideoView below the ImageView. |
| mVideoView = new VideoView(mContext); |
| viewsByPosition.put(new Position(imageLeft + 1, imageTop), mVideoView); |
| } |
| for (View view : viewsByPosition.values()) { |
| if (view instanceof VideoView) { |
| mViewPort.addView(view, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, |
| LayoutManager.getInstance().getLayoutParameters().getHeight())); |
| } else { |
| mViewPort.addView(view, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, |
| LayoutParams.WRAP_CONTENT)); |
| } |
| view.setVisibility(View.GONE); |
| } |
| } |
| |
| public void setVideoThumbnail(String name, Bitmap bitmap) { |
| } |
| } |