1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.videoeditor; 18 19 import java.util.List; 20 21 import android.app.Activity; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.graphics.Bitmap; 25 import android.media.AudioManager; 26 import android.media.videoeditor.AudioTrack; 27 import android.media.videoeditor.MediaItem; 28 import android.media.videoeditor.MediaVideoItem; 29 import android.media.videoeditor.Transition; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.util.Log; 33 import android.widget.Toast; 34 35 import com.android.videoeditor.service.ApiService; 36 import com.android.videoeditor.service.ApiServiceListener; 37 import com.android.videoeditor.service.MovieAudioTrack; 38 import com.android.videoeditor.service.MovieEffect; 39 import com.android.videoeditor.service.MovieMediaItem; 40 import com.android.videoeditor.service.MovieOverlay; 41 import com.android.videoeditor.service.MovieTransition; 42 import com.android.videoeditor.service.VideoEditorProject; 43 import com.android.videoeditor.widgets.AudioTrackLinearLayout; 44 import com.android.videoeditor.widgets.MediaLinearLayout; 45 import com.android.videoeditor.widgets.OverlayLinearLayout; 46 47 /** 48 * This activity handles callbacks from the service and manages the project 49 */ 50 public abstract class VideoEditorBaseActivity extends Activity { 51 // Logging 52 private static final String TAG = "VideoEditorBase"; 53 54 protected static final int DIALOG_DELETE_BAD_PROJECT_ID = 100; 55 56 // State keys 57 private static final String STATE_PROJECT_PATH = "path"; 58 private static final String STATE_EXPORT_FILENAME = "export_filename"; 59 60 // Dialog parameters 61 protected static final String PARAM_PROJECT_PATH = "path"; 62 63 // Instance variables 64 private final ServiceListener mServiceListener = new ServiceListener(); 65 protected String mProjectPath; 66 protected VideoEditorProject mProject; 67 protected String mPendingExportFilename; 68 private boolean mProjectEditState; 69 70 /** 71 * The service listener 72 */ 73 private class ServiceListener extends ApiServiceListener { 74 75 @Override 76 public void onProjectEditState(String projectPath, boolean projectEdited) { 77 // Check if the VideoEditor is the one we are expecting 78 if (!projectPath.equals(mProjectPath)) { 79 return; 80 } 81 82 if (mProjectEditState != projectEdited) { 83 mProjectEditState = projectEdited; 84 onProjectEditStateChange(projectEdited); 85 } 86 } 87 88 @Override 89 public void onVideoEditorCreated(String projectPath, VideoEditorProject project, 90 List<MediaItem> mediaItems, List<AudioTrack> audioTracks, Exception exception) { 91 // Check if the VideoEditor is the one we are expecting 92 if (!projectPath.equals(mProjectPath)) { 93 return; 94 } 95 96 // Check if an error occurred 97 if (exception != null) { 98 // Invalidate the project path 99 mProjectPath = null; 100 101 enterDisabledState(R.string.editor_no_project); 102 // Display an error 103 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_create_error, 104 Toast.LENGTH_LONG).show(); 105 } else { 106 enterReadyState(); 107 108 mProject = project; 109 initializeFromProject(true); 110 } 111 } 112 113 @Override 114 public void onVideoEditorLoaded(String projectPath, VideoEditorProject project, 115 List<MediaItem> mediaItems, List<AudioTrack> audioTracks, Exception exception) { 116 if (!projectPath.equals(mProjectPath)) { 117 return; 118 } 119 120 // Check if an error occurred 121 if (exception != null || project == null) { 122 mProjectPath = null; 123 124 enterDisabledState(R.string.editor_no_project); 125 126 final Bundle bundle = new Bundle(); 127 bundle.putString(VideoEditorActivity.PARAM_PROJECT_PATH, projectPath); 128 showDialog(DIALOG_DELETE_BAD_PROJECT_ID, bundle); 129 } else { 130 // The project may be loaded already. This can happen when we 131 // create a new project 132 if (mProject == null) { 133 mProject = project; 134 initializeFromProject(true); 135 } 136 } 137 } 138 139 @Override 140 public void onVideoEditorAspectRatioSet(String projectPath, int aspectRatio, 141 Exception exception) { 142 // Check if the VideoEditor is the one we are expecting 143 if (!projectPath.equals(mProjectPath)) { 144 return; 145 } 146 147 if (mProject == null) { 148 return; 149 } 150 151 if (exception != null) { 152 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_aspect_ratio_error, 153 Toast.LENGTH_LONG).show(); 154 } else { 155 // The aspect ratio has changed 156 setAspectRatio(aspectRatio); 157 } 158 } 159 160 @Override 161 public void onVideoEditorThemeApplied(String projectPath, String theme, 162 Exception exception) { 163 // Check if the VideoEditor is the one we are expecting 164 if (!projectPath.equals(mProjectPath)) { 165 return; 166 } 167 168 if (mProject == null) { 169 return; 170 } 171 172 if (exception != null) { 173 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_apply_theme_error, 174 Toast.LENGTH_LONG).show(); 175 } else { 176 getMediaLayout().addMediaItems(mProject.getMediaItems()); 177 getOverlayLayout().addMediaItems(mProject.getMediaItems()); 178 getAudioTrackLayout().addAudioTracks(mProject.getAudioTracks()); 179 180 updateTimelineDuration(); 181 } 182 } 183 184 @Override 185 public void onVideoEditorGeneratePreviewProgress(String projectPath, String className, 186 String itemId, int action, int progress) { 187 // Check if the VideoEditor is the one we are expecting 188 if (!projectPath.equals(mProjectPath)) { 189 return; 190 } 191 192 if (mProject == null) { 193 return; 194 } 195 196 if (Log.isLoggable(TAG, Log.VERBOSE)) { 197 Log.v(TAG, "onVideoEditorGeneratePreviewProgress: " + className + " " + progress); 198 } 199 200 if (className == null) { // Last callback (after all items are generated) 201 if (action == ApiService.ACTION_UPDATE_FRAME) { 202 showPreviewFrame(); 203 } 204 } else if (MediaItem.class.getCanonicalName().equals(className)) { 205 getMediaLayout().onGeneratePreviewMediaItemProgress(itemId, action, progress); 206 } else if (Transition.class.getCanonicalName().equals(className)) { 207 getMediaLayout().onGeneratePreviewTransitionProgress(itemId, action, progress); 208 } else if (AudioTrack.class.getCanonicalName().equals(className)) { 209 getAudioTrackLayout().onGeneratePreviewProgress(itemId, action, progress); 210 } else { 211 Log.w(TAG, "Unsupported storyboard item type: " + className); 212 } 213 } 214 215 @Override 216 public void onVideoEditorExportProgress(String projectPath, String filename, 217 int progress) { 218 // Check if the VideoEditor is the one we are expecting 219 if (!projectPath.equals(mProjectPath)) { 220 return; 221 } 222 223 if (mProject == null) { 224 return; 225 } 226 227 if (!filename.equals(mPendingExportFilename)) { 228 return; 229 } 230 231 // Update the export progress 232 onExportProgress(progress); 233 } 234 235 @Override 236 public void onVideoEditorExportComplete(String projectPath, String filename, 237 Exception exception, boolean cancelled) { 238 // Check if the VideoEditor is the one we are expecting 239 if (!projectPath.equals(mProjectPath)) { 240 return; 241 } 242 243 if (mProject == null) { 244 return; 245 } 246 247 if (!filename.equals(mPendingExportFilename)) { 248 return; 249 } 250 251 onExportComplete(); 252 253 mPendingExportFilename = null; 254 255 if (exception != null) { 256 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_export_error, 257 Toast.LENGTH_LONG).show(); 258 } 259 } 260 261 @Override 262 public void onVideoEditorSaved(String projectPath, Exception exception) { 263 // Check if the VideoEditor is the one we are expecting 264 if (!projectPath.equals(mProjectPath)) { 265 return; 266 } 267 268 if (exception != null) { 269 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_saved_error, 270 Toast.LENGTH_LONG).show(); 271 } 272 } 273 274 @Override 275 public void onVideoEditorReleased(String projectPath, Exception exception) { 276 if (exception != null) { 277 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_release_error, 278 Toast.LENGTH_LONG).show(); 279 } 280 } 281 282 @Override 283 public void onVideoEditorDeleted(String projectPath, Exception exception) { 284 if (exception != null) { 285 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_delete_error, 286 Toast.LENGTH_LONG).show(); 287 } 288 } 289 290 @Override 291 public void onMediaItemAdded(String projectPath, String mediaItemId, 292 MovieMediaItem mediaItem, String afterMediaItemId, Class<?> mediaItemClass, 293 Integer newAspectRatio, Exception exception) { 294 // Check if the VideoEditor is the one we are expecting. 295 if (!projectPath.equals(mProjectPath) || mProject == null) { 296 return; 297 } 298 299 if (exception != null) { 300 if (mediaItemClass.getCanonicalName().equals( 301 MediaVideoItem.class.getCanonicalName())) { 302 Toast.makeText(VideoEditorBaseActivity.this, 303 R.string.editor_add_video_clip_error, Toast.LENGTH_LONG).show(); 304 } else { 305 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_add_image_error, 306 Toast.LENGTH_LONG).show(); 307 } 308 } else { 309 getMediaLayout().insertMediaItem(mediaItem, afterMediaItemId); 310 getOverlayLayout().insertMediaItem(mediaItem, afterMediaItemId); 311 312 if (newAspectRatio != null) { 313 // The aspect ratio has changed 314 setAspectRatio(newAspectRatio); 315 } 316 317 updateTimelineDuration(); 318 } 319 } 320 321 @Override 322 public void onMediaLoaded(String projectPath, Uri mediaIUri, String mimeType, 323 String filename, Exception exception) { 324 // Check if the VideoEditor is the one we are expecting 325 if (!projectPath.equals(mProjectPath)) { 326 return; 327 } 328 329 if (mProject == null) { 330 return; 331 } 332 if (exception != null) { 333 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_media_load_error, 334 Toast.LENGTH_LONG).show(); 335 } else { 336 // Update the status of the downloaded item in the user interface 337 } 338 } 339 340 @Override 341 public void onMediaItemMoved(String projectPath, String mediaItemId, 342 String afterMediaItemId, Exception exception) { 343 // Check if the VideoEditor is the one we are expecting 344 if (!projectPath.equals(mProjectPath)) { 345 return; 346 } 347 348 if (mProject == null) { 349 return; 350 } 351 352 if (exception != null) { 353 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_move_media_item_error, 354 Toast.LENGTH_LONG).show(); 355 } else { 356 // Update the entire timeline 357 getMediaLayout().addMediaItems(mProject.getMediaItems()); 358 getOverlayLayout().addMediaItems(mProject.getMediaItems()); 359 360 updateTimelineDuration(); 361 } 362 } 363 364 @Override 365 public void onMediaItemRemoved(String projectPath, String mediaItemId, 366 MovieTransition transition, Exception exception) { 367 // Check if the VideoEditor is the one we are expecting 368 if (!projectPath.equals(mProjectPath)) { 369 return; 370 } 371 372 if (mProject == null) { 373 return; 374 } 375 376 if (exception != null) { 377 Toast.makeText(VideoEditorBaseActivity.this, 378 R.string.editor_remove_media_item_error, Toast.LENGTH_LONG).show(); 379 } else { 380 // Remove the media item and bounding transitions 381 getMediaLayout().removeMediaItem(mediaItemId, transition); 382 getOverlayLayout().removeMediaItem(mediaItemId); 383 384 updateTimelineDuration(); 385 } 386 } 387 388 @Override 389 public void onMediaItemRenderingModeSet(String projectPath, String mediaItemId, 390 int renderingMode, Exception exception) { 391 // Check if the VideoEditor is the one we are expecting 392 if (!projectPath.equals(mProjectPath)) { 393 return; 394 } 395 396 if (mProject == null) { 397 return; 398 } 399 400 if (exception != null) { 401 Toast.makeText(VideoEditorBaseActivity.this, 402 R.string.editor_set_rendering_mode_error, Toast.LENGTH_LONG).show(); 403 } 404 } 405 406 @Override 407 public void onMediaItemDurationSet(String projectPath, String mediaItemId, 408 long durationMs, Exception exception) { 409 // Check if the VideoEditor is the one we are expecting 410 if (!projectPath.equals(mProjectPath)) { 411 return; 412 } 413 414 if (mProject == null) { 415 return; 416 } 417 418 if (exception != null) { 419 Toast.makeText(VideoEditorBaseActivity.this, 420 R.string.editor_set_media_item_duration_error, Toast.LENGTH_LONG).show(); 421 } else { 422 final MovieMediaItem mediaItem = mProject.getMediaItem(mediaItemId); 423 // Update the media item 424 getMediaLayout().updateMediaItem(mediaItem); 425 getOverlayLayout().updateMediaItem(mediaItem); 426 427 updateTimelineDuration(); 428 } 429 } 430 431 @Override 432 public void onMediaItemBoundariesSet(String projectPath, String mediaItemId, 433 long beginBoundaryMs, long endBoundaryMs, Exception exception) { 434 // Check if the VideoEditor is the one we are expecting 435 if (!projectPath.equals(mProjectPath)) { 436 return; 437 } 438 439 if (mProject == null) { 440 return; 441 } 442 443 if (exception != null) { 444 Toast.makeText(VideoEditorBaseActivity.this, 445 R.string.editor_set_media_item_boundaries_error, Toast.LENGTH_LONG).show(); 446 } else { 447 final MovieMediaItem mediaItem = mProject.getMediaItem(mediaItemId); 448 getMediaLayout().updateMediaItem(mediaItem); 449 getOverlayLayout().updateMediaItem(mediaItem); 450 451 // Place the cursor at the beginning of the trimmed media item 452 //movePlayhead(mProject.getMediaItemBeginTime(mediaItemId)); 453 updateTimelineDuration(); 454 } 455 } 456 457 @Override 458 public boolean onMediaItemThumbnail(String projectPath, String mediaItemId, 459 Bitmap thumbnail, int index, int token, Exception exception) { 460 // Check if the VideoEditor is the one we are expecting 461 if (!projectPath.equals(mProjectPath)) { 462 return false; 463 } 464 465 if (mProject == null) { 466 return false; 467 } 468 469 if (exception != null) { 470 return false; 471 } else { 472 return getMediaLayout().setMediaItemThumbnail( 473 mediaItemId, thumbnail, index, token); 474 } 475 } 476 477 @Override 478 public void onTransitionInserted(String projectPath, MovieTransition transition, 479 String afterMediaId, Exception exception) { 480 // Check if the VideoEditor is the one we are expecting 481 if (!projectPath.equals(mProjectPath)) { 482 return; 483 } 484 485 if (mProject == null) { 486 return; 487 } 488 489 if (exception != null) { 490 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_add_transition_error, 491 Toast.LENGTH_LONG).show(); 492 } else { 493 getMediaLayout().addTransition(transition, afterMediaId); 494 495 updateTimelineDuration(); 496 } 497 } 498 499 @Override 500 public void onTransitionRemoved(String projectPath, String transitionId, 501 Exception exception) { 502 // Check if the VideoEditor is the one we are expecting 503 if (!projectPath.equals(mProjectPath)) { 504 return; 505 } 506 507 if (mProject == null) { 508 return; 509 } 510 511 if (exception != null) { 512 Toast.makeText(VideoEditorBaseActivity.this, 513 R.string.editor_remove_transition_error, Toast.LENGTH_LONG).show(); 514 } else { 515 getMediaLayout().removeTransition(transitionId); 516 517 updateTimelineDuration(); 518 } 519 } 520 521 @Override 522 public void onTransitionDurationSet(String projectPath, String transitionId, 523 long durationMs, Exception exception) { 524 // Check if the VideoEditor is the one we are expecting 525 if (!projectPath.equals(mProjectPath)) { 526 return; 527 } 528 529 if (mProject == null) { 530 return; 531 } 532 533 if (exception != null) { 534 Toast.makeText(VideoEditorBaseActivity.this, 535 R.string.editor_set_transition_duration_error, Toast.LENGTH_LONG).show(); 536 } else { 537 getMediaLayout().updateTransition(transitionId); 538 getOverlayLayout().refresh(); 539 540 updateTimelineDuration(); 541 } 542 } 543 544 @Override 545 public boolean onTransitionThumbnails(String projectPath, String transitionId, 546 Bitmap[] thumbnails, Exception exception) { 547 // Check if the VideoEditor is the one we are expecting 548 if (!projectPath.equals(mProjectPath)) { 549 return false; 550 } 551 552 if (mProject == null) { 553 return false; 554 } 555 556 if (exception != null) { 557 return false; 558 } else { 559 return getMediaLayout().setTransitionThumbnails(transitionId, thumbnails); 560 } 561 } 562 563 @Override 564 public void onOverlayAdded(String projectPath, MovieOverlay overlay, 565 String mediaItemId, Exception exception) { 566 // Check if the VideoEditor is the one we are expecting 567 if (!projectPath.equals(mProjectPath)) { 568 return; 569 } 570 571 if (mProject == null) { 572 return; 573 } 574 575 if (exception != null) { 576 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_add_overlay_error, 577 Toast.LENGTH_LONG).show(); 578 } else { 579 getMediaLayout().invalidateActionBar(); 580 getOverlayLayout().addOverlay(mediaItemId, overlay); 581 } 582 } 583 584 @Override 585 public void onOverlayRemoved(String projectPath, String overlayId, 586 String mediaItemId, Exception exception) { 587 // Check if the VideoEditor is the one we are expecting 588 if (!projectPath.equals(mProjectPath)) { 589 return; 590 } 591 592 if (mProject == null) { 593 return; 594 } 595 596 if (exception != null) { 597 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_remove_overlay_error, 598 Toast.LENGTH_LONG).show(); 599 } else { 600 getOverlayLayout().removeOverlay(mediaItemId, overlayId); 601 } 602 } 603 604 @Override 605 public void onOverlayStartTimeSet(String projectPath, String overlayId, 606 String mediaItemId, long startTimeMs, Exception exception) { 607 // Check if the VideoEditor is the one we are expecting 608 if (!projectPath.equals(mProjectPath)) { 609 return; 610 } 611 612 if (mProject == null) { 613 return; 614 } 615 616 if (exception != null) { 617 Toast.makeText(VideoEditorBaseActivity.this, 618 R.string.editor_set_start_time_overlay_error, Toast.LENGTH_LONG).show(); 619 } 620 } 621 622 @Override 623 public void onOverlayDurationSet(String projectPath, String overlayId, 624 String mediaItemId, long durationMs, Exception exception) { 625 // Check if the VideoEditor is the one we are expecting 626 if (!projectPath.equals(mProjectPath)) { 627 return; 628 } 629 630 if (mProject == null) { 631 return; 632 } 633 634 if (exception != null) { 635 Toast.makeText(VideoEditorBaseActivity.this, 636 R.string.editor_set_duration_overlay_error, Toast.LENGTH_LONG).show(); 637 } 638 } 639 640 @Override 641 public void onOverlayUserAttributesSet(String projectPath, String overlayId, 642 String mediaItemId, Bundle userAttributes, Exception exception) { 643 // Check if the VideoEditor is the one we are expecting 644 if (!projectPath.equals(mProjectPath)) { 645 return; 646 } 647 648 if (mProject == null) { 649 return; 650 } 651 652 if (exception != null) { 653 Toast.makeText(VideoEditorBaseActivity.this, 654 R.string.editor_set_user_attributes_overlay_error, 655 Toast.LENGTH_LONG).show(); 656 } else { 657 getOverlayLayout().updateOverlayAttributes(mediaItemId, overlayId, userAttributes); 658 } 659 } 660 661 @Override 662 public void onEffectAdded(String projectPath, MovieEffect effect, String mediaItemId, 663 Exception exception) { 664 // Check if the VideoEditor is the one we are expecting 665 if (!projectPath.equals(mProjectPath)) { 666 return; 667 } 668 669 if (mProject == null) { 670 return; 671 } 672 673 if (exception != null) { 674 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_add_effect_error, 675 Toast.LENGTH_LONG).show(); 676 } else { 677 getMediaLayout().updateMediaItem(mProject.getMediaItem(mediaItemId)); 678 } 679 } 680 681 @Override 682 public void onEffectRemoved(String projectPath, String effectId, String mediaItemId, 683 Exception exception) { 684 // Check if the VideoEditor is the one we are expecting 685 if (!projectPath.equals(mProjectPath)) { 686 return; 687 } 688 689 if (mProject == null) { 690 return; 691 } 692 693 if (exception != null) { 694 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_remove_effect_error, 695 Toast.LENGTH_LONG).show(); 696 } else { 697 // Remove the effect 698 getMediaLayout().updateMediaItem(mProject.getMediaItem(mediaItemId)); 699 } 700 } 701 702 @Override 703 public void onAudioTrackAdded(String projectPath, MovieAudioTrack audioTrack, 704 Exception exception) { 705 // Check if the VideoEditor is the one we are expecting 706 if (!projectPath.equals(mProjectPath)) { 707 return; 708 } 709 710 if (mProject == null) { 711 return; 712 } 713 714 if (exception != null) { 715 Toast.makeText(VideoEditorBaseActivity.this, R.string.editor_add_audio_track_error, 716 Toast.LENGTH_LONG).show(); 717 } else { 718 getAudioTrackLayout().addAudioTrack(audioTrack); 719 } 720 } 721 722 @Override 723 public void onAudioTrackRemoved(String projectPath, String audioTrackId, 724 Exception exception) { 725 // Check if the VideoEditor is the one we are expecting 726 if (!projectPath.equals(mProjectPath)) { 727 return; 728 } 729 730 if (mProject == null) { 731 return; 732 } 733 734 if (exception != null) { 735 Toast.makeText(VideoEditorBaseActivity.this, 736 R.string.editor_remove_audio_track_error, Toast.LENGTH_LONG).show(); 737 } else { 738 getAudioTrackLayout().removeAudioTrack(audioTrackId); 739 } 740 } 741 742 @Override 743 public void onAudioTrackBoundariesSet(String projectPath, String audioTrackId, 744 long beginBoundaryMs, long endBoundaryMs, Exception exception) { 745 // Check if the VideoEditor is the one we are expecting 746 if (!projectPath.equals(mProjectPath)) { 747 return; 748 } 749 750 if (mProject == null) { 751 return; 752 } 753 754 if (exception != null) { 755 Toast.makeText(VideoEditorBaseActivity.this, 756 R.string.editor_set_audio_track_boundaries_error, 757 Toast.LENGTH_LONG).show(); 758 } else { 759 getAudioTrackLayout().updateAudioTrack(audioTrackId); 760 } 761 } 762 763 @Override 764 public void onAudioTrackExtractAudioWaveformProgress(String projectPath, 765 String audioTrackId, int progress) { 766 // Check if the VideoEditor is the one we are expecting 767 if (!projectPath.equals(mProjectPath)) { 768 return; 769 } 770 771 if (mProject == null) { 772 return; 773 } 774 775 getAudioTrackLayout().setWaveformExtractionProgress(audioTrackId, progress); 776 } 777 778 @Override 779 public void onAudioTrackExtractAudioWaveformComplete(String projectPath, 780 String audioTrackId, Exception exception) { 781 // Check if the VideoEditor is the one we are expecting 782 if (!projectPath.equals(mProjectPath)) { 783 return; 784 } 785 786 if (mProject == null) { 787 return; 788 } 789 790 if (exception == null) { 791 getAudioTrackLayout().setWaveformExtractionComplete(audioTrackId); 792 } 793 } 794 } 795 796 @Override 797 public void onCreate(Bundle savedInstanceState) { 798 super.onCreate(savedInstanceState); 799 setContentView(R.layout.video_editor); 800 801 if (savedInstanceState != null) { 802 mProjectPath = savedInstanceState.getString(STATE_PROJECT_PATH); 803 mPendingExportFilename = savedInstanceState.getString(STATE_EXPORT_FILENAME); 804 } else { 805 final Intent intent = getIntent(); 806 mProjectPath = intent.getStringExtra(ProjectsActivity.PARAM_OPEN_PROJECT_PATH); 807 if (Intent.ACTION_INSERT.equals(intent.getAction())) { 808 ApiService.createVideoEditor(this, mProjectPath, 809 intent.getStringExtra(ProjectsActivity.PARAM_CREATE_PROJECT_NAME), 810 new String[0], new String[0], null); 811 } 812 } 813 } 814 815 @Override 816 public void onResume() { 817 super.onResume(); 818 819 mProjectEditState = ApiService.isProjectBeingEdited(mProjectPath); 820 onProjectEditStateChange(mProjectEditState); 821 822 ApiService.registerListener(mServiceListener); 823 824 // Check if we need to load the project 825 if (mProjectPath != null) { 826 if (mProject == null) { 827 // We need to load the project 828 ApiService.loadVideoEditor(this, mProjectPath); 829 enterTransitionalState(R.string.editor_loading_project); 830 } else { // The project is already loaded 831 initializeFromProject(false); 832 } 833 } else { 834 enterDisabledState(R.string.editor_no_project); 835 } 836 837 // Request the audio focus so that other apps can pause playback. 838 final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); 839 audioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, 840 AudioManager.AUDIOFOCUS_GAIN); 841 } 842 843 @Override 844 public void onPause() { 845 super.onPause(); 846 ApiService.unregisterListener(mServiceListener); 847 848 if (mProject != null) { 849 // Mark the project as clean. If/when we resume the activity 850 // we can check this flag in order to refresh the UI for changes 851 // which may had occurred when the activity was paused. 852 mProject.setClean(true); 853 // Save the contents of the current project 854 ApiService.saveVideoEditor(this, mProjectPath); 855 } 856 857 // Release the audio focus 858 final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); 859 audioManager.abandonAudioFocus(null); 860 } 861 862 @Override 863 public void onDestroy() { 864 super.onDestroy(); 865 866 // If we have an active project release the VideoEditor 867 if (mProjectPath != null) { 868 if (!isChangingConfigurations()) { 869 ApiService.releaseVideoEditor(this, mProjectPath); 870 } 871 872 mProjectPath = null; 873 } 874 } 875 876 @Override 877 public void onSaveInstanceState(Bundle outState) { 878 super.onSaveInstanceState(outState); 879 880 outState.putString(STATE_PROJECT_PATH, mProjectPath); 881 outState.putString(STATE_EXPORT_FILENAME, mPendingExportFilename); 882 } 883 884 /** 885 * @return true if the project is edited 886 */ 887 protected boolean isProjectEdited() { 888 return mProjectEditState; 889 } 890 891 /** 892 * Enter the disabled state 893 * 894 * @param statusStringId The status string id 895 */ 896 protected abstract void enterDisabledState(int statusStringId); 897 898 /** 899 * Enter the transitional state 900 * 901 * @param statusStringId The status string id 902 */ 903 protected abstract void enterTransitionalState(int statusStringId); 904 905 /** 906 * Enter the ready state 907 */ 908 protected abstract void enterReadyState(); 909 910 /** 911 * Show the preview frame at the current playhead position 912 * 913 * @return true if the surface is created, false otherwise. 914 */ 915 protected abstract boolean showPreviewFrame(); 916 917 /** 918 * The duration of the timeline has changed 919 */ 920 protected abstract void updateTimelineDuration(); 921 922 /** 923 * Move the playhead to the specified position 924 * 925 * @param timeMs The time position 926 */ 927 protected abstract void movePlayhead(long timeMs); 928 929 /** 930 * Change the aspect ratio 931 * 932 * @param aspectRatio The new aspect ratio 933 */ 934 protected abstract void setAspectRatio(int aspectRatio); 935 936 /** 937 * Initialize the project when restored from storage. 938 * 939 * @param updateUI true to update the UI 940 * 941 * Note that this method may be called also after the project was loaded 942 */ 943 protected abstract void initializeFromProject(boolean updateUI); 944 945 /** 946 * @return The media layout 947 */ 948 protected abstract MediaLinearLayout getMediaLayout(); 949 950 /** 951 * @return The overlay layout 952 */ 953 protected abstract OverlayLinearLayout getOverlayLayout(); 954 955 /** 956 * @return The audio layout 957 */ 958 protected abstract AudioTrackLinearLayout getAudioTrackLayout(); 959 960 /** 961 * The export is progressing 962 */ 963 protected abstract void onExportProgress(int progress); 964 965 /** 966 * The export has completed 967 */ 968 protected abstract void onExportComplete(); 969 970 /** 971 * @param projectEdited true if the project is edited 972 */ 973 protected abstract void onProjectEditStateChange(boolean projectEdited); 974 } 975