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