Loading packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +6 −1 Original line number Diff line number Diff line Loading @@ -108,7 +108,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> Log.d(TAG, "Starting countdown"); // Close QS, otherwise the permission dialog appears beneath it getHost().collapsePanels(); mController.launchRecordPrompt(this); mController.launchRecordPrompt(); } private void cancelCountdown() { Loading @@ -128,6 +128,11 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> refreshState(); } @Override public void onCountdownEnd() { refreshState(); } @Override public void onRecordingStart() { refreshState(); Loading packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +15 −12 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.content.Intent; import android.os.CountDownTimer; import android.util.Log; import com.android.systemui.qs.tiles.ScreenRecordTile; import com.android.systemui.statusbar.policy.CallbackController; import java.util.ArrayList; Loading Loading @@ -62,7 +61,7 @@ public class RecordingController /** * Show dialog of screen recording options to user. */ public void launchRecordPrompt(ScreenRecordTile tileToUpdate) { public void launchRecordPrompt() { final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE, SYSUI_SCREENRECORD_LAUNCHER); final Intent intent = new Intent(); Loading @@ -73,15 +72,17 @@ public class RecordingController /** * Start counting down in preparation to start a recording * @param ms Time in ms to count down * @param ms Total time in ms to wait before starting * @param interval Time in ms per countdown step * @param startIntent Intent to start a recording * @param stopIntent Intent to stop a recording */ public void startCountdown(long ms, PendingIntent startIntent, PendingIntent stopIntent) { public void startCountdown(long ms, long interval, PendingIntent startIntent, PendingIntent stopIntent) { mIsStarting = true; mStopIntent = stopIntent; mCountDownTimer = new CountDownTimer(ms, 1000) { mCountDownTimer = new CountDownTimer(ms, interval) { @Override public void onTick(long millisUntilFinished) { for (RecordingStateChangeCallback cb : mListeners) { Loading @@ -94,7 +95,7 @@ public class RecordingController mIsStarting = false; mIsRecording = true; for (RecordingStateChangeCallback cb : mListeners) { cb.onRecordingEnd(); cb.onCountdownEnd(); } try { startIntent.send(); Loading @@ -120,7 +121,7 @@ public class RecordingController mIsStarting = false; for (RecordingStateChangeCallback cb : mListeners) { cb.onRecordingEnd(); cb.onCountdownEnd(); } } Loading @@ -144,16 +145,12 @@ public class RecordingController * Stop the recording */ public void stopRecording() { updateState(false); try { mStopIntent.send(); updateState(false); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Error stopping: " + e.getMessage()); } for (RecordingStateChangeCallback cb : mListeners) { cb.onRecordingEnd(); } } /** Loading Loading @@ -192,6 +189,12 @@ public class RecordingController */ default void onCountdown(long millisUntilFinished) {} /** * Called when a countdown to recording has ended. This is a separate method so that if * needed, listeners can handle cases where recording fails to start */ default void onCountdownEnd() {} /** * Called when a screen recording has started */ Loading packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java +2 −1 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import javax.inject.Inject; */ public class ScreenRecordDialog extends Activity { private static final long DELAY_MS = 3000; private static final long INTERVAL_MS = 1000; private final RecordingController mController; private Switch mAudioSwitch; Loading Loading @@ -83,6 +84,6 @@ public class ScreenRecordDialog extends Activity { RecordingService.REQUEST_CODE, RecordingService.getStopIntent(this), PendingIntent.FLAG_UPDATE_CURRENT); mController.startCountdown(DELAY_MS, startIntent, stopIntent); mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent); } } packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +7 −1 Original line number Diff line number Diff line Loading @@ -677,6 +677,12 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotScreenRecord, true); } @Override public void onCountdownEnd() { if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown"); mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false)); } @Override public void onRecordingStart() { if (DEBUG) Log.d(TAG, "screenrecord: showing icon"); Loading @@ -687,7 +693,7 @@ public class PhoneStatusBarPolicy @Override public void onRecordingEnd() { // Ensure this is on the main thread, since it could be called during countdown // Ensure this is on the main thread if (DEBUG) Log.d(TAG, "screenrecord: hiding icon"); mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false)); } Loading packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.qs.tiles; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.service.quicksettings.Tile; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.QSTileHost; import com.android.systemui.screenrecord.RecordingController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest public class ScreenRecordTileTest extends SysuiTestCase { @Mock private RecordingController mController; @Mock private QSTileHost mHost; private TestableLooper mTestableLooper; private ScreenRecordTile mTile; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); mController = mDependency.injectMockDependency(RecordingController.class); when(mHost.getContext()).thenReturn(mContext); mTile = new ScreenRecordTile(mHost, mController); } // Test that the tile is inactive and labeled correctly when the controller is neither starting // or recording, and that clicking on the tile in this state brings up the record prompt @Test public void testNotActive() { when(mController.isStarting()).thenReturn(false); when(mController.isRecording()).thenReturn(false); mTile.refreshState(); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_INACTIVE, mTile.getState().state); assertTrue(mTile.getState().secondaryLabel.toString().equals( mContext.getString(R.string.quick_settings_screen_record_start))); mTile.handleClick(); verify(mController, times(1)).launchRecordPrompt(); } // Test that the tile is active and labeled correctly when the controller is starting @Test public void testIsStarting() { when(mController.isStarting()).thenReturn(true); when(mController.isRecording()).thenReturn(false); mTile.refreshState(); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_ACTIVE, mTile.getState().state); assertTrue(mTile.getState().secondaryLabel.toString().endsWith("...")); } // Test that the tile cancels countdown if it is clicked when the controller is starting @Test public void testCancelRecording() { when(mController.isStarting()).thenReturn(true); when(mController.isRecording()).thenReturn(false); mTile.handleClick(); verify(mController, times(1)).cancelCountdown(); } // Test that the tile is active and labeled correctly when the controller is recording @Test public void testIsRecording() { when(mController.isStarting()).thenReturn(false); when(mController.isRecording()).thenReturn(true); mTile.refreshState(); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_ACTIVE, mTile.getState().state); assertTrue(mTile.getState().secondaryLabel.toString().equals( mContext.getString(R.string.quick_settings_screen_record_stop))); } // Test that the tile stops the recording if it is clicked when the controller is recording @Test public void testStopRecording() { when(mController.isStarting()).thenReturn(false); when(mController.isRecording()).thenReturn(true); mTile.handleClick(); verify(mController, times(1)).stopRecording(); } } Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +6 −1 Original line number Diff line number Diff line Loading @@ -108,7 +108,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> Log.d(TAG, "Starting countdown"); // Close QS, otherwise the permission dialog appears beneath it getHost().collapsePanels(); mController.launchRecordPrompt(this); mController.launchRecordPrompt(); } private void cancelCountdown() { Loading @@ -128,6 +128,11 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> refreshState(); } @Override public void onCountdownEnd() { refreshState(); } @Override public void onRecordingStart() { refreshState(); Loading
packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +15 −12 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.content.Intent; import android.os.CountDownTimer; import android.util.Log; import com.android.systemui.qs.tiles.ScreenRecordTile; import com.android.systemui.statusbar.policy.CallbackController; import java.util.ArrayList; Loading Loading @@ -62,7 +61,7 @@ public class RecordingController /** * Show dialog of screen recording options to user. */ public void launchRecordPrompt(ScreenRecordTile tileToUpdate) { public void launchRecordPrompt() { final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE, SYSUI_SCREENRECORD_LAUNCHER); final Intent intent = new Intent(); Loading @@ -73,15 +72,17 @@ public class RecordingController /** * Start counting down in preparation to start a recording * @param ms Time in ms to count down * @param ms Total time in ms to wait before starting * @param interval Time in ms per countdown step * @param startIntent Intent to start a recording * @param stopIntent Intent to stop a recording */ public void startCountdown(long ms, PendingIntent startIntent, PendingIntent stopIntent) { public void startCountdown(long ms, long interval, PendingIntent startIntent, PendingIntent stopIntent) { mIsStarting = true; mStopIntent = stopIntent; mCountDownTimer = new CountDownTimer(ms, 1000) { mCountDownTimer = new CountDownTimer(ms, interval) { @Override public void onTick(long millisUntilFinished) { for (RecordingStateChangeCallback cb : mListeners) { Loading @@ -94,7 +95,7 @@ public class RecordingController mIsStarting = false; mIsRecording = true; for (RecordingStateChangeCallback cb : mListeners) { cb.onRecordingEnd(); cb.onCountdownEnd(); } try { startIntent.send(); Loading @@ -120,7 +121,7 @@ public class RecordingController mIsStarting = false; for (RecordingStateChangeCallback cb : mListeners) { cb.onRecordingEnd(); cb.onCountdownEnd(); } } Loading @@ -144,16 +145,12 @@ public class RecordingController * Stop the recording */ public void stopRecording() { updateState(false); try { mStopIntent.send(); updateState(false); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Error stopping: " + e.getMessage()); } for (RecordingStateChangeCallback cb : mListeners) { cb.onRecordingEnd(); } } /** Loading Loading @@ -192,6 +189,12 @@ public class RecordingController */ default void onCountdown(long millisUntilFinished) {} /** * Called when a countdown to recording has ended. This is a separate method so that if * needed, listeners can handle cases where recording fails to start */ default void onCountdownEnd() {} /** * Called when a screen recording has started */ Loading
packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java +2 −1 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import javax.inject.Inject; */ public class ScreenRecordDialog extends Activity { private static final long DELAY_MS = 3000; private static final long INTERVAL_MS = 1000; private final RecordingController mController; private Switch mAudioSwitch; Loading Loading @@ -83,6 +84,6 @@ public class ScreenRecordDialog extends Activity { RecordingService.REQUEST_CODE, RecordingService.getStopIntent(this), PendingIntent.FLAG_UPDATE_CURRENT); mController.startCountdown(DELAY_MS, startIntent, stopIntent); mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent); } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +7 −1 Original line number Diff line number Diff line Loading @@ -677,6 +677,12 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotScreenRecord, true); } @Override public void onCountdownEnd() { if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown"); mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false)); } @Override public void onRecordingStart() { if (DEBUG) Log.d(TAG, "screenrecord: showing icon"); Loading @@ -687,7 +693,7 @@ public class PhoneStatusBarPolicy @Override public void onRecordingEnd() { // Ensure this is on the main thread, since it could be called during countdown // Ensure this is on the main thread if (DEBUG) Log.d(TAG, "screenrecord: hiding icon"); mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false)); } Loading
packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java 0 → 100644 +136 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.qs.tiles; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.service.quicksettings.Tile; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.QSTileHost; import com.android.systemui.screenrecord.RecordingController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest public class ScreenRecordTileTest extends SysuiTestCase { @Mock private RecordingController mController; @Mock private QSTileHost mHost; private TestableLooper mTestableLooper; private ScreenRecordTile mTile; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); mController = mDependency.injectMockDependency(RecordingController.class); when(mHost.getContext()).thenReturn(mContext); mTile = new ScreenRecordTile(mHost, mController); } // Test that the tile is inactive and labeled correctly when the controller is neither starting // or recording, and that clicking on the tile in this state brings up the record prompt @Test public void testNotActive() { when(mController.isStarting()).thenReturn(false); when(mController.isRecording()).thenReturn(false); mTile.refreshState(); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_INACTIVE, mTile.getState().state); assertTrue(mTile.getState().secondaryLabel.toString().equals( mContext.getString(R.string.quick_settings_screen_record_start))); mTile.handleClick(); verify(mController, times(1)).launchRecordPrompt(); } // Test that the tile is active and labeled correctly when the controller is starting @Test public void testIsStarting() { when(mController.isStarting()).thenReturn(true); when(mController.isRecording()).thenReturn(false); mTile.refreshState(); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_ACTIVE, mTile.getState().state); assertTrue(mTile.getState().secondaryLabel.toString().endsWith("...")); } // Test that the tile cancels countdown if it is clicked when the controller is starting @Test public void testCancelRecording() { when(mController.isStarting()).thenReturn(true); when(mController.isRecording()).thenReturn(false); mTile.handleClick(); verify(mController, times(1)).cancelCountdown(); } // Test that the tile is active and labeled correctly when the controller is recording @Test public void testIsRecording() { when(mController.isStarting()).thenReturn(false); when(mController.isRecording()).thenReturn(true); mTile.refreshState(); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_ACTIVE, mTile.getState().state); assertTrue(mTile.getState().secondaryLabel.toString().equals( mContext.getString(R.string.quick_settings_screen_record_stop))); } // Test that the tile stops the recording if it is clicked when the controller is recording @Test public void testStopRecording() { when(mController.isStarting()).thenReturn(false); when(mController.isRecording()).thenReturn(true); mTile.handleClick(); verify(mController, times(1)).stopRecording(); } }