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