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.R; 42 import com.android.mms.model.IModelChangedObserver; 43 import com.android.mms.model.Model; 44 import com.android.mms.model.SlideModel; 45 import com.android.mms.model.SlideshowModel; 46 import com.google.android.mms.MmsException; 47 import com.google.android.mms.pdu.PduBody; 48 import com.google.android.mms.pdu.PduPersister; 49 50 /** 51 * A list of slides which allows user to edit each item in it. 52 */ 53 public 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