KeyguardViewStateManager.java revision e3643138c89d49a01ba6a622ffaf71c9a95d5cdc
1b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller/* 2b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * Copyright (C) 2012 The Android Open Source Project 3b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * 4b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * Licensed under the Apache License, Version 2.0 (the "License"); 5b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * you may not use this file except in compliance with the License. 6b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * You may obtain a copy of the License at 7b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * 8b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * http://www.apache.org/licenses/LICENSE-2.0 9b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * 10b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * Unless required by applicable law or agreed to in writing, software 11b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * distributed under the License is distributed on an "AS IS" BASIS, 12b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * See the License for the specific language governing permissions and 14b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller * limitations under the License. 15b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller */ 16d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerpackage com.android.internal.policy.impl.keyguard; 17d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 186fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohenimport android.os.Handler; 196fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohenimport android.os.Looper; 20d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.View; 21d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 22d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerpublic class KeyguardViewStateManager implements SlidingChallengeLayout.OnChallengeScrolledListener { 23d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 24d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private KeyguardWidgetPager mPagedView; 2519a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller private ChallengeLayout mChallengeLayout; 266fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen private Runnable mHideHintsRunnable; 276fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen private KeyguardSecurityView mKeyguardSecurityContainer; 28d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private int[] mTmpPoint = new int[2]; 296fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen private static final int SCREEN_ON_HINT_DURATION = 1000; 3047c6cfaa3799951f999a4d2000cb628080708551Adam Cohen private static final int SCREEN_ON_RING_HINT_DELAY = 300; 316fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen Handler mMainQueue = new Handler(Looper.myLooper()); 32d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 33e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE; 34e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 35e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // Paged view state 36e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen private int mPageListeningToSlider = -1; 37e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen private int mCurrentPage = -1; 38e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 39d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller int mChallengeTop = 0; 40d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 41d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public KeyguardViewStateManager() { 42d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 43d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 44d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void setPagedView(KeyguardWidgetPager pagedView) { 45d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mPagedView = pagedView; 46d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 47d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 4819a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller public void setChallengeLayout(ChallengeLayout layout) { 4919a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller mChallengeLayout = layout; 50d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 51d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 526fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen public void setSecurityViewContainer(KeyguardSecurityView container) { 536fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen mKeyguardSecurityContainer = container; 546fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen } 556fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen 56d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void onPageBeginMoving() { 5719a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller if (mChallengeLayout.isChallengeShowing()) { 5819a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller mChallengeLayout.showChallenge(false); 59d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 606fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen if (mHideHintsRunnable != null) { 616fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen mMainQueue.removeCallbacks(mHideHintsRunnable); 626fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen mHideHintsRunnable = null; 636fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen } 64d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 65d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 66d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void onPageEndMoving() { 67d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 68d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 69b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller public void showBouncer(boolean show) { 7019a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller mChallengeLayout.showBouncer(); 71b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller } 72b5f3b707a7010e8d867da9342ea87cf0a1679692Jim Miller 73d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void onPageSwitch(View newPage, int newPageIndex) { 74e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // Reset the previous page size and ensure the current page is sized appropriately. 75e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // We only modify the page state if it is not currently under control by the slider. 76e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // This prevents conflicts. 77e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (mPagedView != null && mChallengeLayout != null) { 78e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen KeyguardWidgetFrame prevPage = mPagedView.getWidgetPageAt(mCurrentPage); 79e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (prevPage != null && mCurrentPage != mPageListeningToSlider) { 80e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen prevPage.resetSize(); 81d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 82d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 83d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller KeyguardWidgetFrame newCurPage = mPagedView.getWidgetPageAt(newPageIndex); 84e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping(); 85e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (challengeOverlapping && !newCurPage.isSmall() 86e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen && mPageListeningToSlider != newPageIndex) { 87e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen shrinkWidget(newCurPage); 88d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 89d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 90e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen mCurrentPage = newPageIndex; 91d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 92d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 93e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) { 94d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mTmpPoint[0] = 0; 95e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen mTmpPoint[1] = top; 9619a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller mapPoint((View) mChallengeLayout, frame, mTmpPoint); 97e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen return mTmpPoint[1]; 98e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen } 99e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 100e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen private void shrinkWidget(KeyguardWidgetFrame frame) { 101e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (frame != null && mChallengeLayout != null && 102e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen mChallengeLayout instanceof SlidingChallengeLayout) { 103e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout; 104e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen int top = scl.getMaxChallengeTop(); 105e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen frame.shrinkWidget(getChallengeTopRelativeToFrame(frame, top)); 106e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen } 107d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 108d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 109d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller /** 110d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Simple method to map a point from one view's coordinates to another's. Note: this method 111d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * doesn't account for transforms, so if the views will be transformed, this should not be used. 112d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 113d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param fromView The view to which the point is relative 114d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param toView The view into which the point should be mapped 115d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param pt The point 116d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 117d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void mapPoint(View fromView, View toView, int pt[]) { 118d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller int[] loc = new int[2]; 119d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller fromView.getLocationInWindow(loc); 120d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller int x = loc[0]; 121d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller int y = loc[1]; 122d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 123d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller toView.getLocationInWindow(loc); 124d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller int vX = loc[0]; 125d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller int vY = loc[1]; 126d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 127d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller pt[0] += x - vX; 128d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller pt[1] += y - vY; 129d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 130d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 131d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 132d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void onScrollStateChanged(int scrollState) { 133e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (mPagedView == null || mChallengeLayout == null) return; 134e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping(); 135e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 136d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) { 137e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen KeyguardWidgetFrame frame = mPagedView.getWidgetPageAt(mPageListeningToSlider); 138e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (frame == null) return; 139d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 140e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (!challengeOverlapping) { 141e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen frame.resetSize(); 142d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 143e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen frame.hideFrame(this); 144d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 14519a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller if (challengeOverlapping) { 146d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mPagedView.setOnlyAllowEdgeSwipes(true); 147d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } else { 148d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mPagedView.setOnlyAllowEdgeSwipes(false); 149d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 150d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 151bbba68a14ab578ab00400d4cf2ed5d1dcf60dbdfJim Miller if (mChallengeLayout.isChallengeShowing()) { 152bbba68a14ab578ab00400d4cf2ed5d1dcf60dbdfJim Miller mKeyguardSecurityContainer.onResume(); 153bbba68a14ab578ab00400d4cf2ed5d1dcf60dbdfJim Miller } else { 154bbba68a14ab578ab00400d4cf2ed5d1dcf60dbdfJim Miller mKeyguardSecurityContainer.onPause(); 155bbba68a14ab578ab00400d4cf2ed5d1dcf60dbdfJim Miller } 156e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen mPageListeningToSlider = -1; 157e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen } else if (mLastScrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) { 158e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // Whether dragging or settling, if the last state was idle, we use this signal 159e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // to update the current page who will receive events from the sliding challenge. 160e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // We resize the frame as appropriate. 161e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen mPageListeningToSlider = mPagedView.getNextPage(); 162e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen KeyguardWidgetFrame frame = mPagedView.getWidgetPageAt(mPageListeningToSlider); 163e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (frame == null) return; 164e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 165e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen frame.showFrame(this); 166e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 167e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // As soon as the security begins sliding, the widget becomes small (if it wasn't 168e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // small to begin with). 169e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (!frame.isSmall()) { 170e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen // We need to fetch the final page, in case the pages are in motion. 171e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen mPageListeningToSlider = mPagedView.getNextPage(); 172e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen System.out.println("Shrink widget from scroll state changed!"); 173e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen shrinkWidget(frame); 174e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen } 175bbba68a14ab578ab00400d4cf2ed5d1dcf60dbdfJim Miller // View is on the move. Pause the security view until it completes. 176bbba68a14ab578ab00400d4cf2ed5d1dcf60dbdfJim Miller mKeyguardSecurityContainer.onPause(); 177d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 178e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen mLastScrollState = scrollState; 179e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen } 180e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 181e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen @Override 182e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen public void onScrollPositionChanged(float scrollPosition, int challengeTop) { 183e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen mChallengeTop = challengeTop; 184e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen KeyguardWidgetFrame frame = mPagedView.getWidgetPageAt(mPageListeningToSlider); 185e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (frame != null) { 186e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen frame.adjustFrame(getChallengeTopRelativeToFrame(frame, mChallengeTop)); 187e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen } 188d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 189d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1906fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen public void showUsabilityHints() { 19147c6cfaa3799951f999a4d2000cb628080708551Adam Cohen mMainQueue.postDelayed( new Runnable() { 19247c6cfaa3799951f999a4d2000cb628080708551Adam Cohen @Override 19347c6cfaa3799951f999a4d2000cb628080708551Adam Cohen public void run() { 19447c6cfaa3799951f999a4d2000cb628080708551Adam Cohen mKeyguardSecurityContainer.showUsabilityHint(); 19547c6cfaa3799951f999a4d2000cb628080708551Adam Cohen } 19647c6cfaa3799951f999a4d2000cb628080708551Adam Cohen } , SCREEN_ON_RING_HINT_DELAY); 1976fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen mPagedView.showInitialPageHints(); 1986fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen mHideHintsRunnable = new Runnable() { 1996fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen @Override 2006fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen public void run() { 2016fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen mPagedView.hideOutlinesAndSidePages(); 2026fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen mHideHintsRunnable = null; 2036fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen } 2046fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen }; 2056fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen 2066fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION); 2076fb841fa219eaae3e87f2fdc05e105d7a1813c42Adam Cohen } 208d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller} 209