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
463b3940d38105a8c8d50dd04ff50a77b8eff1311eErik Wolsheimer    private static final long BURNIN_PROTECTION_FIRST_WAKEUP_INTERVAL_MS =
473b3940d38105a8c8d50dd04ff50a77b8eff1311eErik Wolsheimer            TimeUnit.MINUTES.toMillis(1);
483b3940d38105a8c8d50dd04ff50a77b8eff1311eErik Wolsheimer    private static final long BURNIN_PROTECTION_SUBSEQUENT_WAKEUP_INTERVAL_MS =
493b3940d38105a8c8d50dd04ff50a77b8eff1311eErik Wolsheimer            TimeUnit.MINUTES.toMillis(2);
50d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private static final long BURNIN_PROTECTION_MINIMAL_INTERVAL_MS = TimeUnit.SECONDS.toMillis(10);
51d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
52c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski    private static final boolean DEBUG = false;
53c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski
54d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private static final String ACTION_BURN_IN_PROTECTION =
55d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            "android.internal.policy.action.BURN_IN_PROTECTION";
56d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
57d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private static final int BURN_IN_SHIFT_STEP = 2;
5817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    private static final long CENTERING_ANIMATION_DURATION_MS = 100;
5917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    private final ValueAnimator mCenteringAnimator;
60d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
61d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private boolean mBurnInProtectionActive;
6217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    private boolean mFirstUpdate;
63d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
64d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mMinHorizontalBurnInOffset;
65d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mMaxHorizontalBurnInOffset;
66d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mMinVerticalBurnInOffset;
67d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mMaxVerticalBurnInOffset;
68d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
69d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final int mBurnInRadiusMaxSquared;
70d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
71d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private int mLastBurnInXOffset = 0;
72d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    /* 1 means increasing, -1 means decreasing */
73d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private int mXOffsetDirection = 1;
74d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private int mLastBurnInYOffset = 0;
75d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    /* 1 means increasing, -1 means decreasing */
76d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private int mYOffsetDirection = 1;
77d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
7841e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski    private int mAppliedBurnInXOffset = 0;
7941e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski    private int mAppliedBurnInYOffset = 0;
8041e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski
81d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final AlarmManager mAlarmManager;
82d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final PendingIntent mBurnInProtectionIntent;
83d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final DisplayManagerInternal mDisplayManagerInternal;
84d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private final Display mDisplay;
85d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
86d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private BroadcastReceiver mBurnInProtectionReceiver = new BroadcastReceiver() {
87d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        @Override
88d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        public void onReceive(Context context, Intent intent) {
89c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            if (DEBUG) {
90c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                Slog.d(TAG, "onReceive " + intent);
91c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            }
92d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            updateBurnInProtection();
93d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
94d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    };
95c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski
96c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf    public BurnInProtectionHelper(Context context, int minHorizontalOffset,
97c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf            int maxHorizontalOffset, int minVerticalOffset, int maxVerticalOffset,
98c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf            int maxOffsetRadius) {
99c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        mMinHorizontalBurnInOffset = minHorizontalOffset;
100c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        mMaxHorizontalBurnInOffset = maxHorizontalOffset;
101c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        mMinVerticalBurnInOffset = minVerticalOffset;
10217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mMaxVerticalBurnInOffset = maxVerticalOffset;
103c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        if (maxOffsetRadius != BURN_IN_MAX_RADIUS_DEFAULT) {
104c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf            mBurnInRadiusMaxSquared = maxOffsetRadius * maxOffsetRadius;
105d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        } else {
106c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf            mBurnInRadiusMaxSquared = BURN_IN_MAX_RADIUS_DEFAULT;
107d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
108d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
109d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
110d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
111d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        context.registerReceiver(mBurnInProtectionReceiver,
112d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                new IntentFilter(ACTION_BURN_IN_PROTECTION));
113d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        Intent intent = new Intent(ACTION_BURN_IN_PROTECTION);
114d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        intent.setPackage(context.getPackageName());
115d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
116d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        mBurnInProtectionIntent = PendingIntent.getBroadcast(context, 0,
117d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                intent, PendingIntent.FLAG_UPDATE_CURRENT);
118d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        DisplayManager displayManager =
119d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
120d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        mDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
121d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        displayManager.registerDisplayListener(this, null /* handler */);
12217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
12317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator = ValueAnimator.ofFloat(1f, 0f);
12417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator.setDuration(CENTERING_ANIMATION_DURATION_MS);
12517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator.setInterpolator(new LinearInterpolator());
12617413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator.addListener(this);
12717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        mCenteringAnimator.addUpdateListener(this);
128d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
129d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
130d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void startBurnInProtection() {
131d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        if (!mBurnInProtectionActive) {
132d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            mBurnInProtectionActive = true;
13317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mFirstUpdate = true;
13417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mCenteringAnimator.cancel();
135d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            updateBurnInProtection();
136d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
137d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
138d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
139d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private void updateBurnInProtection() {
140d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        if (mBurnInProtectionActive) {
14117413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            // We don't want to adjust offsets immediately after the device goes into ambient mode.
14217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            // Instead, we want to wait until it's more likely that the user is not observing the
14317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            // screen anymore.
1443b3940d38105a8c8d50dd04ff50a77b8eff1311eErik Wolsheimer            final long interval = mFirstUpdate
1453b3940d38105a8c8d50dd04ff50a77b8eff1311eErik Wolsheimer                ? BURNIN_PROTECTION_FIRST_WAKEUP_INTERVAL_MS
1463b3940d38105a8c8d50dd04ff50a77b8eff1311eErik Wolsheimer                : BURNIN_PROTECTION_SUBSEQUENT_WAKEUP_INTERVAL_MS;
14717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            if (mFirstUpdate) {
14817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski                mFirstUpdate = false;
14917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            } else {
15017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski                adjustOffsets();
15141e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski                mAppliedBurnInXOffset = mLastBurnInXOffset;
15241e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski                mAppliedBurnInYOffset = mLastBurnInYOffset;
15317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski                mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
15417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski                        mLastBurnInXOffset, mLastBurnInYOffset);
15517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            }
156c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // We use currentTimeMillis to compute the next wakeup time since we want to wake up at
157c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // the same time as we wake up to update ambient mode to minimize power consumption.
158c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // However, we use elapsedRealtime to schedule the alarm so that setting the time can't
159c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // disable burn-in protection for extended periods.
160c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            final long nowWall = System.currentTimeMillis();
161c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            final long nowElapsed = SystemClock.elapsedRealtime();
162d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            // Next adjustment at least ten seconds in the future.
163c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            long nextWall = nowWall + BURNIN_PROTECTION_MINIMAL_INTERVAL_MS;
164d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            // And aligned to the minute.
1653b3940d38105a8c8d50dd04ff50a77b8eff1311eErik Wolsheimer            nextWall = (nextWall - (nextWall % interval)) + interval;
166c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            // Use elapsed real time that is adjusted to full minute on wall clock.
167c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            final long nextElapsed = nowElapsed + (nextWall - nowWall);
168c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            if (DEBUG) {
169c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                Slog.d(TAG, "scheduling next wake-up, now wall time " + nowWall
170c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                        + ", next wall: " + nextWall + ", now elapsed: " + nowElapsed
171c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                        + ", next elapsed: " + nextElapsed);
172c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            }
173c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextElapsed,
174c069f921d9bf9acf8d14f43049ac01ce43bc3a7eFilip Gruszczynski                    mBurnInProtectionIntent);
175d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        } else {
176d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            mAlarmManager.cancel(mBurnInProtectionIntent);
17717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mCenteringAnimator.start();
178d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
179d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
180d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
181d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void cancelBurnInProtection() {
182d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        if (mBurnInProtectionActive) {
183d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            mBurnInProtectionActive = false;
184d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            updateBurnInProtection();
185d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
186d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
187d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
188d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    /**
189d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * Gently shifts current burn-in offsets, minimizing the change for the user.
190d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     *
191d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * Shifts are applied in following fashion:
192d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 1) shift horizontally from minimum to the maximum;
193d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 2) shift vertically by one from minimum to the maximum;
194d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 3) shift horizontally from maximum to the minimum;
195d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 4) shift vertically by one from minimum to the maximum.
196d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * 5) if you reach the maximum vertically, start shifting back by one from maximum to minimum.
197d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     *
198d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * On top of that, stay within specified radius. If the shift distance from the center is
199d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     * higher than the radius, skip these values and go the next position that is within the radius.
200d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski     */
201d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    private void adjustOffsets() {
202d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        do {
203d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            // By default, let's just shift the X offset.
204d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            final int xChange = mXOffsetDirection * BURN_IN_SHIFT_STEP;
205d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            mLastBurnInXOffset += xChange;
206d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            if (mLastBurnInXOffset > mMaxHorizontalBurnInOffset
207d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    || mLastBurnInXOffset < mMinHorizontalBurnInOffset) {
208d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                // Whoops, we went too far horizontally. Let's retract..
209d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                mLastBurnInXOffset -= xChange;
210d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                // change horizontal direction..
211d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                mXOffsetDirection *= -1;
212d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                // and let's shift the Y offset.
213d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                final int yChange = mYOffsetDirection * BURN_IN_SHIFT_STEP;
214d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                mLastBurnInYOffset += yChange;
215d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                if (mLastBurnInYOffset > mMaxVerticalBurnInOffset
216d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                        || mLastBurnInYOffset < mMinVerticalBurnInOffset) {
217d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    // Whoops, we went to far vertically. Let's retract..
218d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    mLastBurnInYOffset -= yChange;
219d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    // and change vertical direction.
220d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    mYOffsetDirection *= -1;
221d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                }
222d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            }
223d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            // If we are outside of the radius, let's try again.
224c12569110d558e584fb9074dbe0c1aaf79ccdcd6Mark Renouf        } while (mBurnInRadiusMaxSquared != BURN_IN_MAX_RADIUS_DEFAULT
225d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                && mLastBurnInXOffset * mLastBurnInXOffset + mLastBurnInYOffset * mLastBurnInYOffset
226d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                        > mBurnInRadiusMaxSquared);
227d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
228d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
229d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void dump(String prefix, PrintWriter pw) {
230d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + TAG);
231d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        prefix += "  ";
232d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mBurnInProtectionActive=" + mBurnInProtectionActive);
233d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mHorizontalBurnInOffsetsBounds=(" + mMinHorizontalBurnInOffset + ", "
234d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                + mMaxHorizontalBurnInOffset + ")");
235d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mVerticalBurnInOffsetsBounds=(" + mMinVerticalBurnInOffset + ", "
236d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                + mMaxVerticalBurnInOffset + ")");
237d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mBurnInRadiusMaxSquared=" + mBurnInRadiusMaxSquared);
238d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mLastBurnInOffset=(" + mLastBurnInXOffset + ", "
239d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                + mLastBurnInYOffset + ")");
240d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        pw.println(prefix + "mOfsetChangeDirections=(" + mXOffsetDirection + ", "
241d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                + mYOffsetDirection + ")");
242d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
243d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
244d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    @Override
245d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void onDisplayAdded(int i) {
246d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
247d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
248d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    @Override
249d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void onDisplayRemoved(int i) {
250d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
251d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski
252d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    @Override
253d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    public void onDisplayChanged(int displayId) {
254d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        if (displayId == mDisplay.getDisplayId()) {
255d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            if (mDisplay.getState() == Display.STATE_DOZE
256d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                    || mDisplay.getState() == Display.STATE_DOZE_SUSPEND) {
257d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                startBurnInProtection();
258d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            } else {
259d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski                cancelBurnInProtection();
260d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski            }
261d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski        }
262d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski    }
26317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
26417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
26517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationStart(Animator animator) {
26617413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
26717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
26817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
26917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationEnd(Animator animator) {
27017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        if (animator == mCenteringAnimator && !mBurnInProtectionActive) {
27141e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski            mAppliedBurnInXOffset = 0;
27241e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski            mAppliedBurnInYOffset = 0;
27317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            // No matter how the animation finishes, we want to zero the offsets.
27417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(), 0, 0);
27517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        }
27617413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
27717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
27817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
27917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationCancel(Animator animator) {
28017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
28117413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
28217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
28317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationRepeat(Animator animator) {
28417413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
28517413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski
28617413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    @Override
28717413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    public void onAnimationUpdate(ValueAnimator valueAnimator) {
28817413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        if (!mBurnInProtectionActive) {
28917413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            final float value = (Float) valueAnimator.getAnimatedValue();
29017413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski            mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
29141e6346b4cac3b096d0986d49e7ceb94a082bc7aFilip Gruszczynski                    (int) (mAppliedBurnInXOffset * value), (int) (mAppliedBurnInYOffset * value));
29217413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski        }
29317413eb575f020e399bee3257f3221b85445d950Filip Gruszczynski    }
294d2e8640c2c1972d4f2e92d48660fa9e17d9291c4Filip Gruszczynski}
295