1.简介
简单学习下系统的屏幕录制功能,就是下拉状态栏的screenRecord,如下图
2.ScreenRecordTile.java
2.1.handleClick
private final RecordingController mController;
protected void handleClick(@Nullable View view) {
if (mController.isStarting()) {
//启动中,就是倒计时还没结束的时候,取消倒计时
cancelCountdown();//见3.3
} else if (mController.isRecording()) {
//已经在录制中,停止录制,件3.4
stopRecording();
} else {
//显示弹框,见2.2
mUiHandler.post(() -> showPrompt(view));
}
//刷新tile的状态
refreshState();
}
2.2.showPrompt
private void showPrompt(@Nullable View view) {
//是否需要显示动画,锁屏界面不显示
boolean shouldAnimateFromView = view != null && !mKeyguardStateController.isShowing();
//开始录制的点击事件回调
Runnable onStartRecordingClicked = () -> {
//
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
//收起下拉面板
getHost().collapsePanels();
};
//创建弹框,见3.1
Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);
ActivityStarter.OnDismissAction dismissAction = () -> {
//显示dialog
//非锁屏界面有动画,
if (shouldAnimateFromView) {
mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG));
} else {
//锁屏界面没有动画
dialog.show();
}
return false;
};
//解锁的条件下执行上边的dismiss操作
mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false /* requiresShadeOpen */,
true /* afterKeyguardDone */);
}
3.RecordingController.java
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
protected static final String EXTRA_STATE = "extra_state";
3.1.createScreenRecordDialog
创建一个录屏对话框,我们这里看第二个
public Dialog createScreenRecordDialog(Context context, FeatureFlags flags,
DialogLaunchAnimator dialogLaunchAnimator,
ActivityStarter activityStarter,
@Nullable Runnable onStartRecordingClicked) {
//部分屏幕共享可用的话,默认是false吧
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
? new ScreenRecordPermissionDialog(context, this, activityStarter,
dialogLaunchAnimator, mUserContextProvider, onStartRecordingClicked)
: new ScreenRecordDialog(context, this, activityStarter,
mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
}
3.2.startCountdown
开启一个倒计时,4.1.2里用到,就是点击record按钮以后会走这里
public void startCountdown(long ms, long interval, PendingIntent startIntent,
PendingIntent stopIntent) {
mIsStarting = true;
mStopIntent = stopIntent;
mCountDownTimer = new CountDownTimer(ms, interval) {
@Override
public void onTick(long millisUntilFinished) {
for (RecordingStateChangeCallback cb : mListeners) {
cb.onCountdown(millisUntilFinished);
}
}
@Override
public void onFinish() {
//倒计时结束,开始录制
mIsStarting = false;
mIsRecording = true;
for (RecordingStateChangeCallback cb : mListeners) {
cb.onCountdownEnd();
}
try {
//start意图执行
startIntent.send();
//添加用户改变监听
mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
//注册更新状态广播
IntentFilter stateFilter = new IntentFilter(INTENT_UPDATE_STATE);
mBroadcastDispatcher.registerReceiver(mStateChangeReceiver, stateFilter, null,
UserHandle.ALL);
}
}
};
mCountDownTimer.start();
}
>1.mStateChangeReceiver
录屏状态监听
protected final BroadcastReceiver mStateChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && INTENT_UPDATE_STATE.equals(intent.getAction())) {
if (intent.hasExtra(EXTRA_STATE)) {
boolean state = intent.getBooleanExtra(EXTRA_STATE, false);
//见补充2,更新录屏状态
updateState(state);
}
}
}
};
>2.updateState
public synchronized void updateState(boolean isRecording) {
if (!isRecording && mIsRecording) {
//停止录制屏幕的时候,移除监听
mUserTracker.removeCallback(mUserChangedCallback);
mBroadcastDispatcher.unregisterReceiver(mStateChangeReceiver);
}
mIsRecording = isRecording;
for (RecordingStateChangeCallback cb : mListeners) {
//回调更新录制状态
if (isRecording) {
cb.onRecordingStart();
} else {
cb.onRecordingEnd();
}
}
}
3.3.cancelCountdown
取消倒计时
public void cancelCountdown() {
if (mCountDownTimer != null) {
mCountDownTimer.cancel();
}
mIsStarting = false;
for (RecordingStateChangeCallback cb : mListeners) {
cb.onCountdownEnd();
}
}
3.4.stopRecording
public void stopRecording() {
try {
if (mStopIntent != null) {
//执行stop意图,数据是3.2赋值的
mStopIntent.send();
} else {
Log.e(TAG, "Stop intent was null");
}
//录制状态设置为false
updateState(false);
}
}
4.ScreenRecordDialog
public class ScreenRecordDialog extends SystemUIDialog {
弹框效果见4.1.1
4.1.onCreate
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
window.setGravity(Gravity.CENTER);
setTitle(R.string.screenrecord_name);
//布局见补充1
setContentView(R.layout.screen_record_dialog);
//点击取消按钮,隐藏对话框即可
TextView cancelBtn = findViewById(R.id.button_cancel);
cancelBtn.setOnClickListener(v -> dismiss());
//开始按钮点击
TextView startBtn = findViewById(R.id.button_start);
startBtn.setOnClickListener(v -> {
if (mOnStartRecordingClicked != null) {
//回调,构造方法里传递的,具体见2.2
mOnStartRecordingClicked.run();
}
//见补充2,开始请求录屏
requestScreenCapture(/* captureTarget= */ null);
dismiss();
});
mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
mOptions = findViewById(R.id.screen_recording_options);
//音频类型数据
ArrayAdapter a = new ScreenRecordingAdapter(getContext().getApplicationContext(),
android.R.layout.simple_spinner_dropdown_item,
MODES);
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mOptions.setAdapter(a);
mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
//选择了任何一种音频,开关自动打开
mAudioSwitch.setChecked(true);
});
}
>1.screen_record_dialog.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Scrollview is necessary to fit everything in landscape layout -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dialog_side_padding"
android:paddingEnd="@dimen/dialog_side_padding"
android:paddingTop="@dimen/dialog_top_padding"
android:paddingBottom="@dimen/dialog_bottom_padding"
android:orientation="vertical">
<!-- Header,图上第一部分:图片+标题+描述 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="@dimen/screenrecord_logo_size"
android:layout_height="@dimen/screenrecord_logo_size"
android:src="@drawable/ic_screenrecord"
android:tint="@color/screenrecord_icon_color"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:text="@string/screenrecord_start_label"
android:layout_marginTop="22dp"
android:layout_marginBottom="15dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/screenrecord_description"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorSecondary"
android:gravity="center"
android:layout_marginBottom="20dp"/>
<!-- Options 图上第二部分,音频相关-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="@dimen/screenrecord_option_icon_size"
android:layout_height="@dimen/screenrecord_option_icon_size"
android:src="@drawable/ic_mic_26dp"
android:tint="?android:attr/textColorSecondary"
android:layout_gravity="center"
android:layout_weight="0"
android:layout_marginRight="@dimen/screenrecord_option_padding"/>
<Spinner
android:id="@+id/screen_recording_options"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:layout_weight="1"
android:popupBackground="@drawable/screenrecord_spinner_background"
android:dropDownWidth="274dp"
android:prompt="@string/screenrecord_audio_label"/>
<Switch
android:layout_width="wrap_content"
android:minWidth="48dp"
android:layout_height="48dp"
android:layout_weight="0"
android:layout_gravity="end"
android:contentDescription="@string/screenrecord_audio_label"
android:id="@+id/screenrecord_audio_switch"
style="@style/ScreenRecord.Switch"/>
</LinearLayout>
<!--图上第二部分,屏幕上是否显示触摸效果-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="@dimen/screenrecord_option_padding">
<ImageView
android:layout_width="@dimen/screenrecord_option_icon_size"
android:layout_height="@dimen/screenrecord_option_icon_size"
android:layout_weight="0"
android:src="@drawable/ic_touch"
android:tint="?android:attr/textColorSecondary"
android:layout_gravity="center"
android:layout_marginRight="@dimen/screenrecord_option_padding"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:layout_weight="1"
android:layout_gravity="fill_vertical"
android:gravity="center_vertical"
android:text="@string/screenrecord_taps_label"
android:textAppearance="?android:attr/textAppearanceMedium"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:textColor="?android:attr/textColorPrimary"
android:importantForAccessibility="no"/>
<Switch
android:layout_width="wrap_content"
android:minWidth="48dp"
android:layout_height="48dp"
android:layout_weight="0"
android:id="@+id/screenrecord_taps_switch"
android:contentDescription="@string/screenrecord_taps_label"
style="@style/ScreenRecord.Switch"/>
</LinearLayout>
</LinearLayout>
<!-- Buttons ,取消以及开始按钮-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="36dp">
<TextView
android:id="@+id/button_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="start"
android:text="@string/cancel"
style="@style/Widget.Dialog.Button.BorderButton" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<TextView
android:id="@+id/button_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="end"
android:text="@string/screenrecord_start"
style="@style/Widget.Dialog.Button" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
>2.requestScreenCapture
请求录屏,交给小节3处理
private static final long DELAY_MS = 3000;//录屏开始倒计时
private static final long INTERVAL_MS = 1000;
private void requestScreenCapture(@Nullable MediaProjectionCaptureTarget captureTarget) {
Context userContext = mUserContextProvider.getUserContext();
//是否显示触摸效果
boolean showTaps = mTapsSwitch.isChecked();
//音频模式
ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
: NONE;
//屏幕录制用到的intent
PendingIntent startIntent = PendingIntent.getForegroundService(userContext,
RecordingService.REQUEST_CODE,
RecordingService.getStartIntent(
userContext, Activity.RESULT_OK,
audioMode.ordinal(), showTaps, captureTarget),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
//停止屏幕录制用到的intent
PendingIntent stopIntent = PendingIntent.getService(userContext,
RecordingService.REQUEST_CODE,
RecordingService.getStopIntent(userContext),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
//见3.2,开启倒计时
mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent);
}
5.RecordingService.java
5.1.getStartIntent
开始录屏用到的intent
public static Intent getStartIntent(Context context, int resultCode,
int audioSource, boolean showTaps,
@Nullable MediaProjectionCaptureTarget captureTarget) {
return new Intent(context, RecordingService.class)
.setAction(ACTION_START)
.putExtra(EXTRA_RESULT_CODE, resultCode)
.putExtra(EXTRA_AUDIO_SOURCE, audioSource)
.putExtra(EXTRA_SHOW_TAPS, showTaps)
.putExtra(EXTRA_CAPTURE_TARGET, captureTarget);
}
5.2.getStopIntent
停止录屏用到的intent
public static Intent getStopIntent(Context context) {
return new Intent(context, RecordingService.class)
.setAction(ACTION_STOP)
.putExtra(Intent.EXTRA_USER_HANDLE, context.getUserId());
}
5.3.onStartCommand
这里就是录屏服务里处理上边的start,stop等action的地方
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
return Service.START_NOT_STICKY;
}
String action = intent.getAction();
Log.d(TAG, "onStartCommand " + action);
int currentUserId = mUserContextTracker.getUserContext().getUserId();
UserHandle currentUser = new UserHandle(currentUserId);
switch (action) {
case ACTION_START:
//音频源
mAudioSource = ScreenRecordingAudioSource
.values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)];
mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);
MediaProjectionCaptureTarget captureTarget =
intent.getParcelableExtra(EXTRA_CAPTURE_TARGET,
MediaProjectionCaptureTarget.class);
//先获取原本设置里的showTap,开发者模式里
mOriginalShowTaps = Settings.System.getInt(
getApplicationContext().getContentResolver(),
Settings.System.SHOW_TOUCHES, 0) != 0;
//再根据传递的参数修改上边的Settings.System.SHOW_TOUCHES
setTapsVisible(mShowTaps);//见补充1
//录屏工具类
mRecorder = new ScreenMediaRecorder(
mUserContextTracker.getUserContext(),
mMainHandler,
currentUserId,
mAudioSource,
captureTarget,
this
);
//补充2
if (startRecording()) {
//录屏启动成功,更新状态为true
updateState(true);//见补充3
//创建通知,见5.4
createRecordingNotification();
} else {
//启动失败,更新状态为false
updateState(false);
//创建error通知,见5.5
createErrorNotification();
//停止服务
stopForeground(true);
stopSelf();
return Service.START_NOT_STICKY;
}
break;
case ACTION_STOP_NOTIF:
case ACTION_STOP://停止录屏
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_ID_NOT_SPECIFIED);
//见5.6
stopService(userId);
break;
case ACTION_SHARE://分享录制的屏幕视频
Uri shareUri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
//创建分享意图
Intent shareIntent = new Intent(Intent.ACTION_SEND)
.setType("video/mp4")
.putExtra(Intent.EXTRA_STREAM, shareUri);
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
String shareLabel = getResources().getString(R.string.screenrecord_share_label);
启动分享页面
startActivity(Intent.createChooser(shareIntent, shareLabel)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
// Remove notification
mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
return false;
}, false, false);
// Close quick shade
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
break;
}
return Service.START_STICKY;
}
>1.setTapsVisible
private void setTapsVisible(boolean turnOn) {
int value = turnOn ? 1 : 0;
Settings.System.putInt(getContentResolver(), Settings.System.SHOW_TOUCHES, value);
}
>2.startRecording
private boolean startRecording() {
try {
getRecorder().start();
//录屏启动正常
return true;
} catch (IOException | RemoteException | RuntimeException e) {
showErrorToast(R.string.screenrecord_start_error);
e.printStackTrace();
}
//录屏启动失败
return false;
}
>3.updateState
private void updateState(boolean state) {
int userId = mUserContextTracker.getUserContext().getUserId();
if (userId == UserHandle.USER_SYSTEM) {
//系统用户和controller关联的,直接调用即可,见3.2.2
mController.updateState(state);
} else {
//否则发送广播通知
Intent intent = new Intent(RecordingController.INTENT_UPDATE_STATE);
intent.putExtra(RecordingController.EXTRA_STATE, state);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
sendBroadcast(intent, PERMISSION_SELF);
}
}
5.4.createRecordingNotification
创建录屏中的通知
protected void createRecordingNotification() {
Resources res = getResources();
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
getString(R.string.screenrecord_name),
NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(getString(R.string.screenrecord_channel_description));
channel.enableVibration(true);
mNotificationManager.createNotificationChannel(channel);
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
res.getString(R.string.screenrecord_name));
//有无音频,提示文字不一样
String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE
? res.getString(R.string.screenrecord_ongoing_screen_only)
: res.getString(R.string.screenrecord_ongoing_screen_and_audio);
PendingIntent pendingIntent = PendingIntent.getService(
this,
REQUEST_CODE,
getNotificationIntent(this),//见补充2,停止录屏的action
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
//创建通知里stop按钮的点击事件
Notification.Action stopAction = new Notification.Action.Builder(
Icon.createWithResource(this, R.drawable.ic_android),
getResources().getString(R.string.screenrecord_stop_label),
pendingIntent).build();
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(notificationTitle)
.setUsesChronometer(true)
.setColorized(true)
.setColor(getResources().getColor(R.color.GM2_red_700))
.setOngoing(true)
.setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
.addAction(stopAction)
.addExtras(extras);
//开启前台通知
startForeground(NOTIFICATION_RECORDING_ID, builder.build());
}
>1.效果图
>2.getNotificationIntent
作用和action stop一样
protected static Intent getNotificationIntent(Context context) {
return new Intent(context, RecordingService.class).setAction(ACTION_STOP_NOTIF);
}
5.5.createErrorNotification
录屏错误通知,没见过,就不贴图了。
protected void createErrorNotification() {
Resources res = getResources();
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
getString(R.string.screenrecord_name),
NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(getString(R.string.screenrecord_channel_description));
channel.enableVibration(true);
mNotificationManager.createNotificationChannel(channel);
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
res.getString(R.string.screenrecord_name));
String notificationTitle = res.getString(R.string.screenrecord_start_error);
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(notificationTitle)
.addExtras(extras);
startForeground(NOTIFICATION_RECORDING_ID, builder.build());
}
5.6.stopService
private void stopService(int userId) {
if (userId == USER_ID_NOT_SPECIFIED) {
userId = mUserContextTracker.getUserContext().getUserId();
}
//恢复原本设置里的showTap
setTapsVisible(mOriginalShowTaps);
if (getRecorder() != null) {
try {
//停止录屏
getRecorder().end();
//保存视频
saveRecording(userId);
}
}
//更新录屏状态为false
updateState(false);
//停止服务
stopSelf();
}
>1.saveRecording
private void saveRecording(int userId) {
UserHandle currentUser = new UserHandle(userId);
//见5.7,弹出新的通知,不带stop
mNotificationManager.notifyAsUser(null, NOTIFICATION_PROCESSING_ID,
createProcessingNotification(), currentUser);
mLongExecutor.execute(() -> {
try {
//见5.8,弹出保存成功的通知
Notification notification = createSaveNotification(getRecorder().save());
if (!mController.isRecording()) {
//确保再非录制的状态下再弹
mNotificationManager.notifyAsUser(null, NOTIFICATION_VIEW_ID, notification,
currentUser);
}
} catch (IOException e) {
Log.e(TAG, "Error saving screen recording: " + e.getMessage());
showErrorToast(R.string.screenrecord_delete_error);
} finally {
//取消方法开头的通知
mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser);
}
});
}
5.7.createProcessingNotification
和5.4差不多的通知,就是没有了stop按钮
protected Notification createProcessingNotification() {
Resources res = getApplicationContext().getResources();
String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE
? res.getString(R.string.screenrecord_ongoing_screen_only)
: res.getString(R.string.screenrecord_ongoing_screen_and_audio);
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
res.getString(R.string.screenrecord_name));
Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID)
.setContentTitle(notificationTitle)
.setContentText(
getResources().getString(R.string.screenrecord_background_processing_label))
.setSmallIcon(R.drawable.ic_screenrecord)
.addExtras(extras);
return builder.build();
}
5.8.createSaveNotification
录屏保存成功以后弹的通知
protected Notification createSaveNotification(ScreenMediaRecorder.SavedRecording recording) {
Uri uri = recording.getUri();
//预览intent,可以看到,跳转的是支持视频浏览的页面
Intent viewIntent = new Intent(Intent.ACTION_VIEW)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION)
.setDataAndType(uri, "video/mp4");
//share按钮事件
Notification.Action shareAction = new Notification.Action.Builder(
Icon.createWithResource(this, R.drawable.ic_screenrecord),
getResources().getString(R.string.screenrecord_share_label),
PendingIntent.getService(
this,
REQUEST_CODE,
getShareIntent(this, uri.toString()),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.build();
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
getResources().getString(R.string.screenrecord_name));
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(getResources().getString(R.string.screenrecord_save_title))
.setContentText(getResources().getString(R.string.screenrecord_save_text))
//内容区域点击事件
.setContentIntent(PendingIntent.getActivity(
this,
REQUEST_CODE,
viewIntent,
PendingIntent.FLAG_IMMUTABLE))
.addAction(shareAction)
.setAutoCancel(true)//点击后通知取消
.addExtras(extras);
// 预览图
Bitmap thumbnailBitmap = recording.getThumbnail();
if (thumbnailBitmap != null) {
Notification.BigPictureStyle pictureStyle = new Notification.BigPictureStyle()
.bigPicture(thumbnailBitmap)
.showBigPictureWhenCollapsed(true);
//设置大图预览
builder.setStyle(pictureStyle);
}
return builder.build();
}
>1.效果图
6.PhoneStatusBarPolicy.java
mRecordingController.addCallback(this);
6.1.onRecordingStart
监听录屏状态,在状态栏显示或者隐藏图标
public void onRecordingStart() {
mIconController.setIcon(mSlotScreenRecord,
R.drawable.stat_sys_screen_record,
mResources.getString(R.string.screenrecord_ongoing_screen_only));
mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, true));
}
@Override
public void onRecordingEnd() {
mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
}
7.SystemUIDialog.java
系统ui对话框,小节4的父类。
7.1.构造方法
public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
SystemUIDialogManager dialogManager, SysUiState sysUiState,
BroadcastDispatcher broadcastDispatcher, DialogLaunchAnimator dialogLaunchAnimator) {
super(context, theme);
mContext = context;
applyFlags(this);
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle(getClass().getSimpleName());
getWindow().setAttributes(attrs);
mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this, broadcastDispatcher,
dialogLaunchAnimator) : null;
mDialogManager = dialogManager;
mSysUiState = sysUiState;
}
>1.applyFlags
public static AlertDialog applyFlags(AlertDialog dialog) {
final Window window = dialog.getWindow();
//类型
window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
//flag
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
//去除导致窗口缩进的类型 :statusbar
window.getAttributes().setFitInsetsTypes(
window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
return dialog;
}
7.2.onStart
protected void onStart() {
super.onStart();
if (mDismissReceiver != null) {
mDismissReceiver.register();
}
ViewRootImpl.addConfigCallback(this);
mDialogManager.setShowing(this, true);
mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, true)
.commitUpdate(mContext.getDisplayId());
}
7.3.onStop
protected void onStop() {
super.onStop();
if (mDismissReceiver != null) {
mDismissReceiver.unregister();
}
ViewRootImpl.removeConfigCallback(this);
mDialogManager.setShowing(this, false);
mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
}
8.ScreenMediaRecorder
这个暂时不看,以后有空再说。