• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Esmertec AG.
3  * Copyright (C) 2008 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.mms.ui;
19 
20 import com.android.mms.R;
21 import com.android.mms.layout.LayoutManager;
22 
23 import android.content.Context;
24 import android.graphics.Bitmap;
25 import android.graphics.BitmapFactory;
26 import android.media.MediaPlayer;
27 import android.net.Uri;
28 import android.text.method.HideReturnsTransformationMethod;
29 import android.util.AttributeSet;
30 import android.util.Log;
31 import android.view.Gravity;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.widget.AbsoluteLayout;
35 import android.widget.FrameLayout;
36 import android.widget.ImageView;
37 import android.widget.LinearLayout;
38 import android.widget.MediaController;
39 import android.widget.ScrollView;
40 import android.widget.TextView;
41 import android.widget.VideoView;
42 
43 import java.io.IOException;
44 import java.util.Comparator;
45 import java.util.Map;
46 import java.util.TreeMap;
47 
48 /**
49  * A basic view to show the contents of a slide.
50  */
51 public class SlideView extends AbsoluteLayout implements
52         AdaptableSlideViewInterface {
53     private static final String TAG = "SlideView";
54     private static final boolean DEBUG = false;
55     private static final boolean LOCAL_LOGV = false;
56     // FIXME: Need getHeight from mAudioInfoView instead of constant AUDIO_INFO_HEIGHT.
57     private static final int AUDIO_INFO_HEIGHT = 82;
58 
59     private View mAudioInfoView;
60     private ImageView mImageView;
61     private VideoView mVideoView;
62     private ScrollView mScrollText;
63     private TextView mTextView;
64     private OnSizeChangedListener mSizeChangedListener;
65     private MediaPlayer mAudioPlayer;
66     private boolean mIsPrepared;
67     private boolean mStartWhenPrepared;
68     private int     mSeekWhenPrepared;
69     private boolean mStopWhenPrepared;
70     private ScrollView mScrollViewPort;
71     private LinearLayout mViewPort;
72     // Indicates whether the view is in MMS conformance mode.
73     private boolean mConformanceMode;
74     private MediaController mMediaController;
75 
76     MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
77         public void onPrepared(MediaPlayer mp) {
78             mIsPrepared = true;
79             if (mSeekWhenPrepared > 0) {
80                 mAudioPlayer.seekTo(mSeekWhenPrepared);
81                 mSeekWhenPrepared = 0;
82             }
83             if (mStartWhenPrepared) {
84                 mAudioPlayer.start();
85                 mStartWhenPrepared = false;
86                 displayAudioInfo();
87             }
88             if (mStopWhenPrepared) {
89                 mAudioPlayer.stop();
90                 mAudioPlayer.release();
91                 mAudioPlayer = null;
92                 mStopWhenPrepared = false;
93                 hideAudioInfo();
94             }
95         }
96     };
97 
SlideView(Context context)98     public SlideView(Context context) {
99         super(context);
100     }
101 
SlideView(Context context, AttributeSet attrs)102     public SlideView(Context context, AttributeSet attrs) {
103         super(context, attrs);
104     }
105 
setImage(String name, Bitmap bitmap)106     public void setImage(String name, Bitmap bitmap) {
107         if (mImageView == null) {
108             mImageView = new ImageView(mContext);
109             mImageView.setPadding(0, 5, 0, 5);
110             addView(mImageView, new LayoutParams(
111                     LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0));
112             if (DEBUG) {
113                 mImageView.setBackgroundColor(0xFFFF0000);
114             }
115         }
116         try {
117             if (null == bitmap) {
118                 bitmap = BitmapFactory.decodeResource(getResources(),
119                         R.drawable.ic_missing_thumbnail_picture);
120             }
121             mImageView.setVisibility(View.VISIBLE);
122             mImageView.setImageBitmap(bitmap);
123         } catch (java.lang.OutOfMemoryError e) {
124             Log.e(TAG, "setImage: out of memory: ", e);
125         }
126     }
127 
setImageRegion(int left, int top, int width, int height)128     public void setImageRegion(int left, int top, int width, int height) {
129         // Ignore any requirement of layout change once we are in MMS conformance mode.
130         if (mImageView != null && !mConformanceMode) {
131             mImageView.setLayoutParams(new LayoutParams(width, height, left, top));
132         }
133     }
134 
setImageRegionFit(String fit)135     public void setImageRegionFit(String fit) {
136         // TODO Auto-generated method stub
137     }
138 
setVideo(String name, Uri video)139     public void setVideo(String name, Uri video) {
140         if (mVideoView == null) {
141             mVideoView = new VideoView(mContext);
142             addView(mVideoView, new LayoutParams(
143                     LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0));
144             if (DEBUG) {
145                 mVideoView.setBackgroundColor(0xFFFF0000);
146             }
147         }
148 
149         if (LOCAL_LOGV) {
150             Log.v(TAG, "Changing video source to " + video);
151         }
152         mVideoView.setVisibility(View.VISIBLE);
153         mVideoView.setVideoURI(video);
154     }
155 
setMediaController(MediaController mediaController)156     public void setMediaController(MediaController mediaController) {
157         mMediaController = mediaController;
158     }
159 
initAudioInfoView(String name)160     private void initAudioInfoView(String name) {
161         if (null == mAudioInfoView) {
162             LayoutInflater factory = LayoutInflater.from(getContext());
163             mAudioInfoView = factory.inflate(R.layout.playing_audio_info, null);
164             int height = mAudioInfoView.getHeight();
165             if (mConformanceMode) {
166                 mViewPort.addView(mAudioInfoView, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
167                         AUDIO_INFO_HEIGHT));
168             } else {
169                 addView(mAudioInfoView, new LayoutParams(
170                         LayoutParams.MATCH_PARENT, AUDIO_INFO_HEIGHT,
171                         0, getHeight() - AUDIO_INFO_HEIGHT));
172                 if (DEBUG) {
173                     mAudioInfoView.setBackgroundColor(0xFFFF0000);
174                 }
175             }
176         }
177         TextView audioName = (TextView) mAudioInfoView.findViewById(R.id.name);
178         audioName.setText(name);
179         mAudioInfoView.setVisibility(View.GONE);
180     }
181 
displayAudioInfo()182     private void displayAudioInfo() {
183         if (null != mAudioInfoView) {
184             mAudioInfoView.setVisibility(View.VISIBLE);
185         }
186     }
187 
hideAudioInfo()188     private void hideAudioInfo() {
189         if (null != mAudioInfoView) {
190             mAudioInfoView.setVisibility(View.GONE);
191         }
192     }
193 
setAudio(Uri audio, String name, Map<String, ?> extras)194     public void setAudio(Uri audio, String name, Map<String, ?> extras) {
195         if (audio == null) {
196             throw new IllegalArgumentException("Audio URI may not be null.");
197         }
198 
199         if (LOCAL_LOGV) {
200             Log.v(TAG, "Changing audio source to " + audio);
201         }
202 
203         if (mAudioPlayer != null) {
204             mAudioPlayer.reset();
205             mAudioPlayer.release();
206             mAudioPlayer = null;
207         }
208 
209         // Reset state variables
210         mIsPrepared = false;
211         mStartWhenPrepared = false;
212         mSeekWhenPrepared = 0;
213         mStopWhenPrepared = false;
214 
215         try {
216             mAudioPlayer = new MediaPlayer();
217             mAudioPlayer.setOnPreparedListener(mPreparedListener);
218             mAudioPlayer.setDataSource(mContext, audio);
219             mAudioPlayer.prepareAsync();
220         } catch (IOException e) {
221             Log.e(TAG, "Unexpected IOException.", e);
222             mAudioPlayer.release();
223             mAudioPlayer = null;
224         }
225         initAudioInfoView(name);
226     }
227 
setText(String name, String text)228     public void setText(String name, String text) {
229         if (!mConformanceMode) {
230             if (null == mScrollText) {
231                 mScrollText = new ScrollView(mContext);
232                 mScrollText.setScrollBarStyle(SCROLLBARS_OUTSIDE_INSET);
233                 addView(mScrollText, new LayoutParams(
234                         LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0));
235                 if (DEBUG) {
236                     mScrollText.setBackgroundColor(0xFF00FF00);
237                 }
238             }
239             if (null == mTextView) {
240                 mTextView = new TextView(mContext);
241                 mTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
242                 mScrollText.addView(mTextView);
243             }
244             mScrollText.requestFocus();
245         }
246         mTextView.setVisibility(View.VISIBLE);
247         mTextView.setText(text);
248     }
249 
setTextRegion(int left, int top, int width, int height)250     public void setTextRegion(int left, int top, int width, int height) {
251         // Ignore any requirement of layout change once we are in MMS conformance mode.
252         if (mScrollText != null && !mConformanceMode) {
253             mScrollText.setLayoutParams(new LayoutParams(width, height, left, top));
254         }
255     }
256 
setVideoRegion(int left, int top, int width, int height)257     public void setVideoRegion(int left, int top, int width, int height) {
258         if (mVideoView != null && !mConformanceMode) {
259             mVideoView.setLayoutParams(new LayoutParams(width, height, left, top));
260         }
261     }
262 
setImageVisibility(boolean visible)263     public void setImageVisibility(boolean visible) {
264         if (mImageView != null) {
265             if (mConformanceMode) {
266                 mImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
267             } else {
268                 mImageView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
269             }
270         }
271     }
272 
setTextVisibility(boolean visible)273     public void setTextVisibility(boolean visible) {
274         if (mConformanceMode) {
275             if (mTextView != null) {
276                 mTextView.setVisibility(visible ? View.VISIBLE : View.GONE);
277             }
278         } else if (mScrollText != null) {
279             mScrollText.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
280         }
281     }
282 
setVideoVisibility(boolean visible)283     public void setVideoVisibility(boolean visible) {
284         if (mVideoView != null) {
285             if (mConformanceMode) {
286                 mVideoView.setVisibility(visible ? View.VISIBLE : View.GONE);
287             } else {
288                 mVideoView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
289             }
290         }
291     }
292 
startAudio()293     public void startAudio() {
294         if ((mAudioPlayer != null) && mIsPrepared) {
295             mAudioPlayer.start();
296             mStartWhenPrepared = false;
297             displayAudioInfo();
298         } else {
299             mStartWhenPrepared = true;
300         }
301     }
302 
stopAudio()303     public void stopAudio() {
304         if ((mAudioPlayer != null) && mIsPrepared) {
305             mAudioPlayer.stop();
306             mAudioPlayer.release();
307             mAudioPlayer = null;
308             hideAudioInfo();
309         } else {
310             mStopWhenPrepared = true;
311         }
312     }
313 
pauseAudio()314     public void pauseAudio() {
315         if ((mAudioPlayer != null) && mIsPrepared) {
316             if (mAudioPlayer.isPlaying()) {
317                 mAudioPlayer.pause();
318             }
319         }
320         mStartWhenPrepared = false;
321     }
322 
seekAudio(int seekTo)323     public void seekAudio(int seekTo) {
324         if ((mAudioPlayer != null) && mIsPrepared) {
325             mAudioPlayer.seekTo(seekTo);
326         } else {
327             mSeekWhenPrepared = seekTo;
328         }
329     }
330 
startVideo()331     public void startVideo() {
332         if (mVideoView != null) {
333             if (LOCAL_LOGV) {
334                 Log.v(TAG, "Starting video playback.");
335             }
336             mVideoView.start();
337         }
338     }
339 
stopVideo()340     public void stopVideo() {
341         if ((mVideoView != null)) {
342             if (LOCAL_LOGV) {
343                 Log.v(TAG, "Stopping video playback.");
344             }
345             mVideoView.stopPlayback();
346         }
347     }
348 
pauseVideo()349     public void pauseVideo() {
350         if (mVideoView != null) {
351             if (LOCAL_LOGV) {
352                 Log.v(TAG, "Pausing video playback.");
353             }
354             mVideoView.pause();
355         }
356     }
357 
seekVideo(int seekTo)358     public void seekVideo(int seekTo) {
359         if (mVideoView != null) {
360             if (seekTo > 0) {
361                 if (LOCAL_LOGV) {
362                     Log.v(TAG, "Seeking video playback to " + seekTo);
363                 }
364                 mVideoView.seekTo(seekTo);
365             }
366         }
367     }
368 
reset()369     public void reset() {
370         if (null != mScrollText) {
371             mScrollText.setVisibility(View.GONE);
372         }
373 
374         if (null != mImageView) {
375             mImageView.setVisibility(View.GONE);
376         }
377 
378         if (null != mAudioPlayer) {
379             stopAudio();
380         }
381 
382         if (null != mVideoView) {
383             stopVideo();
384             mVideoView.setVisibility(View.GONE);
385         }
386 
387         if (null != mTextView) {
388             mTextView.setVisibility(View.GONE);
389         }
390 
391         if (mScrollViewPort != null) {
392             mScrollViewPort.scrollTo(0, 0);
393             mScrollViewPort.setLayoutParams(
394                     new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0, 0));
395         }
396 
397     }
398 
setVisibility(boolean visible)399     public void setVisibility(boolean visible) {
400         // TODO Auto-generated method stub
401     }
402 
403     @Override
onSizeChanged(int w, int h, int oldw, int oldh)404     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
405         super.onSizeChanged(w, h, oldw, oldh);
406 
407         if (mSizeChangedListener != null) {
408             if (LOCAL_LOGV) {
409                 Log.v(TAG, "new size=" + w + "x" + h);
410             }
411             mSizeChangedListener.onSizeChanged(w, h - AUDIO_INFO_HEIGHT);
412         }
413     }
414 
setOnSizeChangedListener(OnSizeChangedListener l)415     public void setOnSizeChangedListener(OnSizeChangedListener l) {
416         mSizeChangedListener = l;
417     }
418 
419     private class Position {
Position(int left, int top)420         public Position(int left, int top) {
421             mTop = top;
422             mLeft = left;
423         }
424         public int mTop;
425         public int mLeft;
426     }
427 
428     /**
429      * Makes the SlideView working on  MMSConformance Mode. The view will be
430      * re-layout to the linear view.
431      * <p>
432      * This is Chinese requirement about mms conformance.
433      * The most popular Mms service in China is newspaper which is MMS conformance,
434      * normally it mixes the image and text and has a number of slides. The
435      * AbsoluteLayout doesn't have good user experience for this kind of message,
436      * for example,
437      *
438      * 1. AbsoluteLayout exactly follows the smil's layout which is not optimized,
439      * and actually, no other MMS applications follow the smil's layout, they adjust
440      * the layout according their screen size. MMS conformance doc also allows the
441      * implementation to adjust the layout.
442      *
443      * 2. The TextView is fixed in the small area of screen, and other part of screen
444      * is empty once there is no image in the current slide.
445      *
446      * 3. The TextView is scrollable in a small area of screen and the font size is
447      * small which make the user experience bad.
448      *
449      * The better UI for the MMS conformance message could be putting the image/video
450      * and text in a linear layout view and making them scrollable together.
451      *
452      * Another reason for only applying the LinearLayout to the MMS conformance message
453      * is that the AbsoluteLayout has ability to play image and video in a same screen.
454      * which shouldn't be broken.
455      */
enableMMSConformanceMode(int textLeft, int textTop, int imageLeft, int imageTop)456     public void enableMMSConformanceMode(int textLeft, int textTop,
457             int imageLeft, int imageTop) {
458         mConformanceMode = true;
459         if (mScrollViewPort == null) {
460             mScrollViewPort = new ScrollView(mContext) {
461                 private int mBottomY;
462                 @Override
463                 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
464                     super.onLayout(changed, left, top, right, bottom);
465                     if (getChildCount() > 0) {
466                         int childHeight = getChildAt(0).getHeight();
467                         int height = getHeight();
468                         mBottomY = height < childHeight ? childHeight - height : 0;
469                     }
470                 }
471                 @Override
472                 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
473                     // Shows MediaController when the view is scrolled to the top/bottom of itself.
474                     if (t == 0 || t >= mBottomY){
475                         if (mMediaController != null) {
476                             mMediaController.show();
477                         }
478                     }
479                 }
480             };
481             mScrollViewPort.setScrollBarStyle(SCROLLBARS_OUTSIDE_INSET);
482             mViewPort = new LinearLayout(mContext);
483             mViewPort.setOrientation(LinearLayout.VERTICAL);
484             mViewPort.setGravity(Gravity.CENTER);
485             mViewPort.setOnClickListener(new OnClickListener() {
486                 public void onClick(View v) {
487                     if (mMediaController != null) {
488                         mMediaController.show();
489                     }
490                 }
491             });
492             mScrollViewPort.addView(mViewPort, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
493                     LayoutParams.WRAP_CONTENT));
494             addView(mScrollViewPort);
495         }
496         // Layout views to fit the LinearLayout from left to right, then top to
497         // bottom.
498         TreeMap<Position, View> viewsByPosition = new TreeMap<Position, View>(new Comparator<Position>() {
499             public int compare(Position p1, Position p2) {
500                 int l1 = p1.mLeft;
501                 int t1 = p1.mTop;
502                 int l2 = p2.mLeft;
503                 int t2 = p2.mTop;
504                 int res = t1 - t2;
505                 if (res == 0) {
506                     res = l1 - l2;
507                 }
508                 if (res == 0) {
509                     // A view will be lost if return 0.
510                     return -1;
511                 }
512                 return res;
513             }
514         });
515         if (textLeft >=0 && textTop >=0) {
516             mTextView = new TextView(mContext);
517             mTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
518             mTextView.setTextSize(18);
519             mTextView.setPadding(5, 5, 5, 5);
520             viewsByPosition.put(new Position(textLeft, textTop), mTextView);
521         }
522 
523         if (imageLeft >=0 && imageTop >=0) {
524             mImageView = new ImageView(mContext);
525             mImageView.setPadding(0, 5, 0, 5);
526             viewsByPosition.put(new Position(imageLeft, imageTop), mImageView);
527             // According MMS Conformance Document, the image and video should use the same
528             // region. So, put the VideoView below the ImageView.
529             mVideoView = new VideoView(mContext);
530             viewsByPosition.put(new Position(imageLeft + 1, imageTop), mVideoView);
531         }
532         for (View view : viewsByPosition.values()) {
533             if (view instanceof VideoView) {
534                 mViewPort.addView(view, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
535                         LayoutManager.getInstance().getLayoutParameters().getHeight()));
536             } else {
537                 mViewPort.addView(view, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
538                         LayoutParams.WRAP_CONTENT));
539             }
540             view.setVisibility(View.GONE);
541         }
542     }
543 }
544