Merge "Add screen recording QS tile"
This commit is contained in:
committed by
Android (Google) Code Review
commit
0b56225ecf
@@ -308,7 +308,8 @@
|
||||
</receiver>
|
||||
|
||||
<activity android:name=".screenrecord.ScreenRecordDialog"
|
||||
android:theme="@style/ScreenRecord" />
|
||||
android:theme="@style/ScreenRecord"
|
||||
android:excludeFromRecents="true" />
|
||||
<service android:name=".screenrecord.RecordingService" />
|
||||
|
||||
<receiver android:name=".SysuiRestartReceiver"
|
||||
|
||||
9
packages/SystemUI/res/drawable/ic_qs_screenrecord.xml
Normal file
9
packages/SystemUI/res/drawable/ic_qs_screenrecord.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,10.48L18,6c0,-1.1 -0.9,-2 -2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-4.48l4,3.98v-11l-4,3.98zM16,9.69L16,18L4,18L4,6h12v3.69z"/>
|
||||
</vector>
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="top"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/global_actions_padding"
|
||||
android:background="@drawable/rounded_bg_full">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_mic"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/screenrecord_mic_label"/>
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_taps"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/screenrecord_taps_label"/>
|
||||
<Button
|
||||
android:id="@+id/record_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:text="@string/screenrecord_start_label"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
<!-- The default tiles to display in QuickSettings -->
|
||||
<string name="quick_settings_tiles_default" translatable="false">
|
||||
wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast
|
||||
wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast,screenrecord
|
||||
</string>
|
||||
|
||||
<!-- The minimum number of tiles to display in QuickSettings -->
|
||||
@@ -117,7 +117,7 @@
|
||||
|
||||
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
|
||||
<string name="quick_settings_tiles_stock" translatable="false">
|
||||
wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,controls
|
||||
wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,controls,screenrecord
|
||||
</string>
|
||||
|
||||
<!-- The tiles to display in QuickSettings -->
|
||||
|
||||
@@ -928,6 +928,13 @@
|
||||
<!-- QuickSettings: NFC (on) [CHAR LIMIT=NONE] -->
|
||||
<string name="quick_settings_nfc_on">NFC is enabled</string>
|
||||
|
||||
<!-- QuickSettings: Screen record tile [CHAR LIMIT=NONE] -->
|
||||
<string name="quick_settings_screen_record_label">Screen Record</string>
|
||||
<!-- QuickSettings: Text to prompt the user to begin a new recording [CHAR LIMIT=20] -->
|
||||
<string name="quick_settings_screen_record_start">Start</string>
|
||||
<!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] -->
|
||||
<string name="quick_settings_screen_record_stop">Stop</string>
|
||||
|
||||
<!-- Recents: Text that shows above the navigation bar after launching a few apps. [CHAR LIMIT=NONE] -->
|
||||
<string name="recents_swipe_up_onboarding">Swipe up to switch apps</string>
|
||||
<!-- Recents: Text that shows above the navigation bar after launching several apps. [CHAR LIMIT=NONE] -->
|
||||
|
||||
@@ -56,6 +56,7 @@ import com.android.systemui.power.EnhancedEstimates;
|
||||
import com.android.systemui.power.PowerUI;
|
||||
import com.android.systemui.recents.OverviewProxyService;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.screenrecord.RecordingController;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
|
||||
@@ -323,6 +324,7 @@ public class Dependency {
|
||||
@Inject Lazy<DisplayWindowController> mDisplayWindowController;
|
||||
@Inject Lazy<SystemWindows> mSystemWindows;
|
||||
@Inject Lazy<DisplayImeController> mDisplayImeController;
|
||||
@Inject Lazy<RecordingController> mRecordingController;
|
||||
|
||||
@Inject
|
||||
public Dependency() {
|
||||
@@ -519,6 +521,8 @@ public class Dependency {
|
||||
// Dependency problem.
|
||||
mProviders.put(AutoHideController.class, mAutoHideController::get);
|
||||
|
||||
mProviders.put(RecordingController.class, mRecordingController::get);
|
||||
|
||||
sDependency = this;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.app.Activity;
|
||||
|
||||
import com.android.systemui.ForegroundServicesDialog;
|
||||
import com.android.systemui.keyguard.WorkLockActivity;
|
||||
import com.android.systemui.screenrecord.ScreenRecordDialog;
|
||||
import com.android.systemui.settings.BrightnessDialog;
|
||||
import com.android.systemui.tuner.TunerActivity;
|
||||
|
||||
@@ -29,7 +30,7 @@ import dagger.multibindings.ClassKey;
|
||||
import dagger.multibindings.IntoMap;
|
||||
|
||||
/**
|
||||
* Services and Activities that are injectable should go here.
|
||||
* Activities that are injectable should go here.
|
||||
*/
|
||||
@Module
|
||||
public abstract class DefaultActivityBinder {
|
||||
@@ -56,4 +57,10 @@ public abstract class DefaultActivityBinder {
|
||||
@IntoMap
|
||||
@ClassKey(BrightnessDialog.class)
|
||||
public abstract Activity bindBrightnessDialog(BrightnessDialog activity);
|
||||
|
||||
/** Inject into ScreenRecordDialog */
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(ScreenRecordDialog.class)
|
||||
public abstract Activity bindScreenRecordDialog(ScreenRecordDialog activity);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.android.systemui.ImageWallpaper;
|
||||
import com.android.systemui.SystemUIService;
|
||||
import com.android.systemui.doze.DozeService;
|
||||
import com.android.systemui.keyguard.KeyguardService;
|
||||
import com.android.systemui.screenrecord.RecordingService;
|
||||
import com.android.systemui.screenshot.TakeScreenshotService;
|
||||
|
||||
import dagger.Binds;
|
||||
@@ -63,4 +64,10 @@ public abstract class DefaultServiceBinder {
|
||||
@IntoMap
|
||||
@ClassKey(TakeScreenshotService.class)
|
||||
public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
|
||||
|
||||
/** Inject into RecordingService */
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(RecordingService.class)
|
||||
public abstract Service bindRecordingService(RecordingService service);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.android.systemui.qs.tiles.LocationTile;
|
||||
import com.android.systemui.qs.tiles.NfcTile;
|
||||
import com.android.systemui.qs.tiles.NightDisplayTile;
|
||||
import com.android.systemui.qs.tiles.RotationLockTile;
|
||||
import com.android.systemui.qs.tiles.ScreenRecordTile;
|
||||
import com.android.systemui.qs.tiles.UiModeNightTile;
|
||||
import com.android.systemui.qs.tiles.UserTile;
|
||||
import com.android.systemui.qs.tiles.WifiTile;
|
||||
@@ -77,6 +78,7 @@ public class QSFactoryImpl implements QSFactory {
|
||||
private final Provider<NfcTile> mNfcTileProvider;
|
||||
private final Provider<GarbageMonitor.MemoryTile> mMemoryTileProvider;
|
||||
private final Provider<UiModeNightTile> mUiModeNightTileProvider;
|
||||
private final Provider<ScreenRecordTile> mScreenRecordTileProvider;
|
||||
|
||||
private QSTileHost mHost;
|
||||
|
||||
@@ -100,7 +102,8 @@ public class QSFactoryImpl implements QSFactory {
|
||||
Provider<NightDisplayTile> nightDisplayTileProvider,
|
||||
Provider<NfcTile> nfcTileProvider,
|
||||
Provider<GarbageMonitor.MemoryTile> memoryTileProvider,
|
||||
Provider<UiModeNightTile> uiModeNightTileProvider) {
|
||||
Provider<UiModeNightTile> uiModeNightTileProvider,
|
||||
Provider<ScreenRecordTile> screenRecordTileProvider) {
|
||||
mWifiTileProvider = wifiTileProvider;
|
||||
mBluetoothTileProvider = bluetoothTileProvider;
|
||||
mControlsTileProvider = controlsTileProvider;
|
||||
@@ -121,6 +124,7 @@ public class QSFactoryImpl implements QSFactory {
|
||||
mNfcTileProvider = nfcTileProvider;
|
||||
mMemoryTileProvider = memoryTileProvider;
|
||||
mUiModeNightTileProvider = uiModeNightTileProvider;
|
||||
mScreenRecordTileProvider = screenRecordTileProvider;
|
||||
}
|
||||
|
||||
public void setHost(QSTileHost host) {
|
||||
@@ -179,6 +183,8 @@ public class QSFactoryImpl implements QSFactory {
|
||||
return mNfcTileProvider.get();
|
||||
case "dark":
|
||||
return mUiModeNightTileProvider.get();
|
||||
case "screenrecord":
|
||||
return mScreenRecordTileProvider.get();
|
||||
}
|
||||
|
||||
// Custom tiles
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 android.content.Intent;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.qs.QSTile;
|
||||
import com.android.systemui.qs.QSHost;
|
||||
import com.android.systemui.qs.tileimpl.QSTileImpl;
|
||||
import com.android.systemui.screenrecord.RecordingController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Quick settings tile for screen recording
|
||||
*/
|
||||
public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> {
|
||||
private static final String TAG = "ScreenRecordTile";
|
||||
private RecordingController mController;
|
||||
private long mMillisUntilFinished = 0;
|
||||
|
||||
@Inject
|
||||
public ScreenRecordTile(QSHost host, RecordingController controller) {
|
||||
super(host);
|
||||
mController = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClick() {
|
||||
if (mController.isStarting()) {
|
||||
cancelCountdown();
|
||||
} else if (mController.isRecording()) {
|
||||
stopRecording();
|
||||
} else {
|
||||
startCountdown();
|
||||
}
|
||||
refreshState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh tile state
|
||||
* @param millisUntilFinished Time until countdown completes, or 0 if not counting down
|
||||
*/
|
||||
public void refreshState(long millisUntilFinished) {
|
||||
mMillisUntilFinished = millisUntilFinished;
|
||||
refreshState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUpdateState(BooleanState state, Object arg) {
|
||||
boolean isStarting = mController.isStarting();
|
||||
boolean isRecording = mController.isRecording();
|
||||
|
||||
state.label = mContext.getString(R.string.quick_settings_screen_record_label);
|
||||
state.value = isRecording || isStarting;
|
||||
state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
|
||||
state.handlesLongClick = false;
|
||||
|
||||
if (isRecording) {
|
||||
state.icon = ResourceIcon.get(R.drawable.ic_qs_screenrecord);
|
||||
state.secondaryLabel = mContext.getString(R.string.quick_settings_screen_record_stop);
|
||||
} else if (isStarting) {
|
||||
// round, since the timer isn't exact
|
||||
int countdown = (int) Math.floorDiv(mMillisUntilFinished + 500, 1000);
|
||||
// TODO update icon
|
||||
state.icon = ResourceIcon.get(R.drawable.ic_qs_screenrecord);
|
||||
state.secondaryLabel = String.format("%d...", countdown);
|
||||
} else {
|
||||
// TODO update icon
|
||||
state.icon = ResourceIcon.get(R.drawable.ic_qs_screenrecord);
|
||||
state.secondaryLabel = mContext.getString(R.string.quick_settings_screen_record_start);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getLongClickIntent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleSetListening(boolean listening) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTileLabel() {
|
||||
return mContext.getString(R.string.quick_settings_screen_record_label);
|
||||
}
|
||||
|
||||
private void startCountdown() {
|
||||
Log.d(TAG, "Starting countdown");
|
||||
// Close QS, otherwise the permission dialog appears beneath it
|
||||
getHost().collapsePanels();
|
||||
mController.launchRecordPrompt(this);
|
||||
}
|
||||
|
||||
private void cancelCountdown() {
|
||||
Log.d(TAG, "Cancelling countdown");
|
||||
mController.cancelCountdown();
|
||||
}
|
||||
|
||||
private void stopRecording() {
|
||||
Log.d(TAG, "Stopping recording from tile");
|
||||
mController.stopRecording();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.screenrecord;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.CountDownTimer;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.qs.tiles.ScreenRecordTile;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Helper class to initiate a screen recording
|
||||
*/
|
||||
@Singleton
|
||||
public class RecordingController {
|
||||
private static final String TAG = "RecordingController";
|
||||
private static final String SYSUI_PACKAGE = "com.android.systemui";
|
||||
private static final String SYSUI_SCREENRECORD_LAUNCHER =
|
||||
"com.android.systemui.screenrecord.ScreenRecordDialog";
|
||||
|
||||
private final Context mContext;
|
||||
private boolean mIsStarting;
|
||||
private boolean mIsRecording;
|
||||
private ScreenRecordTile mTileToUpdate;
|
||||
private PendingIntent mStopIntent;
|
||||
private CountDownTimer mCountDownTimer = null;
|
||||
|
||||
/**
|
||||
* Create a new RecordingController
|
||||
* @param context Context for the controller
|
||||
*/
|
||||
@Inject
|
||||
public RecordingController(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show dialog of screen recording options to user.
|
||||
*/
|
||||
public void launchRecordPrompt(ScreenRecordTile tileToUpdate) {
|
||||
final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE,
|
||||
SYSUI_SCREENRECORD_LAUNCHER);
|
||||
final Intent intent = new Intent();
|
||||
intent.setComponent(launcherComponent);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra("com.android.systemui.screenrecord.EXTRA_SETTINGS_ONLY", true);
|
||||
mContext.startActivity(intent);
|
||||
|
||||
mTileToUpdate = tileToUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start counting down in preparation to start a recording
|
||||
* @param ms Time in ms to count down
|
||||
* @param startIntent Intent to start a recording
|
||||
* @param stopIntent Intent to stop a recording
|
||||
*/
|
||||
public void startCountdown(long ms, PendingIntent startIntent, PendingIntent stopIntent) {
|
||||
mIsStarting = true;
|
||||
mStopIntent = stopIntent;
|
||||
|
||||
mCountDownTimer = new CountDownTimer(ms, 1000) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
refreshTile(millisUntilFinished);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
mIsStarting = false;
|
||||
mIsRecording = true;
|
||||
refreshTile();
|
||||
try {
|
||||
startIntent.send();
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.e(TAG, "Pending intent was cancelled: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mCountDownTimer.start();
|
||||
}
|
||||
|
||||
private void refreshTile() {
|
||||
refreshTile(0);
|
||||
}
|
||||
|
||||
private void refreshTile(long millisUntilFinished) {
|
||||
if (mTileToUpdate != null) {
|
||||
mTileToUpdate.refreshState(millisUntilFinished);
|
||||
} else {
|
||||
Log.e(TAG, "No tile to refresh");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a countdown in progress. This will not stop the recording if it already started.
|
||||
*/
|
||||
public void cancelCountdown() {
|
||||
if (mCountDownTimer != null) {
|
||||
mCountDownTimer.cancel();
|
||||
} else {
|
||||
Log.e(TAG, "Timer was null");
|
||||
}
|
||||
mIsStarting = false;
|
||||
refreshTile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the recording is currently counting down to begin
|
||||
* @return
|
||||
*/
|
||||
public boolean isStarting() {
|
||||
return mIsStarting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the recording is ongoing
|
||||
* @return
|
||||
*/
|
||||
public boolean isRecording() {
|
||||
return mIsRecording;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the recording
|
||||
*/
|
||||
public void stopRecording() {
|
||||
updateState(false);
|
||||
try {
|
||||
mStopIntent.send();
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.e(TAG, "Error stopping: " + e.getMessage());
|
||||
}
|
||||
refreshTile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current status
|
||||
* @param isRecording
|
||||
*/
|
||||
public void updateState(boolean isRecording) {
|
||||
mIsRecording = isRecording;
|
||||
refreshTile();
|
||||
}
|
||||
}
|
||||
@@ -53,10 +53,14 @@ import java.nio.file.Files;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* A service which records the device screen and optionally microphone input.
|
||||
*/
|
||||
public class RecordingService extends Service {
|
||||
public static final int REQUEST_CODE = 2;
|
||||
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
private static final String TAG = "RecordingService";
|
||||
private static final String CHANNEL_ID = "screen_record";
|
||||
@@ -65,7 +69,6 @@ public class RecordingService extends Service {
|
||||
private static final String EXTRA_PATH = "extra_path";
|
||||
private static final String EXTRA_USE_AUDIO = "extra_useAudio";
|
||||
private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
|
||||
private static final int REQUEST_CODE = 2;
|
||||
|
||||
private static final String ACTION_START = "com.android.systemui.screenrecord.START";
|
||||
private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
|
||||
@@ -81,6 +84,7 @@ public class RecordingService extends Service {
|
||||
private static final int AUDIO_BIT_RATE = 16;
|
||||
private static final int AUDIO_SAMPLE_RATE = 44100;
|
||||
|
||||
private final RecordingController mController;
|
||||
private MediaProjectionManager mMediaProjectionManager;
|
||||
private MediaProjection mMediaProjection;
|
||||
private Surface mInputSurface;
|
||||
@@ -92,6 +96,11 @@ public class RecordingService extends Service {
|
||||
private boolean mShowTaps;
|
||||
private File mTempFile;
|
||||
|
||||
@Inject
|
||||
public RecordingService(RecordingController controller) {
|
||||
mController = controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an intent to start the recording service.
|
||||
*
|
||||
@@ -272,6 +281,7 @@ public class RecordingService extends Service {
|
||||
null);
|
||||
|
||||
mMediaRecorder.start();
|
||||
mController.updateState(true);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error starting screen recording: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
@@ -285,7 +295,7 @@ public class RecordingService extends Service {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
getString(R.string.screenrecord_name),
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
channel.setDescription(getString(R.string.screenrecord_channel_description));
|
||||
channel.enableVibration(true);
|
||||
NotificationManager notificationManager =
|
||||
@@ -399,6 +409,7 @@ public class RecordingService extends Service {
|
||||
mInputSurface.release();
|
||||
mVirtualDisplay.release();
|
||||
stopSelf();
|
||||
mController.updateState(false);
|
||||
}
|
||||
|
||||
private void saveRecording(NotificationManager notificationManager) {
|
||||
@@ -439,7 +450,12 @@ public class RecordingService extends Service {
|
||||
Settings.System.SHOW_TOUCHES, value);
|
||||
}
|
||||
|
||||
private static Intent getStopIntent(Context context) {
|
||||
/**
|
||||
* Get an intent to stop the recording service.
|
||||
* @param context Context from the requesting activity
|
||||
* @return
|
||||
*/
|
||||
public static Intent getStopIntent(Context context) {
|
||||
return new Intent(context, RecordingService.class).setAction(ACTION_STOP);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,55 +18,41 @@ package com.android.systemui.screenrecord;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.projection.MediaProjectionManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Activity to select screen recording options
|
||||
*/
|
||||
public class ScreenRecordDialog extends Activity {
|
||||
private static final String TAG = "ScreenRecord";
|
||||
private static final int REQUEST_CODE_VIDEO_ONLY = 200;
|
||||
private static final int REQUEST_CODE_VIDEO_TAPS = 201;
|
||||
private static final int REQUEST_CODE_PERMISSIONS = 299;
|
||||
private static final int REQUEST_CODE_VIDEO_AUDIO = 300;
|
||||
private static final int REQUEST_CODE_VIDEO_AUDIO_TAPS = 301;
|
||||
private static final int REQUEST_CODE_PERMISSIONS_AUDIO = 399;
|
||||
private boolean mUseAudio;
|
||||
private boolean mShowTaps;
|
||||
private static final long DELAY_MS = 3000;
|
||||
|
||||
private final RecordingController mController;
|
||||
|
||||
@Inject
|
||||
public ScreenRecordDialog(RecordingController controller) {
|
||||
mController = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.screen_record_dialog);
|
||||
|
||||
final CheckBox micCheckBox = findViewById(R.id.checkbox_mic);
|
||||
final CheckBox tapsCheckBox = findViewById(R.id.checkbox_taps);
|
||||
|
||||
final Button recordButton = findViewById(R.id.record_button);
|
||||
recordButton.setOnClickListener(v -> {
|
||||
mUseAudio = micCheckBox.isChecked();
|
||||
mShowTaps = tapsCheckBox.isChecked();
|
||||
Log.d(TAG, "Record button clicked: audio " + mUseAudio + ", taps " + mShowTaps);
|
||||
|
||||
if (mUseAudio && checkSelfPermission(Manifest.permission.RECORD_AUDIO)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Requesting permission for audio");
|
||||
requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
|
||||
REQUEST_CODE_PERMISSIONS_AUDIO);
|
||||
} else {
|
||||
requestScreenCapture();
|
||||
}
|
||||
});
|
||||
requestScreenCapture();
|
||||
}
|
||||
|
||||
private void requestScreenCapture() {
|
||||
@@ -74,18 +60,23 @@ public class ScreenRecordDialog extends Activity {
|
||||
Context.MEDIA_PROJECTION_SERVICE);
|
||||
Intent permissionIntent = mediaProjectionManager.createScreenCaptureIntent();
|
||||
|
||||
if (mUseAudio) {
|
||||
// TODO get saved settings
|
||||
boolean useAudio = false;
|
||||
boolean showTaps = false;
|
||||
if (useAudio) {
|
||||
startActivityForResult(permissionIntent,
|
||||
mShowTaps ? REQUEST_CODE_VIDEO_AUDIO_TAPS : REQUEST_CODE_VIDEO_AUDIO);
|
||||
showTaps ? REQUEST_CODE_VIDEO_AUDIO_TAPS : REQUEST_CODE_VIDEO_AUDIO);
|
||||
} else {
|
||||
startActivityForResult(permissionIntent,
|
||||
mShowTaps ? REQUEST_CODE_VIDEO_TAPS : REQUEST_CODE_VIDEO_ONLY);
|
||||
showTaps ? REQUEST_CODE_VIDEO_TAPS : REQUEST_CODE_VIDEO_ONLY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
mShowTaps = (requestCode == REQUEST_CODE_VIDEO_TAPS
|
||||
boolean showTaps = (requestCode == REQUEST_CODE_VIDEO_TAPS
|
||||
|| requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS);
|
||||
boolean useAudio = (requestCode == REQUEST_CODE_VIDEO_AUDIO
|
||||
|| requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS);
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_VIDEO_TAPS:
|
||||
@@ -93,11 +84,17 @@ public class ScreenRecordDialog extends Activity {
|
||||
case REQUEST_CODE_VIDEO_ONLY:
|
||||
case REQUEST_CODE_VIDEO_AUDIO:
|
||||
if (resultCode == RESULT_OK) {
|
||||
mUseAudio = (requestCode == REQUEST_CODE_VIDEO_AUDIO
|
||||
|| requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS);
|
||||
startForegroundService(
|
||||
RecordingService.getStartIntent(this, resultCode, data, mUseAudio,
|
||||
mShowTaps));
|
||||
PendingIntent startIntent = PendingIntent.getForegroundService(
|
||||
this, RecordingService.REQUEST_CODE, RecordingService.getStartIntent(
|
||||
ScreenRecordDialog.this, resultCode, data, useAudio,
|
||||
showTaps),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
PendingIntent stopIntent = PendingIntent.getService(
|
||||
this, RecordingService.REQUEST_CODE,
|
||||
RecordingService.getStopIntent(this),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
mController.startCountdown(DELAY_MS, startIntent, stopIntent);
|
||||
} else {
|
||||
Toast.makeText(this,
|
||||
getResources().getString(R.string.screenrecord_permission_error),
|
||||
|
||||
Reference in New Issue
Block a user