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.app.Activity;
21import android.app.AlertDialog;
22import android.content.ContentUris;
23import android.content.Context;
24import android.content.DialogInterface;
25import android.content.Intent;
26import android.content.res.Resources;
27import android.media.RingtoneManager;
28import android.net.Uri;
29import android.os.Bundle;
30import android.os.Handler;
31import android.provider.Settings;
32import android.text.InputFilter;
33import android.text.InputFilter.LengthFilter;
34import android.text.TextUtils;
35import android.util.Log;
36import android.view.Menu;
37import android.view.MenuItem;
38import android.view.SubMenu;
39import android.view.View;
40import android.view.View.OnClickListener;
41import android.widget.Button;
42import android.widget.EditText;
43import android.widget.ImageButton;
44import android.widget.Toast;
45
46import com.android.mms.ExceedMessageSizeException;
47import com.android.mms.MmsApp;
48import com.android.mms.MmsConfig;
49import com.android.mms.R;
50import com.android.mms.ResolutionException;
51import com.android.mms.TempFileProvider;
52import com.android.mms.UnsupportContentTypeException;
53import com.android.mms.model.IModelChangedObserver;
54import com.android.mms.model.LayoutModel;
55import com.android.mms.model.Model;
56import com.android.mms.model.SlideModel;
57import com.android.mms.model.SlideshowModel;
58import com.android.mms.ui.BasicSlideEditorView.OnTextChangedListener;
59import com.android.mms.ui.MessageUtils.ResizeImageResultCallback;
60import com.google.android.mms.ContentType;
61import com.google.android.mms.MmsException;
62import com.google.android.mms.pdu.PduBody;
63import com.google.android.mms.pdu.PduPart;
64import com.google.android.mms.pdu.PduPersister;
65
66/**
67 * This activity allows user to edit the contents of a slide.
68 */
69public class SlideEditorActivity extends Activity {
70    private static final String TAG = "SlideEditorActivity";
71    private static final boolean DEBUG = false;
72    private static final boolean LOCAL_LOGV = false;
73
74    // Key for extra data.
75    public static final String SLIDE_INDEX = "slide_index";
76
77    // Menu ids.
78    private final static int MENU_REMOVE_TEXT       = 0;
79    private final static int MENU_ADD_PICTURE       = 1;
80    private final static int MENU_TAKE_PICTURE      = 2;
81    private final static int MENU_DEL_PICTURE       = 3;
82    private final static int MENU_ADD_AUDIO         = 4;
83    private final static int MENU_DEL_AUDIO         = 5;
84    private final static int MENU_ADD_VIDEO         = 6;
85    private final static int MENU_ADD_SLIDE         = 7;
86    private final static int MENU_DEL_VIDEO         = 8;
87    private final static int MENU_LAYOUT            = 9;
88    private final static int MENU_DURATION          = 10;
89    private final static int MENU_PREVIEW_SLIDESHOW = 11;
90    private final static int MENU_RECORD_SOUND      = 12;
91    private final static int MENU_SUB_AUDIO         = 13;
92    private final static int MENU_TAKE_VIDEO        = 14;
93
94    // Request code.
95    private final static int REQUEST_CODE_EDIT_TEXT          = 0;
96    private final static int REQUEST_CODE_CHANGE_PICTURE     = 1;
97    private final static int REQUEST_CODE_TAKE_PICTURE       = 2;
98    private final static int REQUEST_CODE_CHANGE_MUSIC       = 3;
99    private final static int REQUEST_CODE_RECORD_SOUND       = 4;
100    private final static int REQUEST_CODE_CHANGE_VIDEO       = 5;
101    private final static int REQUEST_CODE_CHANGE_DURATION    = 6;
102    private final static int REQUEST_CODE_TAKE_VIDEO         = 7;
103
104    // number of items in the duration selector dialog that directly map from
105    // item index to duration in seconds (duration = index + 1)
106    private final static int NUM_DIRECT_DURATIONS = 10;
107
108    private ImageButton mNextSlide;
109    private ImageButton mPreSlide;
110    private Button mPreview;
111    private Button mReplaceImage;
112    private Button mRemoveSlide;
113    private EditText mTextEditor;
114    private Button mDone;
115    private BasicSlideEditorView mSlideView;
116
117    private SlideshowModel mSlideshowModel;
118    private SlideshowEditor mSlideshowEditor;
119    private SlideshowPresenter mPresenter;
120    private boolean mDirty;
121
122    private int mPosition;
123    private Uri mUri;
124
125    private final static String MESSAGE_URI = "message_uri";
126    private AsyncDialog mAsyncDialog;   // Used for background tasks.
127
128    @Override
129    protected void onCreate(Bundle savedInstanceState) {
130        super.onCreate(savedInstanceState);
131        setContentView(R.layout.edit_slide_activity);
132
133        mSlideView = (BasicSlideEditorView) findViewById(R.id.slide_editor_view);
134        mSlideView.setOnTextChangedListener(mOnTextChangedListener);
135
136        mPreSlide = (ImageButton) findViewById(R.id.pre_slide_button);
137        mPreSlide.setOnClickListener(mOnNavigateBackward);
138
139        mNextSlide = (ImageButton) findViewById(R.id.next_slide_button);
140        mNextSlide.setOnClickListener(mOnNavigateForward);
141
142        mPreview = (Button) findViewById(R.id.preview_button);
143        mPreview.setOnClickListener(mOnPreview);
144
145        mReplaceImage = (Button) findViewById(R.id.replace_image_button);
146        mReplaceImage.setOnClickListener(mOnReplaceImage);
147
148        mRemoveSlide = (Button) findViewById(R.id.remove_slide_button);
149        mRemoveSlide.setOnClickListener(mOnRemoveSlide);
150
151        mTextEditor = (EditText) findViewById(R.id.text_message);
152        mTextEditor.setFilters(new InputFilter[] {
153                new LengthFilter(MmsConfig.getMaxTextLimit())});
154
155        mDone = (Button) findViewById(R.id.done_button);
156        mDone.setOnClickListener(mDoneClickListener);
157
158        initActivityState(savedInstanceState, getIntent());
159
160        try {
161            mSlideshowModel = SlideshowModel.createFromMessageUri(this, mUri);
162            // Confirm that we have at least 1 slide to display
163            if (mSlideshowModel.size() == 0) {
164                Log.e(TAG, "Loaded slideshow is empty; can't edit nothingness, exiting.");
165                finish();
166                return;
167            }
168            // Register an observer to watch whether the data model is changed.
169            mSlideshowModel.registerModelChangedObserver(mModelChangedObserver);
170            mSlideshowEditor = new SlideshowEditor(this, mSlideshowModel);
171            mPresenter = (SlideshowPresenter) PresenterFactory.getPresenter(
172                    "SlideshowPresenter", this, mSlideView, mSlideshowModel);
173
174            // Sanitize mPosition
175            if (mPosition >= mSlideshowModel.size()) {
176                mPosition = Math.max(0, mSlideshowModel.size() - 1);
177            } else if (mPosition < 0) {
178                mPosition = 0;
179            }
180
181            showCurrentSlide();
182        } catch (MmsException e) {
183            Log.e(TAG, "Create SlideshowModel failed!", e);
184            finish();
185            return;
186        }
187    }
188
189    private void initActivityState(Bundle savedInstanceState, Intent intent) {
190        if (savedInstanceState != null) {
191            mUri = (Uri) savedInstanceState.getParcelable(MESSAGE_URI);
192            mPosition = savedInstanceState.getInt(SLIDE_INDEX, 0);
193        } else {
194            mUri = intent.getData();
195            mPosition = intent.getIntExtra(SLIDE_INDEX, 0);
196        }
197    }
198
199    @Override
200    protected void onSaveInstanceState(Bundle outState) {
201        super.onSaveInstanceState(outState);
202
203        outState.putInt(SLIDE_INDEX, mPosition);
204        outState.putParcelable(MESSAGE_URI, mUri);
205    }
206
207    @Override
208    protected void onPause()  {
209        super.onPause();
210
211        // remove any callback to display a progress spinner
212        if (mAsyncDialog != null) {
213            mAsyncDialog.clearPendingProgressDialog();
214        }
215
216        synchronized (this) {
217            if (mDirty) {
218                try {
219                    PduBody pb = mSlideshowModel.toPduBody();
220                    PduPersister.getPduPersister(this).updateParts(mUri, pb, null);
221                    mSlideshowModel.sync(pb);
222                }  catch (MmsException e) {
223                    Log.e(TAG, "Cannot update the message: " + mUri, e);
224                }
225            }
226        }
227    }
228
229    @Override
230    protected void onDestroy() {
231        super.onDestroy();
232
233        if (mSlideshowModel != null) {
234            mSlideshowModel.unregisterModelChangedObserver(
235                    mModelChangedObserver);
236        }
237    }
238
239    private final IModelChangedObserver mModelChangedObserver =
240        new IModelChangedObserver() {
241            public void onModelChanged(Model model, boolean dataChanged) {
242                synchronized (SlideEditorActivity.this) {
243                    mDirty = true;
244                }
245                setResult(RESULT_OK);
246            }
247        };
248
249    private final OnClickListener mOnRemoveSlide = new OnClickListener() {
250        public void onClick(View v) {
251            // Validate mPosition
252            if (mPosition >= 0 && mPosition < mSlideshowModel.size()) {
253                mSlideshowEditor.removeSlide(mPosition);
254                int size = mSlideshowModel.size();
255                if (size > 0) {
256                    if (mPosition >= size) {
257                        mPosition--;
258                    }
259                    showCurrentSlide();
260                } else {
261                    finish();
262                    return;
263                }
264            }
265        }
266    };
267
268    private final OnTextChangedListener mOnTextChangedListener = new OnTextChangedListener() {
269        public void onTextChanged(String s) {
270            if (!isFinishing()) {
271                mSlideshowEditor.changeText(mPosition, s);
272            }
273        }
274    };
275
276    private final OnClickListener mOnPreview = new OnClickListener() {
277        public void onClick(View v) {
278            previewSlideshow();
279        }
280    };
281
282    private final OnClickListener mOnReplaceImage = new OnClickListener() {
283        public void onClick(View v) {
284            SlideModel slide = mSlideshowModel.get(mPosition);
285            if (slide != null && slide.hasVideo()) {
286                Toast.makeText(SlideEditorActivity.this, R.string.cannot_add_picture_and_video,
287                        Toast.LENGTH_SHORT).show();
288                return;
289            }
290            Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
291            intent.setType(ContentType.IMAGE_UNSPECIFIED);
292            startActivityForResult(intent, REQUEST_CODE_CHANGE_PICTURE);
293        }
294    };
295
296    private final OnClickListener mOnNavigateBackward = new OnClickListener() {
297        public void onClick(View v) {
298            if (mPosition > 0) {
299                mPosition --;
300                showCurrentSlide();
301            }
302        }
303    };
304
305    private final OnClickListener mOnNavigateForward = new OnClickListener() {
306        public void onClick(View v) {
307            if (mPosition < mSlideshowModel.size() - 1) {
308                mPosition ++;
309                showCurrentSlide();
310            }
311        }
312    };
313
314    private final OnClickListener mDoneClickListener = new OnClickListener() {
315        public void onClick(View v) {
316            Intent data = new Intent();
317            data.putExtra("done", true);
318            setResult(RESULT_OK, data);
319            finish();
320        }
321    };
322
323    private AsyncDialog getAsyncDialog() {
324        if (mAsyncDialog == null) {
325            mAsyncDialog = new AsyncDialog(this);
326        }
327        return mAsyncDialog;
328    }
329
330    private void previewSlideshow() {
331        MessageUtils.viewMmsMessageAttachment(SlideEditorActivity.this, mUri, mSlideshowModel,
332                getAsyncDialog());
333    }
334
335    private void updateTitle() {
336        setTitle(getString(R.string.slide_show_part, (mPosition + 1), mSlideshowModel.size()));
337    }
338
339    @Override
340    public boolean onPrepareOptionsMenu(Menu menu) {
341        if (isFinishing()) {
342            return false;
343        }
344        menu.clear();
345
346        SlideModel slide = mSlideshowModel.get(mPosition);
347
348        if (slide == null) {
349            return false;
350        }
351
352        // Preview slideshow.
353        menu.add(0, MENU_PREVIEW_SLIDESHOW, 0, R.string.preview_slideshow).setIcon(
354                com.android.internal.R.drawable.ic_menu_play_clip);
355
356        // Text
357        if (slide.hasText() && !TextUtils.isEmpty(slide.getText().getText())) {
358            //"Change text" if text is set.
359            menu.add(0, MENU_REMOVE_TEXT, 0, R.string.remove_text).setIcon(
360                    R.drawable.ic_menu_remove_text);
361        }
362
363        // Picture
364        if (slide.hasImage()) {
365            menu.add(0, MENU_DEL_PICTURE, 0, R.string.remove_picture).setIcon(
366                    R.drawable.ic_menu_remove_picture);
367        } else if (!slide.hasVideo()) {
368            menu.add(0, MENU_ADD_PICTURE, 0, R.string.add_picture).setIcon(
369                    R.drawable.ic_menu_picture);
370            menu.add(0, MENU_TAKE_PICTURE, 0, R.string.attach_take_photo).setIcon(
371                    R.drawable.ic_menu_picture);
372        }
373
374        // Music
375        if (slide.hasAudio()) {
376            menu.add(0, MENU_DEL_AUDIO, 0, R.string.remove_music).setIcon(
377                    R.drawable.ic_menu_remove_sound);
378        } else if (!slide.hasVideo()) {
379            if (MmsConfig.getAllowAttachAudio()) {
380                SubMenu subMenu = menu.addSubMenu(0, MENU_SUB_AUDIO, 0, R.string.add_music)
381                    .setIcon(R.drawable.ic_menu_add_sound);
382                subMenu.add(0, MENU_ADD_AUDIO, 0, R.string.attach_sound);
383                subMenu.add(0, MENU_RECORD_SOUND, 0, R.string.attach_record_sound);
384            } else {
385                menu.add(0, MENU_RECORD_SOUND, 0, R.string.attach_record_sound)
386                    .setIcon(R.drawable.ic_menu_add_sound);
387            }
388        }
389
390        // Video
391        if (slide.hasVideo()) {
392            menu.add(0, MENU_DEL_VIDEO, 0, R.string.remove_video).setIcon(
393                    R.drawable.ic_menu_remove_video);
394        } else if (!slide.hasAudio() && !slide.hasImage()) {
395            menu.add(0, MENU_ADD_VIDEO, 0, R.string.add_video).setIcon(R.drawable.ic_menu_movie);
396            menu.add(0, MENU_TAKE_VIDEO, 0, R.string.attach_record_video)
397                .setIcon(R.drawable.ic_menu_movie);
398        }
399
400        // Add slide
401        menu.add(0, MENU_ADD_SLIDE, 0, R.string.add_slide).setIcon(
402                R.drawable.ic_menu_add_slide);
403
404        // Slide duration
405        String duration = getResources().getString(R.string.duration_sec);
406        menu.add(0, MENU_DURATION, 0,
407                duration.replace("%s", String.valueOf(slide.getDuration() / 1000))).setIcon(
408                        R.drawable.ic_menu_duration);
409
410        // Slide layout
411        int resId;
412        if (mSlideshowModel.getLayout().getLayoutType() == LayoutModel.LAYOUT_TOP_TEXT) {
413            resId = R.string.layout_top;
414        } else {
415            resId = R.string.layout_bottom;
416        }
417        // FIXME: set correct icon when layout icon is available.
418        menu.add(0, MENU_LAYOUT, 0, resId).setIcon(R.drawable.ic_menu_picture);
419        return true;
420    }
421
422    @Override
423    public boolean onOptionsItemSelected(MenuItem item) {
424        switch (item.getItemId()) {
425            case MENU_PREVIEW_SLIDESHOW:
426                previewSlideshow();
427                break;
428
429            case MENU_REMOVE_TEXT:
430                SlideModel slide = mSlideshowModel.get(mPosition);
431                if (slide != null) {
432                    slide.removeText();
433                }
434                break;
435
436            case MENU_ADD_PICTURE:
437                Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
438                intent.setType(ContentType.IMAGE_UNSPECIFIED);
439                startActivityForResult(intent, REQUEST_CODE_CHANGE_PICTURE);
440                break;
441
442            case MENU_TAKE_PICTURE:
443                MessageUtils.capturePicture(this, REQUEST_CODE_TAKE_PICTURE);
444                break;
445
446            case MENU_DEL_PICTURE:
447                mSlideshowEditor.removeImage(mPosition);
448                setReplaceButtonText(R.string.add_picture);
449                break;
450
451            case MENU_ADD_AUDIO:
452                MessageUtils.selectAudio(this, REQUEST_CODE_CHANGE_MUSIC);
453                break;
454
455            case MENU_RECORD_SOUND:
456                slide = mSlideshowModel.get(mPosition);
457                int currentSlideSize = slide.getSlideSize();
458                long sizeLimit = ComposeMessageActivity.computeAttachmentSizeLimit(mSlideshowModel,
459                        currentSlideSize);
460                MessageUtils.recordSound(this, REQUEST_CODE_RECORD_SOUND, sizeLimit);
461                break;
462
463            case MENU_DEL_AUDIO:
464                mSlideshowEditor.removeAudio(mPosition);
465                break;
466
467            case MENU_ADD_VIDEO:
468                intent = new Intent(Intent.ACTION_GET_CONTENT);
469                intent.setType(ContentType.VIDEO_UNSPECIFIED);
470                intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
471                startActivityForResult(intent, REQUEST_CODE_CHANGE_VIDEO);
472                break;
473
474            case MENU_TAKE_VIDEO:
475                slide = mSlideshowModel.get(mPosition);
476                currentSlideSize = slide.getSlideSize();
477                sizeLimit = ComposeMessageActivity.computeAttachmentSizeLimit(mSlideshowModel,
478                        currentSlideSize);
479                if (sizeLimit > 0) {
480                    MessageUtils.recordVideo(this, REQUEST_CODE_TAKE_VIDEO, sizeLimit);
481                } else {
482                    Toast.makeText(this,
483                            getString(R.string.message_too_big_for_video),
484                            Toast.LENGTH_SHORT).show();
485                }
486                break;
487
488            case MENU_DEL_VIDEO:
489                mSlideshowEditor.removeVideo(mPosition);
490                break;
491
492            case MENU_ADD_SLIDE:
493                mPosition++;
494                if ( mSlideshowEditor.addNewSlide(mPosition) ) {
495                    // add successfully
496                    showCurrentSlide();
497                } else {
498                    // move position back
499                    mPosition--;
500                    Toast.makeText(this, R.string.cannot_add_slide_anymore,
501                            Toast.LENGTH_SHORT).show();
502                }
503                break;
504
505            case MENU_LAYOUT:
506                showLayoutSelectorDialog();
507                break;
508
509            case MENU_DURATION:
510                showDurationDialog();
511                break;
512        }
513
514        return true;
515    }
516
517    private void setReplaceButtonText(int text) {
518        mReplaceImage.setText(text);
519    }
520
521    private void showDurationDialog() {
522        AlertDialog.Builder builder = new AlertDialog.Builder(this);
523        builder.setIcon(R.drawable.ic_mms_duration);
524        String title = getResources().getString(R.string.duration_selector_title);
525        builder.setTitle(title + (mPosition + 1) + "/" + mSlideshowModel.size());
526
527        builder.setItems(R.array.select_dialog_items,
528                new DialogInterface.OnClickListener() {
529            public void onClick(DialogInterface dialog, int which) {
530                if ((which >= 0) && (which < NUM_DIRECT_DURATIONS)) {
531                    mSlideshowEditor.changeDuration(
532                            mPosition, (which + 1) * 1000);
533                } else {
534                    Intent intent = new Intent(SlideEditorActivity.this,
535                            EditSlideDurationActivity.class);
536                    intent.putExtra(EditSlideDurationActivity.SLIDE_INDEX, mPosition);
537                    intent.putExtra(EditSlideDurationActivity.SLIDE_TOTAL,
538                            mSlideshowModel.size());
539                    intent.putExtra(EditSlideDurationActivity.SLIDE_DUR,
540                            mSlideshowModel.get(mPosition).getDuration() / 1000); // in seconds
541                    startActivityForResult(intent, REQUEST_CODE_CHANGE_DURATION);
542                }
543                dialog.dismiss();
544            }
545        });
546
547        builder.show();
548    }
549
550    private void showLayoutSelectorDialog() {
551        AlertDialog.Builder builder = new AlertDialog.Builder(this);
552        builder.setIcon(R.drawable.ic_mms_layout);
553
554        String title = getResources().getString(R.string.layout_selector_title);
555        builder.setTitle(title + (mPosition + 1) + "/" + mSlideshowModel.size());
556
557        LayoutSelectorAdapter adapter = new LayoutSelectorAdapter(this);
558        builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
559            public void onClick(DialogInterface dialog, int which) {
560                switch (which) {
561                    case 0: // Top text.
562                        mSlideshowEditor.changeLayout(LayoutModel.LAYOUT_TOP_TEXT);
563                        break;
564                    case 1: // Bottom text.
565                        mSlideshowEditor.changeLayout(LayoutModel.LAYOUT_BOTTOM_TEXT);
566                        break;
567                }
568                dialog.dismiss();
569            }
570        });
571
572        builder.show();
573    }
574
575    @Override
576    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
577        if (resultCode != RESULT_OK) {
578            return;
579        }
580
581        switch(requestCode) {
582            case REQUEST_CODE_EDIT_TEXT:
583                // XXX where does this come from?  Action is probably not the
584                // right place to have the text...
585                mSlideshowEditor.changeText(mPosition, data.getAction());
586                break;
587
588            case REQUEST_CODE_TAKE_PICTURE:
589                Uri pictureUri = null;
590                boolean showError = false;
591                try {
592                    pictureUri = TempFileProvider.renameScrapFile(".jpg",
593                            Integer.toString(mPosition), this);
594
595                    if (pictureUri == null) {
596                        showError = true;
597                    } else {
598                        // Remove the old captured picture's thumbnail from the cache
599                        MmsApp.getApplication().getThumbnailManager().removeThumbnail(pictureUri);
600
601                        mSlideshowEditor.changeImage(mPosition, pictureUri);
602                        setReplaceButtonText(R.string.replace_image);
603                    }
604                } catch (MmsException e) {
605                    Log.e(TAG, "add image failed", e);
606                    showError = true;
607                } catch (UnsupportContentTypeException e) {
608                    MessageUtils.showErrorDialog(SlideEditorActivity.this,
609                            getResourcesString(R.string.unsupported_media_format, getPictureString()),
610                            getResourcesString(R.string.select_different_media, getPictureString()));
611                } catch (ResolutionException e) {
612                    MessageUtils.resizeImageAsync(this, pictureUri, new Handler(),
613                            mResizeImageCallback, false);
614                } catch (ExceedMessageSizeException e) {
615                    MessageUtils.resizeImageAsync(this, pictureUri, new Handler(),
616                            mResizeImageCallback, false);
617                }
618                if (showError) {
619                    notifyUser("add picture failed");
620                    Toast.makeText(SlideEditorActivity.this,
621                            getResourcesString(R.string.failed_to_add_media, getPictureString()),
622                            Toast.LENGTH_SHORT).show();
623                }
624                break;
625
626            case REQUEST_CODE_CHANGE_PICTURE:
627                try {
628                    mSlideshowEditor.changeImage(mPosition, data.getData());
629                    setReplaceButtonText(R.string.replace_image);
630                } catch (MmsException e) {
631                    Log.e(TAG, "add image failed", e);
632                    notifyUser("add picture failed");
633                    Toast.makeText(SlideEditorActivity.this,
634                            getResourcesString(R.string.failed_to_add_media, getPictureString()),
635                            Toast.LENGTH_SHORT).show();
636                } catch (UnsupportContentTypeException e) {
637                    MessageUtils.showErrorDialog(SlideEditorActivity.this,
638                            getResourcesString(R.string.unsupported_media_format, getPictureString()),
639                            getResourcesString(R.string.select_different_media, getPictureString()));
640                } catch (ResolutionException e) {
641                    MessageUtils.resizeImageAsync(this, data.getData(), new Handler(),
642                            mResizeImageCallback, false);
643                } catch (ExceedMessageSizeException e) {
644                    MessageUtils.resizeImageAsync(this, data.getData(), new Handler(),
645                            mResizeImageCallback, false);
646                }
647                break;
648
649            case REQUEST_CODE_CHANGE_MUSIC:
650            case REQUEST_CODE_RECORD_SOUND:
651                Uri uri;
652                if (requestCode == REQUEST_CODE_CHANGE_MUSIC) {
653                    uri = (Uri) data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
654                    if (Settings.System.DEFAULT_RINGTONE_URI.equals(uri)) {
655                        return;
656                    }
657                } else {
658                    uri = data.getData();
659                }
660
661                try {
662                    mSlideshowEditor.changeAudio(mPosition, uri);
663                } catch (MmsException e) {
664                    Log.e(TAG, "add audio failed", e);
665                    notifyUser("add music failed");
666                    Toast.makeText(SlideEditorActivity.this,
667                            getResourcesString(R.string.failed_to_add_media, getAudioString()),
668                            Toast.LENGTH_SHORT).show();
669                } catch (UnsupportContentTypeException e) {
670                    MessageUtils.showErrorDialog(SlideEditorActivity.this,
671                            getResourcesString(R.string.unsupported_media_format, getAudioString()),
672                            getResourcesString(R.string.select_different_media, getAudioString()));
673                } catch (ExceedMessageSizeException e) {
674                    MessageUtils.showErrorDialog(SlideEditorActivity.this,
675                            getResourcesString(R.string.exceed_message_size_limitation),
676                            getResourcesString(R.string.failed_to_add_media, getAudioString()));
677                }
678                break;
679
680            case REQUEST_CODE_TAKE_VIDEO:
681                try {
682                    Uri videoUri = TempFileProvider.renameScrapFile(".3gp",
683                            Integer.toString(mPosition), this);
684
685                    mSlideshowEditor.changeVideo(mPosition, videoUri);
686                } catch (MmsException e) {
687                    notifyUser("add video failed");
688                    Toast.makeText(SlideEditorActivity.this,
689                            getResourcesString(R.string.failed_to_add_media, getVideoString()),
690                            Toast.LENGTH_SHORT).show();
691                } catch (UnsupportContentTypeException e) {
692                    MessageUtils.showErrorDialog(SlideEditorActivity.this,
693                            getResourcesString(R.string.unsupported_media_format, getVideoString()),
694                            getResourcesString(R.string.select_different_media, getVideoString()));
695                } catch (ExceedMessageSizeException e) {
696                    MessageUtils.showErrorDialog(SlideEditorActivity.this,
697                            getResourcesString(R.string.exceed_message_size_limitation),
698                            getResourcesString(R.string.failed_to_add_media, getVideoString()));
699                }
700                break;
701
702            case REQUEST_CODE_CHANGE_VIDEO:
703                try {
704                    mSlideshowEditor.changeVideo(mPosition, data.getData());
705                } catch (MmsException e) {
706                    Log.e(TAG, "add video failed", e);
707                    notifyUser("add video failed");
708                    Toast.makeText(SlideEditorActivity.this,
709                            getResourcesString(R.string.failed_to_add_media, getVideoString()),
710                            Toast.LENGTH_SHORT).show();
711                } catch (UnsupportContentTypeException e) {
712                    MessageUtils.showErrorDialog(SlideEditorActivity.this,
713                            getResourcesString(R.string.unsupported_media_format, getVideoString()),
714                            getResourcesString(R.string.select_different_media, getVideoString()));
715                } catch (ExceedMessageSizeException e) {
716                    MessageUtils.showErrorDialog(SlideEditorActivity.this,
717                            getResourcesString(R.string.exceed_message_size_limitation),
718                            getResourcesString(R.string.failed_to_add_media, getVideoString()));
719                }
720                break;
721
722            case REQUEST_CODE_CHANGE_DURATION:
723                mSlideshowEditor.changeDuration(mPosition,
724                    Integer.valueOf(data.getAction()) * 1000);
725                break;
726        }
727    }
728
729    private final ResizeImageResultCallback mResizeImageCallback = new ResizeImageResultCallback() {
730        public void onResizeResult(PduPart part, boolean append) {
731            Context context = SlideEditorActivity.this;
732            if (part == null) {
733                Toast.makeText(SlideEditorActivity.this,
734                        getResourcesString(R.string.failed_to_add_media, getPictureString()),
735                        Toast.LENGTH_SHORT).show();
736                return;
737            }
738
739            try {
740                long messageId = ContentUris.parseId(mUri);
741                PduPersister persister = PduPersister.getPduPersister(context);
742                Uri newUri = persister.persistPart(part, messageId, null);
743                mSlideshowEditor.changeImage(mPosition, newUri);
744
745                setReplaceButtonText(R.string.replace_image);
746            } catch (MmsException e) {
747                notifyUser("add picture failed");
748                Toast.makeText(SlideEditorActivity.this,
749                        getResourcesString(R.string.failed_to_add_media, getPictureString()),
750                        Toast.LENGTH_SHORT).show();
751            } catch (UnsupportContentTypeException e) {
752                MessageUtils.showErrorDialog(SlideEditorActivity.this,
753                        getResourcesString(R.string.unsupported_media_format, getPictureString()),
754                        getResourcesString(R.string.select_different_media, getPictureString()));
755            } catch (ResolutionException e) {
756                MessageUtils.showErrorDialog(SlideEditorActivity.this,
757                        getResourcesString(R.string.failed_to_resize_image),
758                        getResourcesString(R.string.resize_image_error_information));
759            } catch (ExceedMessageSizeException e) {
760                MessageUtils.showErrorDialog(SlideEditorActivity.this,
761                        getResourcesString(R.string.exceed_message_size_limitation),
762                        getResourcesString(R.string.failed_to_add_media, getPictureString()));
763            }
764        }
765    };
766
767    private String getResourcesString(int id, String mediaName) {
768        Resources r = getResources();
769        return r.getString(id, mediaName);
770    }
771
772    private String getResourcesString(int id) {
773        Resources r = getResources();
774        return r.getString(id);
775    }
776
777    private String getAudioString() {
778        return getResourcesString(R.string.type_audio);
779    }
780
781    private String getPictureString() {
782        return getResourcesString(R.string.type_picture);
783    }
784
785    private String getVideoString() {
786        return getResourcesString(R.string.type_video);
787    }
788
789    private void notifyUser(String message) {
790        if (LOCAL_LOGV) {
791            Log.v(TAG, "notifyUser: message=" + message);
792        }
793    }
794
795    private void showCurrentSlide() {
796        mPresenter.setLocation(mPosition);
797        mPresenter.present(null);
798        updateTitle();
799
800        if (mSlideshowModel.get(mPosition).hasImage()) {
801            setReplaceButtonText(R.string.replace_image);
802        } else {
803            setReplaceButtonText(R.string.add_picture);
804        }
805    }
806}
807