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