1c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski/*
2c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * Copyright (C) 2015 The Android Open Source Project
3c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski *
4c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * Licensed under the Apache License, Version 2.0 (the "License");
5c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * you may not use this file except in compliance with the License.
6c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * You may obtain a copy of the License at
7c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski *
8c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski *      http://www.apache.org/licenses/LICENSE-2.0
9c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski *
10c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * Unless required by applicable law or agreed to in writing, software
11c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * distributed under the License is distributed on an "AS IS" BASIS,
12c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * See the License for the specific language governing permissions and
14c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski * limitations under the License
15c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski */
16c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynski
17c045208fef869e71622f7a1596314766138a13c4Filip Gruszczynskipackage com.android.server.policy;
18d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
1917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynskiimport android.animation.Animator;
2017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynskiimport android.animation.ValueAnimator;
21d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.app.AlarmManager;
22d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.app.PendingIntent;
23d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.content.BroadcastReceiver;
24d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.content.Context;
25d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.content.Intent;
26d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.content.IntentFilter;
27d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.hardware.display.DisplayManager;
28d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.hardware.display.DisplayManagerInternal;
29d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.os.SystemClock;
30c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynskiimport android.util.Slog;
31d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport android.view.Display;
3217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynskiimport android.view.animation.LinearInterpolator;
33d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
34d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport com.android.server.LocalServices;
35d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
36d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport java.io.PrintWriter;
37d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynskiimport java.util.concurrent.TimeUnit;
38d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
3917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynskipublic class BurnInProtectionHelper implements DisplayManager.DisplayListener,
4017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
41d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private static final String TAG = "BurnInProtection";
42d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
43d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    // Default value when max burnin radius is not set.
44c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf    public static final int BURN_IN_MAX_RADIUS_DEFAULT = -1;
45d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
46d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private static final long BURNIN_PROTECTION_WAKEUP_INTERVAL_MS = TimeUnit.MINUTES.toMillis(1);
47d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private static final long BURNIN_PROTECTION_MINIMAL_INTERVAL_MS = TimeUnit.SECONDS.toMillis(10);
48d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
49c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski    private static final boolean DEBUG = false;
50c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski
51d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private static final String ACTION_BURN_IN_PROTECTION =
52d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            "android.internal.policy.action.BURN_IN_PROTECTION";
53d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
54d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private static final int BURN_IN_SHIFT_STEP = 2;
5517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    private static final long CENTERING_ANIMATION_DURATION_MS = 100;
5617413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    private final ValueAnimator mCenteringAnimator;
57d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
58d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private boolean mBurnInProtectionActive;
5917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    private boolean mFirstUpdate;
60d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
61d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mMinHorizontalBurnInOffset;
62d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mMaxHorizontalBurnInOffset;
63d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mMinVerticalBurnInOffset;
64d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mMaxVerticalBurnInOffset;
65d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
66d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mBurnInRadiusMaxSquared;
67d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
68d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private int mLastBurnInXOffset = 0;
69d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    /* 1 means increasing, -1 means decreasing */
70d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private int mXOffsetDirection = 1;
71d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private int mLastBurnInYOffset = 0;
72d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    /* 1 means increasing, -1 means decreasing */
73d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private int mYOffsetDirection = 1;
74d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
7541e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski    private int mAppliedBurnInXOffset = 0;
7641e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski    private int mAppliedBurnInYOffset = 0;
7741e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski
78d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final AlarmManager mAlarmManager;
79d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final PendingIntent mBurnInProtectionIntent;
80d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final DisplayManagerInternal mDisplayManagerInternal;
81d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final Display mDisplay;
82d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
83d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private BroadcastReceiver mBurnInProtectionReceiver = new BroadcastReceiver() {
84d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        @Override
85d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        public void onReceive(Context context, Intent intent) {
86c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            if (DEBUG) {
87c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                Slog.d(TAG, "onReceive " + intent);
88c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            }
89d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            updateBurnInProtection();
90d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
91d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    };
92c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski
93c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf    public BurnInProtectionHelper(Context context, int minHorizontalOffset,
94c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf            int maxHorizontalOffset, int minVerticalOffset, int maxVerticalOffset,
95c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf            int maxOffsetRadius) {
96c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        mMinHorizontalBurnInOffset = minHorizontalOffset;
97c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        mMaxHorizontalBurnInOffset = maxHorizontalOffset;
98c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        mMinVerticalBurnInOffset = minVerticalOffset;
9917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mMaxVerticalBurnInOffset = maxVerticalOffset;
100c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        if (maxOffsetRadius != BURN_IN_MAX_RADIUS_DEFAULT) {
101c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf            mBurnInRadiusMaxSquared = maxOffsetRadius * maxOffsetRadius;
102d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        } else {
103c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf            mBurnInRadiusMaxSquared = BURN_IN_MAX_RADIUS_DEFAULT;
104d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
105d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
106d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
107d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
108d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        context.registerReceiver(mBurnInProtectionReceiver,
109d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                new IntentFilter(ACTION_BURN_IN_PROTECTION));
110d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        Intent intent = new Intent(ACTION_BURN_IN_PROTECTION);
111d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        intent.setPackage(context.getPackageName());
112d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
113d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        mBurnInProtectionIntent = PendingIntent.getBroadcast(context, 0,
114d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                intent, PendingIntent.FLAG_UPDATE_CURRENT);
115d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        DisplayManager displayManager =
116d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
117d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        mDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
118d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        displayManager.registerDisplayListener(this, null /* handler */);
11917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
12017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator = ValueAnimator.ofFloat(1f, 0f);
12117413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator.setDuration(CENTERING_ANIMATION_DURATION_MS);
12217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator.setInterpolator(new LinearInterpolator());
12317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator.addListener(this);
12417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator.addUpdateListener(this);
125d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
126d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
127d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void startBurnInProtection() {
128d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        if (!mBurnInProtectionActive) {
129d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            mBurnInProtectionActive = true;
13017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mFirstUpdate = true;
13117413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mCenteringAnimator.cancel();
132d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            updateBurnInProtection();
133d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
134d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
135d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
136d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private void updateBurnInProtection() {
137d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        if (mBurnInProtectionActive) {
13817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            // We don't want to adjust offsets immediately after the device goes into ambient mode.
13917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            // Instead, we want to wait until it's more likely that the user is not observing the
14017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            // screen anymore.
14117413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            if (mFirstUpdate) {
14217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski                mFirstUpdate = false;
14317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            } else {
14417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski                adjustOffsets();
14541e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski                mAppliedBurnInXOffset = mLastBurnInXOffset;
14641e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski                mAppliedBurnInYOffset = mLastBurnInYOffset;
14717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski                mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
14817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski                        mLastBurnInXOffset, mLastBurnInYOffset);
14917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            }
150c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // We use currentTimeMillis to compute the next wakeup time since we want to wake up at
151c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // the same time as we wake up to update ambient mode to minimize power consumption.
152c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // However, we use elapsedRealtime to schedule the alarm so that setting the time can't
153c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // disable burn-in protection for extended periods.
154c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            final long nowWall = System.currentTimeMillis();
155c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            final long nowElapsed = SystemClock.elapsedRealtime();
156d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            // Next adjustment at least ten seconds in the future.
157c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            long nextWall = nowWall + BURNIN_PROTECTION_MINIMAL_INTERVAL_MS;
158d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            // And aligned to the minute.
159c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            nextWall = nextWall - nextWall % BURNIN_PROTECTION_WAKEUP_INTERVAL_MS
160d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    + BURNIN_PROTECTION_WAKEUP_INTERVAL_MS;
161c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // Use elapsed real time that is adjusted to full minute on wall clock.
162c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            final long nextElapsed = nowElapsed + (nextWall - nowWall);
163c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            if (DEBUG) {
164c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                Slog.d(TAG, "scheduling next wake-up, now wall time " + nowWall
165c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                        + ", next wall: " + nextWall + ", now elapsed: " + nowElapsed
166c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                        + ", next elapsed: " + nextElapsed);
167c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            }
168c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextElapsed,
169c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                    mBurnInProtectionIntent);
170d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        } else {
171d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            mAlarmManager.cancel(mBurnInProtectionIntent);
17217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mCenteringAnimator.start();
173d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
174d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
175d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
176d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void cancelBurnInProtection() {
177d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        if (mBurnInProtectionActive) {
178d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            mBurnInProtectionActive = false;
179d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            updateBurnInProtection();
180d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
181d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
182d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
183d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    /**
184d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * Gently shifts current burn-in offsets, minimizing the change for the user.
185d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     *
186d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * Shifts are applied in following fashion:
187d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 1) shift horizontally from minimum to the maximum;
188d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 2) shift vertically by one from minimum to the maximum;
189d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 3) shift horizontally from maximum to the minimum;
190d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 4) shift vertically by one from minimum to the maximum.
191d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 5) if you reach the maximum vertically, start shifting back by one from maximum to minimum.
192d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     *
193d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * On top of that, stay within specified radius. If the shift distance from the center is
194d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * higher than the radius, skip these values and go the next position that is within the radius.
195d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     */
196d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private void adjustOffsets() {
197d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        do {
198d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            // By default, let's just shift the X offset.
199d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            final int xChange = mXOffsetDirection * BURN_IN_SHIFT_STEP;
200d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            mLastBurnInXOffset += xChange;
201d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            if (mLastBurnInXOffset > mMaxHorizontalBurnInOffset
202d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    || mLastBurnInXOffset < mMinHorizontalBurnInOffset) {
203d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                // Whoops, we went too far horizontally. Let's retract..
204d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                mLastBurnInXOffset -= xChange;
205d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                // change horizontal direction..
206d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                mXOffsetDirection *= -1;
207d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                // and let's shift the Y offset.
208d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                final int yChange = mYOffsetDirection * BURN_IN_SHIFT_STEP;
209d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                mLastBurnInYOffset += yChange;
210d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                if (mLastBurnInYOffset > mMaxVerticalBurnInOffset
211d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                        || mLastBurnInYOffset < mMinVerticalBurnInOffset) {
212d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    // Whoops, we went to far vertically. Let's retract..
213d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    mLastBurnInYOffset -= yChange;
214d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    // and change vertical direction.
215d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    mYOffsetDirection *= -1;
216d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                }
217d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            }
218d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            // If we are outside of the radius, let's try again.
219c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        } while (mBurnInRadiusMaxSquared != BURN_IN_MAX_RADIUS_DEFAULT
220d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                && mLastBurnInXOffset * mLastBurnInXOffset + mLastBurnInYOffset * mLastBurnInYOffset
221d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                        > mBurnInRadiusMaxSquared);
222d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
223d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
224d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void dump(String prefix, PrintWriter pw) {
225d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + TAG);
226d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        prefix += "  ";
227d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mBurnInProtectionActive=" + mBurnInProtectionActive);
228d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mHorizontalBurnInOffsetsBounds=(" + mMinHorizontalBurnInOffset + ", "
229d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                + mMaxHorizontalBurnInOffset + ")");
230d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mVerticalBurnInOffsetsBounds=(" + mMinVerticalBurnInOffset + ", "
231d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                + mMaxVerticalBurnInOffset + ")");
232d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mBurnInRadiusMaxSquared=" + mBurnInRadiusMaxSquared);
233d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mLastBurnInOffset=(" + mLastBurnInXOffset + ", "
234d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                + mLastBurnInYOffset + ")");
235d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mOfsetChangeDirections=(" + mXOffsetDirection + ", "
236d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                + mYOffsetDirection + ")");
237d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
238d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
239d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    @Override
240d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void onDisplayAdded(int i) {
241d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
242d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
243d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    @Override
244d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void onDisplayRemoved(int i) {
245d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
246d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
247d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    @Override
248d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void onDisplayChanged(int displayId) {
249d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        if (displayId == mDisplay.getDisplayId()) {
250d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            if (mDisplay.getState() == Display.STATE_DOZE
251d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    || mDisplay.getState() == Display.STATE_DOZE_SUSPEND) {
252d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                startBurnInProtection();
253d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            } else {
254d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                cancelBurnInProtection();
255d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            }
256d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
257d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
25817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
25917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
26017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationStart(Animator animator) {
26117413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
26217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
26317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
26417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationEnd(Animator animator) {
26517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        if (animator == mCenteringAnimator && !mBurnInProtectionActive) {
26641e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski            mAppliedBurnInXOffset = 0;
26741e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski            mAppliedBurnInYOffset = 0;
26817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            // No matter how the animation finishes, we want to zero the offsets.
26917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(), 0, 0);
27017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        }
27117413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
27217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
27317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
27417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationCancel(Animator animator) {
27517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
27617413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
27717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
27817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationRepeat(Animator animator) {
27917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
28017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
28117413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
28217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationUpdate(ValueAnimator valueAnimator) {
28317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        if (!mBurnInProtectionActive) {
28417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            final float value = (Float) valueAnimator.getAnimatedValue();
28517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
28641e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski                    (int) (mAppliedBurnInXOffset * value), (int) (mAppliedBurnInYOffset * value));
28717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        }
28817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
289d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski}
290