Home | History | Annotate | Download | only in deskclock
      1 /*
      2  * Copyright (C) 2016 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.deskclock;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.AnimatorSet;
     22 import android.view.View;
     23 import android.view.animation.AccelerateInterpolator;
     24 import android.view.animation.DecelerateInterpolator;
     25 import android.view.animation.Interpolator;
     26 
     27 import com.android.deskclock.uidata.UiDataModel;
     28 
     29 import static com.android.deskclock.AnimatorUtils.getAlphaAnimator;
     30 import static com.android.deskclock.AnimatorUtils.getScaleAnimator;
     31 
     32 /**
     33  * This runnable chooses a random initial position for {@link #mSaverView} within
     34  * {@link #mContentView} if {@link #mSaverView} is transparent. It also schedules itself to run
     35  * each minute, at which time {@link #mSaverView} is faded out, set to a new random location, and
     36  * faded in.
     37  */
     38 public final class MoveScreensaverRunnable implements Runnable {
     39 
     40     /** The duration over which the fade in/out animations occur. */
     41     private static final long FADE_TIME = 3000L;
     42 
     43     /** Accelerate the hide animation. */
     44     private final Interpolator mAcceleration = new AccelerateInterpolator();
     45 
     46     /** Decelerate the show animation. */
     47     private final Interpolator mDeceleration = new DecelerateInterpolator();
     48 
     49     /** The container that houses {@link #mSaverView}. */
     50     private final View mContentView;
     51 
     52     /** The display within the {@link #mContentView} that is randomly positioned. */
     53     private final View mSaverView;
     54 
     55     /** Tracks the currently executing animation if any; used to gracefully stop the animation. */
     56     private Animator mActiveAnimator;
     57 
     58     /**
     59      * @param contentView contains the {@code saverView}
     60      * @param saverView a child view of {@code contentView} that periodically moves around
     61      */
     62     public MoveScreensaverRunnable(View contentView, View saverView) {
     63         mContentView = contentView;
     64         mSaverView = saverView;
     65     }
     66 
     67     /**
     68      * Start or restart the random movement of the saver view within the content view.
     69      */
     70     public void start() {
     71         // Stop any existing animations or callbacks.
     72         stop();
     73 
     74         // Reset the alpha to 0 so saver view will be randomly positioned within the new bounds.
     75         mSaverView.setAlpha(0);
     76 
     77         // Execute the position updater runnable to choose the first random position of saver view.
     78         run();
     79 
     80         // Schedule callbacks every minute to adjust the position of mSaverView.
     81         UiDataModel.getUiDataModel().addMinuteCallback(this, -FADE_TIME);
     82     }
     83 
     84     /**
     85      * Stop the random movement of the saver view within the content view.
     86      */
     87     public void stop() {
     88         UiDataModel.getUiDataModel().removePeriodicCallback(this);
     89 
     90         // End any animation currently running.
     91         if (mActiveAnimator != null) {
     92             mActiveAnimator.end();
     93             mActiveAnimator = null;
     94         }
     95     }
     96 
     97     @Override
     98     public void run() {
     99         Utils.enforceMainLooper();
    100 
    101         final boolean selectInitialPosition = mSaverView.getAlpha() == 0f;
    102         if (selectInitialPosition) {
    103             // When selecting an initial position for the saver view the width and height of
    104             // mContentView are untrustworthy if this was caused by a configuration change. To
    105             // combat this, we position the mSaverView randomly within the smallest box that is
    106             // guaranteed to work.
    107             final int smallestDim = Math.min(mContentView.getWidth(), mContentView.getHeight());
    108             final float newX = getRandomPoint(smallestDim - mSaverView.getWidth());
    109             final float newY = getRandomPoint(smallestDim - mSaverView.getHeight());
    110 
    111             mSaverView.setX(newX);
    112             mSaverView.setY(newY);
    113             mActiveAnimator = getAlphaAnimator(mSaverView, 0f, 1f);
    114             mActiveAnimator.setDuration(FADE_TIME);
    115             mActiveAnimator.setInterpolator(mDeceleration);
    116             mActiveAnimator.start();
    117         } else {
    118             // Select a new random position anywhere in mContentView that will fit mSaverView.
    119             final float newX = getRandomPoint(mContentView.getWidth() - mSaverView.getWidth());
    120             final float newY = getRandomPoint(mContentView.getHeight() - mSaverView.getHeight());
    121 
    122             // Fade out and shrink the saver view.
    123             final AnimatorSet hide = new AnimatorSet();
    124             hide.setDuration(FADE_TIME);
    125             hide.setInterpolator(mAcceleration);
    126             hide.play(getAlphaAnimator(mSaverView, 1f, 0f))
    127                     .with(getScaleAnimator(mSaverView, 1f, 0.85f));
    128 
    129             // Fade in and grow the saver view after altering its position.
    130             final AnimatorSet show = new AnimatorSet();
    131             show.setDuration(FADE_TIME);
    132             show.setInterpolator(mDeceleration);
    133             show.play(getAlphaAnimator(mSaverView, 0f, 1f))
    134                     .with(getScaleAnimator(mSaverView, 0.85f, 1f));
    135             show.addListener(new AnimatorListenerAdapter() {
    136                 @Override
    137                 public void onAnimationStart(Animator animation) {
    138                     mSaverView.setX(newX);
    139                     mSaverView.setY(newY);
    140                 }
    141             });
    142 
    143             // Execute hide followed by show.
    144             final AnimatorSet all = new AnimatorSet();
    145             all.play(show).after(hide);
    146             mActiveAnimator = all;
    147             mActiveAnimator.start();
    148         }
    149     }
    150 
    151     /**
    152      * @return a random integer between 0 and the {@code maximum} exclusive.
    153      */
    154     private static float getRandomPoint(float maximum) {
    155         return (int) (Math.random() * maximum);
    156     }
    157 }