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.ListActivity;
21import android.content.Context;
22import android.content.Intent;
23import android.net.Uri;
24import android.os.Bundle;
25import android.util.Log;
26import android.view.ContextMenu;
27import android.view.ContextMenu.ContextMenuInfo;
28import android.view.LayoutInflater;
29import android.view.Menu;
30import android.view.MenuItem;
31import android.view.View;
32import android.view.ViewGroup;
33import android.widget.AdapterView;
34import android.widget.AdapterView.AdapterContextMenuInfo;
35import android.widget.ArrayAdapter;
36import android.widget.ImageView;
37import android.widget.ListView;
38import android.widget.TextView;
39import android.widget.Toast;
40
41import com.android.mms.R;
42import com.android.mms.model.IModelChangedObserver;
43import com.android.mms.model.Model;
44import com.android.mms.model.SlideModel;
45import com.android.mms.model.SlideshowModel;
46import com.google.android.mms.MmsException;
47import com.google.android.mms.pdu.PduBody;
48import com.google.android.mms.pdu.PduPersister;
49
50/**
51 * A list of slides which allows user to edit each item in it.
52 */
53public class SlideshowEditActivity extends ListActivity {
54    private final static String TAG = "SlideshowEditActivity";
55    private static final boolean DEBUG = false;
56    private static final boolean LOCAL_LOGV = false;
57
58    // Menu ids.
59    private final static int MENU_MOVE_UP           = 0;
60    private final static int MENU_MOVE_DOWN         = 1;
61    private final static int MENU_REMOVE_SLIDE      = 2;
62    private final static int MENU_ADD_SLIDE         = 3;
63    private final static int MENU_DISCARD_SLIDESHOW = 4;
64
65    private final static int REQUEST_CODE_EDIT_SLIDE         = 6;
66
67    // State.
68    private final static String STATE = "state";
69    private final static String SLIDE_INDEX = "slide_index";
70    private final static String MESSAGE_URI = "message_uri";
71
72    private ListView mList;
73    private SlideListAdapter mSlideListAdapter;
74
75    private SlideshowModel mSlideshowModel = null;
76    private SlideshowEditor mSlideshowEditor = null;
77
78    private Bundle mState;
79    private Uri mUri;
80    private Intent mResultIntent;
81    private boolean mDirty;
82    private View mAddSlideItem;
83
84    @Override
85    protected void onCreate(Bundle icicle) {
86        super.onCreate(icicle);
87
88        mList = getListView();
89        mAddSlideItem = createAddSlideItem();
90        mList.addFooterView(mAddSlideItem);
91        mAddSlideItem.setVisibility(View.GONE);
92
93        if (icicle != null) {
94            // Retrieve previously saved state of this activity.
95            mState = icicle.getBundle(STATE);
96        }
97
98        if (mState != null) {
99            mUri = Uri.parse(mState.getString(MESSAGE_URI));
100        } else {
101            mUri = getIntent().getData();
102        }
103
104        if (mUri == null) {
105            Log.e(TAG, "Cannot startup activity, null Uri.");
106            finish();
107            return;
108        }
109
110        // Return the Uri of the message to whoever invoked us.
111        mResultIntent = new Intent();
112        mResultIntent.setData(mUri);
113
114        try {
115            initSlideList();
116            adjustAddSlideVisibility();
117        } catch (MmsException e) {
118            Log.e(TAG, "Failed to initialize the slide-list.", e);
119            finish();
120        }
121
122        registerForContextMenu(mList);
123    }
124
125    private View createAddSlideItem() {
126        View v = ((LayoutInflater) getSystemService(
127                Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.slideshow_edit_item, null);
128
129        //  Add slide.
130        TextView text = (TextView) v.findViewById(R.id.slide_number_text);
131        text.setText(R.string.add_slide);
132
133        text = (TextView) v.findViewById(R.id.text_preview);
134        text.setText(R.string.add_slide_hint);
135        text.setVisibility(View.VISIBLE);
136
137        ImageView image = (ImageView) v.findViewById(R.id.image_preview);
138        image.setImageResource(R.drawable.ic_attach_slideshow_holo_light);
139
140        return v;
141    }
142
143    @Override
144    protected void onListItemClick(ListView l, View v, int position, long id) {
145        if (position == (l.getCount() - 1)) {
146            addNewSlide();
147        } else {
148            openSlide(position);
149        }
150    }
151
152    @Override
153    protected void onResume() {
154        super.onResume();
155
156        if (mState != null) {
157            mList.setSelection(mState.getInt(SLIDE_INDEX, 0));
158        }
159    }
160
161    /*
162     * (non-Javadoc)
163     * @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
164     */
165    @Override
166    protected void onSaveInstanceState(Bundle outState) {
167        super.onSaveInstanceState(outState);
168
169        mState = new Bundle();
170        if (mList.getSelectedItemPosition() >= 0) {
171            mState.putInt(SLIDE_INDEX, mList.getSelectedItemPosition());
172        }
173
174        if (mUri != null) {
175            mState.putString(MESSAGE_URI, mUri.toString());
176        }
177
178        if (LOCAL_LOGV) {
179            Log.v(TAG, "Saving state: " + mState);
180        }
181        outState.putBundle(STATE, mState);
182    }
183
184    @Override
185    protected void onPause()  {
186        super.onPause();
187
188        synchronized (this) {
189            if (mDirty) {
190                try {
191                    PduBody pb = mSlideshowModel.toPduBody();
192                    PduPersister.getPduPersister(this).updateParts(mUri, pb, null);
193                    mSlideshowModel.sync(pb);
194                }  catch (MmsException e) {
195                    Log.e(TAG, "Cannot update the message: " + mUri, e);
196                }
197            }
198        }
199    }
200
201    @Override
202    protected void onDestroy() {
203        super.onDestroy();
204        cleanupSlideshowModel();
205    }
206
207    private void cleanupSlideshowModel() {
208        if (mSlideshowModel != null) {
209            mSlideshowModel.unregisterModelChangedObserver(
210                    mModelChangedObserver);
211            mSlideshowModel = null;
212        }
213    }
214
215    private void initSlideList() throws MmsException {
216        cleanupSlideshowModel();
217        mSlideshowModel = SlideshowModel.createFromMessageUri(this, mUri);
218        mSlideshowModel.registerModelChangedObserver(mModelChangedObserver);
219        mSlideshowEditor = new SlideshowEditor(this, mSlideshowModel);
220        mSlideListAdapter = new SlideListAdapter(
221                this, R.layout.slideshow_edit_item, mSlideshowModel);
222        mList.setAdapter(mSlideListAdapter);
223    }
224
225    @Override
226    public boolean onPrepareOptionsMenu(Menu menu) {
227        menu.clear();
228
229        int position = mList.getSelectedItemPosition();
230        if ((position >= 0) && (position != (mList.getCount() - 1))) {
231            // Selected one slide.
232            if (position > 0) {
233                menu.add(0, MENU_MOVE_UP, 0, R.string.move_up).setIcon(R.drawable.ic_menu_move_up);
234            }
235
236            if (position < (mSlideListAdapter.getCount() - 1)) {
237                menu.add(0, MENU_MOVE_DOWN, 0, R.string.move_down).setIcon(
238                        R.drawable.ic_menu_move_down);
239            }
240
241            menu.add(0, MENU_ADD_SLIDE, 0, R.string.add_slide).setIcon(R.drawable.ic_menu_add_slide);
242
243            menu.add(0, MENU_REMOVE_SLIDE, 0, R.string.remove_slide).setIcon(
244                    android.R.drawable.ic_menu_delete);
245        } else {
246            menu.add(0, MENU_ADD_SLIDE, 0, R.string.add_slide).setIcon(R.drawable.ic_menu_add_slide);
247        }
248
249        menu.add(0, MENU_DISCARD_SLIDESHOW, 0,
250                R.string.discard_slideshow).setIcon(R.drawable.ic_menu_delete_played);
251
252        return true;
253    }
254
255    @Override
256    public boolean onOptionsItemSelected(MenuItem item) {
257        int position = mList.getSelectedItemPosition();
258
259        switch (item.getItemId()) {
260            case MENU_MOVE_UP:
261                if ((position > 0) && (position < mSlideshowModel.size())) {
262                    mSlideshowEditor.moveSlideUp(position);
263                    mSlideListAdapter.notifyDataSetChanged();
264                    mList.setSelection(position - 1);
265                }
266                break;
267            case MENU_MOVE_DOWN:
268                if ((position >= 0) && (position < mSlideshowModel.size() - 1)) {
269                    mSlideshowEditor.moveSlideDown(position);
270                    mSlideListAdapter.notifyDataSetChanged();
271                    mList.setSelection(position + 1);
272                }
273                break;
274            case MENU_REMOVE_SLIDE:
275                if ((position >= 0) && (position < mSlideshowModel.size())) {
276                    mSlideshowEditor.removeSlide(position);
277                    mSlideListAdapter.notifyDataSetChanged();
278                }
279                break;
280            case MENU_ADD_SLIDE:
281                addNewSlide();
282                break;
283            case MENU_DISCARD_SLIDESHOW:
284                // delete all slides from slideshow.
285                mSlideshowEditor.removeAllSlides();
286                mSlideListAdapter.notifyDataSetChanged();
287                finish();
288                break;
289        }
290
291        return true;
292    }
293
294    private void openSlide(int index) {
295        Intent intent = new Intent(this, SlideEditorActivity.class);
296        intent.setData(mUri);
297        intent.putExtra(SlideEditorActivity.SLIDE_INDEX, index);
298        startActivityForResult(intent, REQUEST_CODE_EDIT_SLIDE);
299    }
300
301    private void adjustAddSlideVisibility() {
302        if (mSlideshowModel.size() >= SlideshowEditor.MAX_SLIDE_NUM) {
303            mAddSlideItem.setVisibility(View.GONE);
304        } else {
305            mAddSlideItem.setVisibility(View.VISIBLE);
306        }
307    }
308
309    private void addNewSlide() {
310        if ( mSlideshowEditor.addNewSlide() ) {
311            // add successfully
312            mSlideListAdapter.notifyDataSetChanged();
313
314            // Select the new slide.
315            mList.requestFocus();
316            mList.setSelection(mSlideshowModel.size() - 1);
317        } else {
318            Toast.makeText(this, R.string.cannot_add_slide_anymore,
319                    Toast.LENGTH_SHORT).show();
320        }
321    }
322
323    @Override
324    protected void onActivityResult(int requestCode, int resultCode,
325            Intent data) {
326        if (resultCode != RESULT_OK) {
327            return;
328        }
329
330        switch(requestCode) {
331            case REQUEST_CODE_EDIT_SLIDE:
332                synchronized (this) {
333                    mDirty = true;
334                }
335                setResult(RESULT_OK, mResultIntent);
336
337                if ((data != null) && data.getBooleanExtra("done", false)) {
338                    finish();
339                    return;
340                }
341
342                try {
343                    initSlideList();
344                    adjustAddSlideVisibility();
345                } catch (MmsException e) {
346                    Log.e(TAG, "Failed to initialize the slide-list.", e);
347                    finish();
348                    return;
349                }
350                break;
351        }
352    }
353
354    private static class SlideListAdapter extends ArrayAdapter<SlideModel> {
355        private final Context mContext;
356        private final int mResource;
357        private final LayoutInflater mInflater;
358        private final SlideshowModel mSlideshow;
359
360        public SlideListAdapter(Context context, int resource,
361                SlideshowModel slideshow) {
362            super(context, resource, slideshow);
363
364            mContext = context;
365            mResource = resource;
366            mInflater = LayoutInflater.from(context);
367            mSlideshow = slideshow;
368        }
369
370        @Override
371        public View getView(int position, View convertView, ViewGroup parent) {
372            return createViewFromResource(position, convertView, mResource);
373        }
374
375        private View createViewFromResource(int position, View convertView, int resource) {
376            SlideListItemView slideListItemView;
377            slideListItemView = (SlideListItemView) mInflater.inflate(
378                    resource, null);
379
380            // Show slide number.
381            TextView text;
382            text = (TextView) slideListItemView.findViewById(R.id.slide_number_text);
383            text.setText(mContext.getString(R.string.slide_number, position + 1));
384
385            SlideModel slide = getItem(position);
386            int dur = slide.getDuration() / 1000;
387            text = (TextView) slideListItemView.findViewById(R.id.duration_text);
388            text.setText(mContext.getResources().
389                         getQuantityString(R.plurals.slide_duration, dur, dur));
390
391            Presenter presenter = PresenterFactory.getPresenter(
392                    "SlideshowPresenter", mContext, slideListItemView, mSlideshow);
393            ((SlideshowPresenter) presenter).setLocation(position);
394            presenter.present(null);
395
396            return slideListItemView;
397        }
398    }
399
400    private final IModelChangedObserver mModelChangedObserver =
401        new IModelChangedObserver() {
402            public void onModelChanged(Model model, boolean dataChanged) {
403                synchronized (SlideshowEditActivity.this) {
404                    mDirty = true;
405                }
406                setResult(RESULT_OK, mResultIntent);
407                adjustAddSlideVisibility();
408            }
409        };
410
411    @Override
412    public void onCreateContextMenu(ContextMenu menu, View v,
413            ContextMenuInfo menuInfo) {
414        menu.setHeaderTitle(R.string.slideshow_options);
415
416        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
417        int position = info.position;
418
419        if ((position >= 0) && (position != (mList.getCount() - 1))) {
420            // Selected one slide.
421            if (position > 0) {
422                menu.add(0, MENU_MOVE_UP, 0, R.string.move_up).setIcon(R.drawable.ic_menu_move_up);
423            }
424            if (position < (mSlideListAdapter.getCount() - 1)) {
425                menu.add(0, MENU_MOVE_DOWN, 0, R.string.move_down).setIcon(
426                        R.drawable.ic_menu_move_down);
427            }
428
429            menu.add(0, MENU_ADD_SLIDE, 0, R.string.add_slide).setIcon(
430                    R.drawable.ic_menu_add_slide);
431
432            menu.add(0, MENU_REMOVE_SLIDE, 0, R.string.remove_slide).setIcon(
433                    android.R.drawable.ic_menu_delete);
434        }
435    }
436
437    @Override
438    public boolean onContextItemSelected(MenuItem item) {
439        AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
440        int position = info.position;
441
442        switch(item.getItemId()) {
443            case MENU_MOVE_UP:
444                if ((position > 0) && (position < mSlideshowModel.size())) {
445                    mSlideshowEditor.moveSlideUp(position);
446                    mSlideListAdapter.notifyDataSetChanged();
447                    mList.setSelection(position - 1);
448                }
449                break;
450            case MENU_MOVE_DOWN:
451                if ((position >= 0) && (position < mSlideshowModel.size() - 1)) {
452                    mSlideshowEditor.moveSlideDown(position);
453                    mSlideListAdapter.notifyDataSetChanged();
454                    mList.setSelection(position + 1);
455                }
456                break;
457            case MENU_REMOVE_SLIDE:
458                if ((position >= 0) && (position < mSlideshowModel.size())) {
459                    mSlideshowEditor.removeSlide(position);
460                    mSlideListAdapter.notifyDataSetChanged();
461                }
462                break;
463            case MENU_ADD_SLIDE:
464                addNewSlide();
465                break;
466            default:
467                break;
468        }
469
470        return true;
471    }
472}
473