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