~ubuntu-branches/debian/stretch/insubstantial/stretch

« back to all changes in this revision

Viewing changes to trident/src/main/java/org/pushingpixels/trident/Timeline.java

  • Committer: Package Import Robot
  • Author(s): Felix Natter
  • Date: 2016-01-18 20:58:45 UTC
  • Revision ID: package-import@ubuntu.com-20160118205845-crbmrkda61qsi5qa
Tags: upstream-7.3+dfsg2
ImportĀ upstreamĀ versionĀ 7.3+dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions are met:
 
6
 *
 
7
 *  o Redistributions of source code must retain the above copyright notice,
 
8
 *    this list of conditions and the following disclaimer.
 
9
 *
 
10
 *  o Redistributions in binary form must reproduce the above copyright notice,
 
11
 *    this list of conditions and the following disclaimer in the documentation
 
12
 *    and/or other materials provided with the distribution.
 
13
 *
 
14
 *  o Neither the name of Trident Kirill Grouchnikov nor the names of
 
15
 *    its contributors may be used to endorse or promote products derived
 
16
 *    from this software without specific prior written permission.
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
19
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 
20
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
21
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
22
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
25
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
26
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 
27
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 
28
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
 */
 
30
package org.pushingpixels.trident;
 
31
 
 
32
import java.util.*;
 
33
 
 
34
import org.pushingpixels.trident.TimelineEngine.FullObjectID;
 
35
import org.pushingpixels.trident.TimelineEngine.TimelineOperationKind;
 
36
import org.pushingpixels.trident.TimelinePropertyBuilder.AbstractFieldInfo;
 
37
import org.pushingpixels.trident.callback.*;
 
38
import org.pushingpixels.trident.ease.Linear;
 
39
import org.pushingpixels.trident.ease.TimelineEase;
 
40
import org.pushingpixels.trident.interpolator.KeyFrames;
 
41
 
 
42
public class Timeline implements TimelineScenario.TimelineScenarioActor {
 
43
        Object mainObject;
 
44
 
 
45
        Comparable<?> secondaryId;
 
46
 
 
47
        FullObjectID fullObjectID;
 
48
 
 
49
        long duration;
 
50
 
 
51
        long initialDelay;
 
52
 
 
53
        long cycleDelay;
 
54
 
 
55
        boolean isLooping;
 
56
 
 
57
        int repeatCount;
 
58
 
 
59
        RepeatBehavior repeatBehavior;
 
60
 
 
61
        UIToolkitHandler uiToolkitHandler;
 
62
 
 
63
        Chain callback;
 
64
 
 
65
        String name;
 
66
 
 
67
        List<AbstractFieldInfo> propertiesToInterpolate;
 
68
 
 
69
        /**
 
70
         * Is used to create unique value for the {@link #id} field.
 
71
         */
 
72
        static long counter;
 
73
 
 
74
        /**
 
75
         * Unique ID.
 
76
         */
 
77
        protected long id;
 
78
 
 
79
        /**
 
80
         * Timeline position.
 
81
         */
 
82
        float durationFraction;
 
83
 
 
84
        /**
 
85
         * Timeline position.
 
86
         */
 
87
        float timelinePosition;
 
88
 
 
89
        long timeUntilPlay;
 
90
 
 
91
        /**
 
92
         * Indication whether the looping timeline should stop at reaching the end
 
93
         * of the cycle. Relevant only when {@link #isLooping} is <code>true</code>.
 
94
         */
 
95
        boolean toCancelAtCycleBreak;
 
96
 
 
97
        Stack<TimelineState> stateStack;
 
98
 
 
99
        TimelineEase ease;
 
100
 
 
101
        private int doneCount;
 
102
 
 
103
        public enum RepeatBehavior {
 
104
                LOOP, REVERSE
 
105
        }
 
106
 
 
107
        public enum TimelineState {
 
108
                IDLE(false), READY(false), PLAYING_FORWARD(true), PLAYING_REVERSE(true), SUSPENDED(
 
109
                                false), CANCELLED(false), DONE(false);
 
110
 
 
111
                private boolean isActive;
 
112
 
 
113
                private TimelineState(boolean isActive) {
 
114
                        this.isActive = isActive;
 
115
                }
 
116
        }
 
117
 
 
118
        private class Setter extends TimelineCallbackAdapter {
 
119
                @Override
 
120
                public void onTimelineStateChanged(TimelineState oldState,
 
121
                                TimelineState newState, float durationFraction,
 
122
                                float timelinePosition) {
 
123
                        if (newState == TimelineState.READY) {
 
124
                                for (AbstractFieldInfo fInfo : propertiesToInterpolate) {
 
125
                                        // check whether the object is in the ready state
 
126
                                        if ((uiToolkitHandler != null)
 
127
                                                        && !uiToolkitHandler.isInReadyState(fInfo.object))
 
128
                                                continue;
 
129
                                        fInfo.onStart();
 
130
                                }
 
131
                        }
 
132
 
 
133
                        // Fix for issue 5 - update field values only when
 
134
                        // either old or new state (or both) are active. Otherwise
 
135
                        // it's a transition between inactive states (such as from
 
136
                        // DONE to IDLE) that shouldn't trigger the property changes
 
137
                        if (oldState.isActive || newState.isActive) {
 
138
                                for (AbstractFieldInfo fInfo : propertiesToInterpolate) {
 
139
                                        // check whether the object is in the ready state
 
140
                                        if ((uiToolkitHandler != null)
 
141
                                                        && !uiToolkitHandler.isInReadyState(fInfo.object))
 
142
                                                continue;
 
143
                                        fInfo.updateFieldValue(timelinePosition);
 
144
                                }
 
145
                        }
 
146
                }
 
147
 
 
148
                @Override
 
149
                public void onTimelinePulse(float durationFraction,
 
150
                                float timelinePosition) {
 
151
                        for (AbstractFieldInfo fInfo : propertiesToInterpolate) {
 
152
                                // check whether the object is in the ready state
 
153
                                if ((uiToolkitHandler != null)
 
154
                                                && !uiToolkitHandler.isInReadyState(fInfo.object))
 
155
                                        continue;
 
156
                                // System.err.println("Timeline @" + Timeline.this.hashCode()
 
157
                                // + " at position " + timelinePosition);
 
158
                                fInfo.updateFieldValue(timelinePosition);
 
159
                        }
 
160
                }
 
161
        }
 
162
 
 
163
        @RunOnUIThread
 
164
        private class UISetter extends Setter {
 
165
        }
 
166
 
 
167
        class Chain implements TimelineCallback {
 
168
                private List<TimelineCallback> callbacks;
 
169
 
 
170
                public Chain(TimelineCallback... callbacks) {
 
171
                        this.callbacks = new ArrayList<TimelineCallback>();
 
172
                        for (TimelineCallback callback : callbacks)
 
173
                                this.callbacks.add(callback);
 
174
                }
 
175
 
 
176
                public void addCallback(TimelineCallback callback) {
 
177
                        this.callbacks.add(callback);
 
178
                }
 
179
 
 
180
                public void removeCallback(TimelineCallback callback) {
 
181
                        this.callbacks.remove(callback);
 
182
                }
 
183
 
 
184
                @Override
 
185
                public void onTimelineStateChanged(final TimelineState oldState,
 
186
                                final TimelineState newState, final float durationFraction,
 
187
                                final float timelinePosition) {
 
188
                        if ((uiToolkitHandler != null)
 
189
                                        && !uiToolkitHandler.isInReadyState(mainObject))
 
190
                                return;
 
191
                        for (int i = this.callbacks.size() - 1; i >= 0; i--) {
 
192
                                final TimelineCallback callback = this.callbacks.get(i);
 
193
                                // special handling for chained callbacks not running on UI
 
194
                                // thread
 
195
                                boolean shouldRunOnUIThread = false;
 
196
                                Class<?> clazz = callback.getClass();
 
197
                                while ((clazz != null) && !shouldRunOnUIThread) {
 
198
                                        shouldRunOnUIThread = clazz
 
199
                                                        .isAnnotationPresent(RunOnUIThread.class);
 
200
                                        clazz = clazz.getSuperclass();
 
201
                                }
 
202
                                if (shouldRunOnUIThread
 
203
                                                && (Timeline.this.uiToolkitHandler != null)) {
 
204
                                        Timeline.this.uiToolkitHandler.runOnUIThread(mainObject,
 
205
                                                        new Runnable() {
 
206
                                                                @Override
 
207
                                public void run() {
 
208
                                                                        callback.onTimelineStateChanged(oldState,
 
209
                                                                                        newState, durationFraction,
 
210
                                                                                        timelinePosition);
 
211
                                                                }
 
212
                                                        });
 
213
                                } else {
 
214
                                        callback.onTimelineStateChanged(oldState, newState,
 
215
                                                        durationFraction, timelinePosition);
 
216
                                }
 
217
                        }
 
218
                }
 
219
 
 
220
                @Override
 
221
                public void onTimelinePulse(final float durationFraction,
 
222
                                final float timelinePosition) {
 
223
                        if ((uiToolkitHandler != null)
 
224
                                        && !uiToolkitHandler.isInReadyState(mainObject))
 
225
                                return;
 
226
                        for (int i = this.callbacks.size() - 1; i >= 0; i--) {
 
227
                                final TimelineCallback callback = this.callbacks.get(i);
 
228
                                // special handling for chained callbacks not running on UI
 
229
                                // thread
 
230
                                boolean shouldRunOnUIThread = false;
 
231
                                Class<?> clazz = callback.getClass();
 
232
                                while ((clazz != null) && !shouldRunOnUIThread) {
 
233
                                        shouldRunOnUIThread = clazz
 
234
                                                        .isAnnotationPresent(RunOnUIThread.class);
 
235
                                        clazz = clazz.getSuperclass();
 
236
                                }
 
237
                                if (shouldRunOnUIThread
 
238
                                                && (Timeline.this.uiToolkitHandler != null)) {
 
239
                                        Timeline.this.uiToolkitHandler.runOnUIThread(mainObject,
 
240
                                                        new Runnable() {
 
241
                                                                @Override
 
242
                                public void run() {
 
243
                                                                        if (Timeline.this.getState() == TimelineState.CANCELLED)
 
244
                                                                                return;
 
245
                                                                        // System.err.println("Timeline @"
 
246
                                                                        // + Timeline.this.hashCode());
 
247
                                                                        callback.onTimelinePulse(durationFraction,
 
248
                                                                                        timelinePosition);
 
249
                                                                }
 
250
                                                        });
 
251
                                } else {
 
252
                                        // System.err.println("Timeline @" +
 
253
                                        // Timeline.this.hashCode());
 
254
                                        callback
 
255
                                                        .onTimelinePulse(durationFraction, timelinePosition);
 
256
                                }
 
257
                        }
 
258
                }
 
259
        }
 
260
 
 
261
        public Timeline() {
 
262
                this(null);
 
263
        }
 
264
 
 
265
        public Timeline(Object mainTimelineObject) {
 
266
                this.mainObject = mainTimelineObject;
 
267
 
 
268
                for (UIToolkitHandler uiToolkitHandler : TridentConfig.getInstance()
 
269
                                .getUIToolkitHandlers()) {
 
270
                        if (uiToolkitHandler.isHandlerFor(mainTimelineObject)) {
 
271
                                this.uiToolkitHandler = uiToolkitHandler;
 
272
                                break;
 
273
                        }
 
274
                }
 
275
 
 
276
                // if the main timeline object is handled by a UI toolkit handler,
 
277
                // the setters registered with the different addProperty
 
278
                // APIs need to run with the matching threading policy
 
279
                TimelineCallback setterCallback = (this.uiToolkitHandler != null) ? new UISetter()
 
280
                                : new Setter();
 
281
                this.callback = new Chain(setterCallback);
 
282
 
 
283
                this.duration = 500;
 
284
                this.propertiesToInterpolate = new ArrayList<AbstractFieldInfo>();
 
285
                this.id = Timeline.getId();
 
286
                // this.loopsToLive = -1;
 
287
 
 
288
                this.stateStack = new Stack<TimelineState>();
 
289
                this.stateStack.push(TimelineState.IDLE);
 
290
                this.doneCount = 0;
 
291
 
 
292
                this.ease = new Linear();
 
293
        }
 
294
 
 
295
        public final void setSecondaryID(Comparable<?> secondaryId) {
 
296
                if (this.getState() != TimelineState.IDLE) {
 
297
                        throw new IllegalArgumentException(
 
298
                                        "Cannot change state of non-idle timeline ["
 
299
                                                        + this.toString() + "]");
 
300
                }
 
301
                this.secondaryId = secondaryId;
 
302
        }
 
303
 
 
304
        public final void setDuration(long durationMs) {
 
305
                if (this.getState() != TimelineState.IDLE) {
 
306
                        throw new IllegalArgumentException(
 
307
                                        "Cannot change state of non-idle timeline ["
 
308
                                                        + this.toString() + "]");
 
309
                }
 
310
                this.duration = durationMs;
 
311
        }
 
312
 
 
313
        public final void setInitialDelay(long initialDelay) {
 
314
                if (this.getState() != TimelineState.IDLE) {
 
315
                        throw new IllegalArgumentException(
 
316
                                        "Cannot change state of non-idle timeline ["
 
317
                                                        + this.toString() + "]");
 
318
                }
 
319
                this.initialDelay = initialDelay;
 
320
        }
 
321
 
 
322
        public final void setCycleDelay(long cycleDelay) {
 
323
                if (this.getState() != TimelineState.IDLE) {
 
324
                        throw new IllegalArgumentException(
 
325
                                        "Cannot change state of non-idle timeline ["
 
326
                                                        + this.toString() + "]");
 
327
                }
 
328
                this.cycleDelay = cycleDelay;
 
329
        }
 
330
 
 
331
        public final void addCallback(TimelineCallback callback) {
 
332
                if (this.getState() != TimelineState.IDLE) {
 
333
                        throw new IllegalArgumentException(
 
334
                                        "Cannot change state of non-idle timeline ["
 
335
                                                        + this.toString() + "]");
 
336
                }
 
337
                this.callback.addCallback(callback);
 
338
        }
 
339
 
 
340
        public final void removeCallback(TimelineCallback callback) {
 
341
                if (this.getState() != TimelineState.IDLE) {
 
342
                        throw new IllegalArgumentException(
 
343
                                        "Cannot change state of non-idle timeline ["
 
344
                                                        + this.toString() + "]");
 
345
                }
 
346
                this.callback.removeCallback(callback);
 
347
        }
 
348
 
 
349
        public static <T> TimelinePropertyBuilder<T> property(String propertyName) {
 
350
                return new TimelinePropertyBuilder<T>(propertyName);
 
351
        }
 
352
 
 
353
        public final <T> void addPropertyToInterpolate(
 
354
                        TimelinePropertyBuilder<T> propertyBuilder) {
 
355
                this.propertiesToInterpolate.add(propertyBuilder.getFieldInfo(this));
 
356
        }
 
357
 
 
358
        public final <T> void addPropertyToInterpolate(String propName,
 
359
                        KeyFrames<T> keyFrames) {
 
360
                this.addPropertyToInterpolate(Timeline.<T> property(propName)
 
361
                                .goingThrough(keyFrames));
 
362
        }
 
363
 
 
364
        public final <T> void addPropertyToInterpolate(String propName, T from, T to) {
 
365
                this.addPropertyToInterpolate(Timeline.<T> property(propName)
 
366
                                .from(from).to(to));
 
367
        }
 
368
 
 
369
        @Override
 
370
    public void play() {
 
371
                this.playSkipping(0);
 
372
        }
 
373
 
 
374
        public void playSkipping(final long msToSkip) {
 
375
                if ((this.initialDelay + this.duration) < msToSkip) {
 
376
                        throw new IllegalArgumentException(
 
377
                                        "Required skip longer than initial delay + duration");
 
378
                }
 
379
                TimelineEngine.getInstance().runTimelineOperation(this,
 
380
                                TimelineOperationKind.PLAY, new Runnable() {
 
381
                                        @Override
 
382
                                        public void run() {
 
383
                                                Timeline.this.isLooping = false;
 
384
                                                TimelineEngine.getInstance().play(Timeline.this, false,
 
385
                                                                msToSkip);
 
386
                                        }
 
387
                                });
 
388
        }
 
389
 
 
390
        public void playReverse() {
 
391
                playReverseSkipping(0);
 
392
        }
 
393
 
 
394
        public void playReverseSkipping(final long msToSkip) {
 
395
                if ((this.initialDelay + this.duration) < msToSkip) {
 
396
                        throw new IllegalArgumentException(
 
397
                                        "Required skip longer than initial delay + duration");
 
398
                }
 
399
                TimelineEngine.getInstance().runTimelineOperation(this,
 
400
                                TimelineOperationKind.PLAY, new Runnable() {
 
401
                                        @Override
 
402
                                        public void run() {
 
403
                                                Timeline.this.isLooping = false;
 
404
                                                TimelineEngine.getInstance().playReverse(Timeline.this,
 
405
                                                                false, msToSkip);
 
406
                                        }
 
407
                                });
 
408
        }
 
409
 
 
410
        public void replay() {
 
411
                TimelineEngine.getInstance().runTimelineOperation(this,
 
412
                                TimelineOperationKind.PLAY, new Runnable() {
 
413
                                        @Override
 
414
                                        public void run() {
 
415
                                                Timeline.this.isLooping = false;
 
416
                                                TimelineEngine.getInstance().play(Timeline.this, true,
 
417
                                                                0);
 
418
                                        }
 
419
                                });
 
420
        }
 
421
 
 
422
        public void replayReverse() {
 
423
                TimelineEngine.getInstance().runTimelineOperation(this,
 
424
                                TimelineOperationKind.PLAY, new Runnable() {
 
425
                                        @Override
 
426
                                        public void run() {
 
427
                                                Timeline.this.isLooping = false;
 
428
                                                TimelineEngine.getInstance().playReverse(Timeline.this,
 
429
                                                                true, 0);
 
430
                                        }
 
431
                                });
 
432
        }
 
433
 
 
434
        public void playLoop(RepeatBehavior repeatBehavior) {
 
435
                this.playLoop(-1, repeatBehavior);
 
436
        }
 
437
 
 
438
        public void playLoopSkipping(RepeatBehavior repeatBehavior,
 
439
                        final long msToSkip) {
 
440
                this.playLoopSkipping(-1, repeatBehavior, msToSkip);
 
441
        }
 
442
 
 
443
        public void playLoop(int loopCount, RepeatBehavior repeatBehavior) {
 
444
                this.playLoopSkipping(loopCount, repeatBehavior, 0);
 
445
        }
 
446
 
 
447
        public void playLoopSkipping(final int loopCount,
 
448
                        final RepeatBehavior repeatBehavior, final long msToSkip) {
 
449
                if ((this.initialDelay + this.duration) < msToSkip) {
 
450
                        throw new IllegalArgumentException(
 
451
                                        "Required skip longer than initial delay + duration");
 
452
                }
 
453
                TimelineEngine.getInstance().runTimelineOperation(this,
 
454
                                TimelineOperationKind.PLAY, new Runnable() {
 
455
                                        @Override
 
456
                                        public void run() {
 
457
                                                Timeline.this.isLooping = true;
 
458
                                                Timeline.this.repeatCount = loopCount;
 
459
                                                Timeline.this.repeatBehavior = repeatBehavior;
 
460
                                                TimelineEngine.getInstance().playLoop(Timeline.this,
 
461
                                                                msToSkip);
 
462
                                        }
 
463
                                });
 
464
        }
 
465
 
 
466
        /**
 
467
         * Cancels this timeline. The timeline transitions to the
 
468
         * {@link TimelineState#CANCELLED} state, preserving its current timeline
 
469
         * position. After application callbacks and field interpolations are done
 
470
         * on the {@link TimelineState#CANCELLED} state, the timeline transitions to
 
471
         * the {@link TimelineState#IDLE} state. Application callbacks and field
 
472
         * interpolations are done on this state as well.
 
473
         * 
 
474
         * @see #end()
 
475
         * @see #abort()
 
476
         */
 
477
        public void cancel() {
 
478
                TimelineEngine.getInstance().runTimelineOperation(this,
 
479
                                TimelineOperationKind.CANCEL, null);
 
480
        }
 
481
 
 
482
        /**
 
483
         * Ends this timeline. The timeline transitions to the
 
484
         * {@link TimelineState#DONE} state, with the timeline position set to 0.0
 
485
         * or 1.0 - based on the direction of the timeline. After application
 
486
         * callbacks and field interpolations are done on the
 
487
         * {@link TimelineState#DONE} state, the timeline transitions to the
 
488
         * {@link TimelineState#IDLE} state. Application callbacks and field
 
489
         * interpolations are done on this state as well.
 
490
         * 
 
491
         * @see #cancel()
 
492
         * @see #abort()
 
493
         */
 
494
        public void end() {
 
495
                TimelineEngine.getInstance().runTimelineOperation(this,
 
496
                                TimelineOperationKind.END, null);
 
497
        }
 
498
 
 
499
        /**
 
500
         * Aborts this timeline. The timeline transitions to the
 
501
         * {@link TimelineState#IDLE} state. No application callbacks or field
 
502
         * interpolations are done.
 
503
         * 
 
504
         * @see #cancel()
 
505
         * @see #end()
 
506
         */
 
507
        public void abort() {
 
508
                TimelineEngine.getInstance().runTimelineOperation(this,
 
509
                                TimelineOperationKind.ABORT, null);
 
510
        }
 
511
 
 
512
        public void suspend() {
 
513
                TimelineEngine.getInstance().runTimelineOperation(this,
 
514
                                TimelineOperationKind.SUSPEND, null);
 
515
        }
 
516
 
 
517
        public void resume() {
 
518
                TimelineEngine.getInstance().runTimelineOperation(this,
 
519
                                TimelineOperationKind.RESUME, null);
 
520
        }
 
521
 
 
522
        /**
 
523
         * Requests that the specified timeline should stop at the end of the cycle.
 
524
         * This method should be called only on looping timelines.
 
525
         */
 
526
        public void cancelAtCycleBreak() {
 
527
                if (!this.isLooping)
 
528
                        throw new IllegalArgumentException(
 
529
                                        "Can only be called on looping timelines");
 
530
                this.toCancelAtCycleBreak = true;
 
531
        }
 
532
 
 
533
        /**
 
534
         * Returns a unique ID.
 
535
         * 
 
536
         * @return Unique ID.
 
537
         */
 
538
        protected static synchronized long getId() {
 
539
                return counter++;
 
540
        }
 
541
 
 
542
        public final float getTimelinePosition() {
 
543
                return this.timelinePosition;
 
544
        }
 
545
 
 
546
        public final float getDurationFraction() {
 
547
                return this.durationFraction;
 
548
        }
 
549
 
 
550
        public final TimelineState getState() {
 
551
                return this.stateStack.peek();
 
552
        }
 
553
 
 
554
        public final void setEase(TimelineEase ease) {
 
555
                if (this.getState() != TimelineState.IDLE) {
 
556
                        throw new IllegalArgumentException(
 
557
                                        "Cannot change state of non-idle timeline");
 
558
                }
 
559
                this.ease = ease;
 
560
        }
 
561
 
 
562
        @Override
 
563
        public boolean isDone() {
 
564
                return (this.doneCount > 0);
 
565
        }
 
566
 
 
567
        @Override
 
568
        public boolean supportsReplay() {
 
569
                return true;
 
570
        }
 
571
 
 
572
        @Override
 
573
        public void resetDoneFlag() {
 
574
                this.doneCount = 0;
 
575
        }
 
576
 
 
577
        @Override
 
578
        public String toString() {
 
579
                StringBuffer res = new StringBuffer();
 
580
                if (this.name != null) {
 
581
                        res.append(this.name);
 
582
                }
 
583
                if (this.mainObject != null) {
 
584
                        res.append(":" + this.mainObject.getClass().getName());
 
585
                }
 
586
                if (this.secondaryId != null) {
 
587
                        res.append(":" + this.secondaryId.toString());
 
588
                }
 
589
 
 
590
                res.append(" " + this.getState().name());
 
591
                res.append(":" + this.timelinePosition);
 
592
 
 
593
                return res.toString();
 
594
        }
 
595
 
 
596
        void replaceState(TimelineState state) {
 
597
                this.stateStack.pop();
 
598
                this.pushState(state);
 
599
        }
 
600
 
 
601
        void pushState(TimelineState state) {
 
602
                if (state == TimelineState.DONE)
 
603
                        this.doneCount++;
 
604
                this.stateStack.add(state);
 
605
        }
 
606
 
 
607
        TimelineState popState() {
 
608
                return this.stateStack.pop();
 
609
        }
 
610
 
 
611
        public final long getDuration() {
 
612
                return this.duration;
 
613
        }
 
614
 
 
615
        public String getName() {
 
616
                return name;
 
617
        }
 
618
 
 
619
        public void setName(String name) {
 
620
                this.name = name;
 
621
        }
 
622
 
 
623
        public Object getMainObject() {
 
624
                return this.mainObject;
 
625
        }
 
626
}
 
 
b'\\ No newline at end of file'