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
18package com.android.mms.ui;
19
20import android.content.Context;
21import android.os.Handler;
22import android.util.Log;
23
24import com.android.mms.model.AudioModel;
25import com.android.mms.model.ImageModel;
26import com.android.mms.model.LayoutModel;
27import com.android.mms.model.MediaModel;
28import com.android.mms.model.MediaModel.MediaAction;
29import com.android.mms.model.Model;
30import com.android.mms.model.RegionMediaModel;
31import com.android.mms.model.RegionModel;
32import com.android.mms.model.SlideModel;
33import com.android.mms.model.SlideshowModel;
34import com.android.mms.model.TextModel;
35import com.android.mms.model.VideoModel;
36import com.android.mms.ui.AdaptableSlideViewInterface.OnSizeChangedListener;
37import com.android.mms.util.ItemLoadedCallback;
38
39/**
40 * A basic presenter of slides.
41 */
42public class SlideshowPresenter extends Presenter {
43    private static final String TAG = "SlideshowPresenter";
44    private static final boolean DEBUG = false;
45    private static final boolean LOCAL_LOGV = false;
46
47    protected int mLocation;
48    protected final int mSlideNumber;
49
50    protected float mWidthTransformRatio = 1.0f;
51    protected float mHeightTransformRatio = 1.0f;
52
53    // Since only the original thread that created a view hierarchy can touch
54    // its views, we have to use Handler to manage the views in the some
55    // callbacks such as onModelChanged().
56    protected final Handler mHandler = new Handler();
57
58    public SlideshowPresenter(Context context, ViewInterface view, Model model) {
59        super(context, view, model);
60        mLocation = 0;
61        mSlideNumber = ((SlideshowModel) mModel).size();
62
63        if (view instanceof AdaptableSlideViewInterface) {
64            ((AdaptableSlideViewInterface) view).setOnSizeChangedListener(
65                    mViewSizeChangedListener);
66        }
67    }
68
69    private final OnSizeChangedListener mViewSizeChangedListener =
70        new OnSizeChangedListener() {
71        public void onSizeChanged(int width, int height) {
72            LayoutModel layout = ((SlideshowModel) mModel).getLayout();
73            mWidthTransformRatio = getWidthTransformRatio(
74                    width, layout.getLayoutWidth());
75            mHeightTransformRatio = getHeightTransformRatio(
76                    height, layout.getLayoutHeight());
77            // The ratio indicates how to reduce the source to match the View,
78            // so the larger one should be used.
79            float ratio = mWidthTransformRatio > mHeightTransformRatio ?
80                    mWidthTransformRatio : mHeightTransformRatio;
81            mWidthTransformRatio = ratio;
82            mHeightTransformRatio = ratio;
83            if (LOCAL_LOGV) {
84                Log.v(TAG, "ratio_w = " + mWidthTransformRatio
85                        + ", ratio_h = " + mHeightTransformRatio);
86            }
87        }
88    };
89
90    private float getWidthTransformRatio(int width, int layoutWidth) {
91        if (width > 0) {
92            return (float) layoutWidth / (float) width;
93        }
94        return 1.0f;
95    }
96
97    private float getHeightTransformRatio(int height, int layoutHeight) {
98        if (height > 0) {
99            return (float) layoutHeight / (float) height;
100        }
101        return 1.0f;
102    }
103
104    private int transformWidth(int width) {
105        return (int) (width / mWidthTransformRatio);
106    }
107
108    private int transformHeight(int height) {
109        return (int) (height / mHeightTransformRatio);
110    }
111
112    @Override
113    public void present(ItemLoadedCallback callback) {
114        // This is called to show a full-screen slideshow. Presently, all parts of
115        // a slideshow (images, sounds, etc.) are loaded and displayed on the UI thread.
116        presentSlide((SlideViewInterface) mView, ((SlideshowModel) mModel).get(mLocation));
117    }
118
119    /**
120     * @param view
121     * @param model
122     */
123    protected void presentSlide(SlideViewInterface view, SlideModel model) {
124        view.reset();
125
126        for (MediaModel media : model) {
127            if (media instanceof RegionMediaModel) {
128                presentRegionMedia(view, (RegionMediaModel) media, true);
129            } else if (media.isAudio()) {
130                presentAudio(view, (AudioModel) media, true);
131            }
132        }
133    }
134
135    /**
136     * @param view
137     */
138    protected void presentRegionMedia(SlideViewInterface view,
139            RegionMediaModel rMedia, boolean dataChanged) {
140        RegionModel r = (rMedia).getRegion();
141        if (rMedia.isText()) {
142            presentText(view, (TextModel) rMedia, r, dataChanged);
143        } else if (rMedia.isImage()) {
144            presentImage(view, (ImageModel) rMedia, r, dataChanged);
145        } else if (rMedia.isVideo()) {
146            presentVideo(view, (VideoModel) rMedia, r, dataChanged);
147        }
148    }
149
150    protected void presentAudio(SlideViewInterface view, AudioModel audio,
151            boolean dataChanged) {
152        // Set audio only when data changed.
153        if (dataChanged) {
154            view.setAudio(audio.getUri(), audio.getSrc(), audio.getExtras());
155        }
156
157        MediaAction action = audio.getCurrentAction();
158        if (action == MediaAction.START) {
159            view.startAudio();
160        } else if (action == MediaAction.PAUSE) {
161            view.pauseAudio();
162        } else if (action == MediaAction.STOP) {
163            view.stopAudio();
164        } else if (action == MediaAction.SEEK) {
165            view.seekAudio(audio.getSeekTo());
166        }
167    }
168
169    protected void presentText(SlideViewInterface view, TextModel text,
170            RegionModel r, boolean dataChanged) {
171        if (dataChanged) {
172            view.setText(text.getSrc(), text.getText());
173        }
174
175        if (view instanceof AdaptableSlideViewInterface) {
176            ((AdaptableSlideViewInterface) view).setTextRegion(
177                    transformWidth(r.getLeft()),
178                    transformHeight(r.getTop()),
179                    transformWidth(r.getWidth()),
180                    transformHeight(r.getHeight()));
181        }
182        view.setTextVisibility(text.isVisible());
183    }
184
185    /**
186     * @param view
187     * @param image
188     * @param r
189     */
190    protected void presentImage(SlideViewInterface view, ImageModel image,
191            RegionModel r, boolean dataChanged) {
192        int transformedWidth = transformWidth(r.getWidth());
193        int transformedHeight = transformWidth(r.getHeight());
194
195        if (LOCAL_LOGV) {
196            Log.v(TAG, "presentImage r.getWidth: " + r.getWidth()
197                    + ", r.getHeight: " + r.getHeight() +
198                    " transformedWidth: " + transformedWidth +
199                    " transformedHeight: " + transformedHeight);
200        }
201
202        if (dataChanged) {
203            view.setImage(image.getSrc(), image.getBitmap(transformedWidth, transformedHeight));
204        }
205
206        if (view instanceof AdaptableSlideViewInterface) {
207            ((AdaptableSlideViewInterface) view).setImageRegion(
208                    transformWidth(r.getLeft()),
209                    transformHeight(r.getTop()),
210                    transformedWidth,
211                    transformedHeight);
212        }
213        view.setImageRegionFit(r.getFit());
214        view.setImageVisibility(image.isVisible());
215    }
216
217    /**
218     * @param view
219     * @param video
220     * @param r
221     */
222    protected void presentVideo(SlideViewInterface view, VideoModel video,
223            RegionModel r, boolean dataChanged) {
224        if (dataChanged) {
225            view.setVideo(video.getSrc(), video.getUri());
226        }
227
228            if (view instanceof AdaptableSlideViewInterface) {
229            ((AdaptableSlideViewInterface) view).setVideoRegion(
230                    transformWidth(r.getLeft()),
231                    transformHeight(r.getTop()),
232                    transformWidth(r.getWidth()),
233                    transformHeight(r.getHeight()));
234        }
235        view.setVideoVisibility(video.isVisible());
236
237        MediaAction action = video.getCurrentAction();
238        if (action == MediaAction.START) {
239            view.startVideo();
240        } else if (action == MediaAction.PAUSE) {
241            view.pauseVideo();
242        } else if (action == MediaAction.STOP) {
243            view.stopVideo();
244        } else if (action == MediaAction.SEEK) {
245            view.seekVideo(video.getSeekTo());
246        }
247    }
248
249    public void setLocation(int location) {
250        mLocation = location;
251    }
252
253    public int getLocation() {
254        return mLocation;
255    }
256
257    public void goBackward() {
258        if (mLocation > 0) {
259            mLocation--;
260        }
261    }
262
263    public void goForward() {
264        if (mLocation < (mSlideNumber - 1)) {
265            mLocation++;
266        }
267    }
268
269    public void onModelChanged(final Model model, final boolean dataChanged) {
270        final SlideViewInterface view = (SlideViewInterface) mView;
271
272        // FIXME: Should be optimized.
273        if (model instanceof SlideshowModel) {
274            // TODO:
275        } else if (model instanceof SlideModel) {
276            if (((SlideModel) model).isVisible()) {
277                mHandler.post(new Runnable() {
278                    public void run() {
279                        presentSlide(view, (SlideModel) model);
280                    }
281                });
282            } else {
283                mHandler.post(new Runnable() {
284                    public void run() {
285                        goForward();
286                    }
287                });
288            }
289        } else if (model instanceof MediaModel) {
290            if (model instanceof RegionMediaModel) {
291                mHandler.post(new Runnable() {
292                    public void run() {
293                        presentRegionMedia(view, (RegionMediaModel) model, dataChanged);
294                    }
295                });
296            } else if (((MediaModel) model).isAudio()) {
297                mHandler.post(new Runnable() {
298                    public void run() {
299                        presentAudio(view, (AudioModel) model, dataChanged);
300                    }
301                });
302            }
303        } else if (model instanceof RegionModel) {
304            // TODO:
305        }
306    }
307
308    @Override
309    public void cancelBackgroundLoading() {
310        // For now, the SlideshowPresenter does no background loading so there is nothing to cancel.
311    }
312}
313