• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onProjectEditState(String projectPath, boolean projectEdited)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
onVideoEditorCreated(String projectPath, VideoEditorProject project, List<MediaItem> mediaItems, List<AudioTrack> audioTracks, Exception exception)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
onVideoEditorLoaded(String projectPath, VideoEditorProject project, List<MediaItem> mediaItems, List<AudioTrack> audioTracks, Exception exception)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
onVideoEditorAspectRatioSet(String projectPath, int aspectRatio, Exception exception)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
onVideoEditorThemeApplied(String projectPath, String theme, Exception exception)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
onVideoEditorGeneratePreviewProgress(String projectPath, String className, String itemId, int action, int progress)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
onVideoEditorExportProgress(String projectPath, String filename, int progress)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
onVideoEditorExportComplete(String projectPath, String filename, Exception exception, boolean cancelled)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
onVideoEditorSaved(String projectPath, Exception exception)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
onVideoEditorReleased(String projectPath, Exception exception)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
onVideoEditorDeleted(String projectPath, Exception exception)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
onMediaItemAdded(String projectPath, String mediaItemId, MovieMediaItem mediaItem, String afterMediaItemId, Class<?> mediaItemClass, Integer newAspectRatio, Exception exception)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
onMediaLoaded(String projectPath, Uri mediaIUri, String mimeType, String filename, Exception exception)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
onMediaItemMoved(String projectPath, String mediaItemId, String afterMediaItemId, Exception exception)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
onMediaItemRemoved(String projectPath, String mediaItemId, MovieTransition transition, Exception exception)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
onMediaItemRenderingModeSet(String projectPath, String mediaItemId, int renderingMode, Exception exception)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
onMediaItemDurationSet(String projectPath, String mediaItemId, long durationMs, Exception exception)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
onMediaItemBoundariesSet(String projectPath, String mediaItemId, long beginBoundaryMs, long endBoundaryMs, Exception exception)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
onMediaItemThumbnail(String projectPath, String mediaItemId, Bitmap thumbnail, int index, int token, Exception exception)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
onTransitionInserted(String projectPath, MovieTransition transition, String afterMediaId, Exception exception)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
onTransitionRemoved(String projectPath, String transitionId, Exception exception)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
onTransitionDurationSet(String projectPath, String transitionId, long durationMs, Exception exception)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
onTransitionThumbnails(String projectPath, String transitionId, Bitmap[] thumbnails, Exception exception)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
onOverlayAdded(String projectPath, MovieOverlay overlay, String mediaItemId, Exception exception)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
onOverlayRemoved(String projectPath, String overlayId, String mediaItemId, Exception exception)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
onOverlayStartTimeSet(String projectPath, String overlayId, String mediaItemId, long startTimeMs, Exception exception)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
onOverlayDurationSet(String projectPath, String overlayId, String mediaItemId, long durationMs, Exception exception)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
onOverlayUserAttributesSet(String projectPath, String overlayId, String mediaItemId, Bundle userAttributes, Exception exception)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
onEffectAdded(String projectPath, MovieEffect effect, String mediaItemId, Exception exception)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
onEffectRemoved(String projectPath, String effectId, String mediaItemId, Exception exception)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
onAudioTrackAdded(String projectPath, MovieAudioTrack audioTrack, Exception exception)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
onAudioTrackRemoved(String projectPath, String audioTrackId, Exception exception)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
onAudioTrackBoundariesSet(String projectPath, String audioTrackId, long beginBoundaryMs, long endBoundaryMs, Exception exception)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
onAudioTrackExtractAudioWaveformProgress(String projectPath, String audioTrackId, int progress)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
onAudioTrackExtractAudioWaveformComplete(String projectPath, String audioTrackId, Exception exception)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
onCreate(Bundle savedInstanceState)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
onResume()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
onPause()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
onDestroy()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
onSaveInstanceState(Bundle outState)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      */
isProjectEdited()887     protected boolean isProjectEdited() {
888         return mProjectEditState;
889     }
890 
891     /**
892      * Enter the disabled state
893      *
894      * @param statusStringId The status string id
895      */
enterDisabledState(int statusStringId)896     protected abstract void enterDisabledState(int statusStringId);
897 
898     /**
899      * Enter the transitional state
900      *
901      * @param statusStringId The status string id
902      */
enterTransitionalState(int statusStringId)903     protected abstract void enterTransitionalState(int statusStringId);
904 
905     /**
906      * Enter the ready state
907      */
enterReadyState()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      */
showPreviewFrame()915     protected abstract boolean showPreviewFrame();
916 
917     /**
918      * The duration of the timeline has changed
919      */
updateTimelineDuration()920     protected abstract void updateTimelineDuration();
921 
922     /**
923      * Move the playhead to the specified position
924      *
925      * @param timeMs The time position
926      */
movePlayhead(long timeMs)927     protected abstract void movePlayhead(long timeMs);
928 
929     /**
930      * Change the aspect ratio
931      *
932      * @param aspectRatio The new aspect ratio
933      */
setAspectRatio(int aspectRatio)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      */
initializeFromProject(boolean updateUI)943     protected abstract void initializeFromProject(boolean updateUI);
944 
945     /**
946      * @return The media layout
947      */
getMediaLayout()948     protected abstract MediaLinearLayout getMediaLayout();
949 
950     /**
951      * @return The overlay layout
952      */
getOverlayLayout()953     protected abstract OverlayLinearLayout getOverlayLayout();
954 
955     /**
956      * @return The audio layout
957      */
getAudioTrackLayout()958     protected abstract AudioTrackLinearLayout getAudioTrackLayout();
959 
960     /**
961      * The export is progressing
962      */
onExportProgress(int progress)963     protected abstract void onExportProgress(int progress);
964 
965     /**
966      * The export has completed
967      */
onExportComplete()968     protected abstract void onExportComplete();
969 
970     /**
971      * @param projectEdited true if the project is edited
972      */
onProjectEditStateChange(boolean projectEdited)973     protected abstract void onProjectEditStateChange(boolean projectEdited);
974 }
975