| /* |
| * Copyright (C) 2022 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.media.dialog; |
| |
| import android.app.AlertDialog; |
| import android.content.Context; |
| import android.graphics.Bitmap; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.text.method.HideReturnsTransformationMethod; |
| import android.text.method.PasswordTransformationMethod; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewStub; |
| import android.view.WindowManager; |
| import android.widget.Button; |
| import android.widget.EditText; |
| import android.widget.ImageView; |
| import android.widget.TextView; |
| |
| import androidx.core.graphics.drawable.IconCompat; |
| |
| import com.android.settingslib.qrcode.QrCodeGenerator; |
| import com.android.systemui.R; |
| import com.android.systemui.broadcast.BroadcastSender; |
| import com.android.systemui.dagger.SysUISingleton; |
| import com.android.systemui.statusbar.phone.SystemUIDialog; |
| |
| import com.google.zxing.WriterException; |
| |
| /** |
| * Dialog for media output broadcast. |
| */ |
| @SysUISingleton |
| public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { |
| private static final String TAG = "BroadcastDialog"; |
| |
| private ViewStub mBroadcastInfoArea; |
| private ImageView mBroadcastQrCodeView; |
| private ImageView mBroadcastNotify; |
| private TextView mBroadcastName; |
| private ImageView mBroadcastNameEdit; |
| private TextView mBroadcastCode; |
| private ImageView mBroadcastCodeEye; |
| private Boolean mIsPasswordHide = true; |
| private ImageView mBroadcastCodeEdit; |
| private AlertDialog mAlertDialog; |
| private TextView mBroadcastErrorMessage; |
| private int mRetryCount = 0; |
| private String mCurrentBroadcastName; |
| private String mCurrentBroadcastCode; |
| private boolean mIsStopbyUpdateBroadcastCode = false; |
| |
| static final int METADATA_BROADCAST_NAME = 0; |
| static final int METADATA_BROADCAST_CODE = 1; |
| |
| private static final int MAX_BROADCAST_INFO_UPDATE = 3; |
| |
| MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar, |
| BroadcastSender broadcastSender, MediaOutputController mediaOutputController) { |
| super(context, broadcastSender, mediaOutputController); |
| mAdapter = new MediaOutputAdapter(mMediaOutputController); |
| // TODO(b/226710953): Move the part to MediaOutputBaseDialog for every class |
| // that extends MediaOutputBaseDialog |
| if (!aboveStatusbar) { |
| getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); |
| } |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| initBtQrCodeUI(); |
| } |
| |
| @Override |
| int getHeaderIconRes() { |
| return 0; |
| } |
| |
| @Override |
| IconCompat getHeaderIcon() { |
| return mMediaOutputController.getHeaderIcon(); |
| } |
| |
| @Override |
| int getHeaderIconSize() { |
| return mContext.getResources().getDimensionPixelSize( |
| R.dimen.media_output_dialog_header_album_icon_size); |
| } |
| |
| @Override |
| CharSequence getHeaderText() { |
| return mMediaOutputController.getHeaderTitle(); |
| } |
| |
| @Override |
| CharSequence getHeaderSubtitle() { |
| return mMediaOutputController.getHeaderSubTitle(); |
| } |
| |
| @Override |
| Drawable getAppSourceIcon() { |
| return mMediaOutputController.getAppSourceIcon(); |
| } |
| |
| @Override |
| int getStopButtonVisibility() { |
| return View.VISIBLE; |
| } |
| |
| @Override |
| public void onStopButtonClick() { |
| mMediaOutputController.stopBluetoothLeBroadcast(); |
| dismiss(); |
| } |
| |
| private String getBroadcastMetadataInfo(int metadata) { |
| switch (metadata) { |
| case METADATA_BROADCAST_NAME: |
| return mMediaOutputController.getBroadcastName(); |
| case METADATA_BROADCAST_CODE: |
| return mMediaOutputController.getBroadcastCode(); |
| default: |
| return ""; |
| } |
| } |
| |
| private void initBtQrCodeUI() { |
| //add the view to xml |
| inflateBroadcastInfoArea(); |
| |
| //init UI component |
| mBroadcastQrCodeView = getDialogView().requireViewById(R.id.qrcode_view); |
| |
| mBroadcastNotify = getDialogView().requireViewById(R.id.broadcast_info); |
| mBroadcastNotify.setOnClickListener(v -> { |
| mMediaOutputController.launchLeBroadcastNotifyDialog( |
| /* view= */ null, |
| /* broadcastSender= */ null, |
| MediaOutputController.BroadcastNotifyDialog.ACTION_BROADCAST_INFO_ICON, |
| /* onClickListener= */ null); |
| }); |
| mBroadcastName = getDialogView().requireViewById(R.id.broadcast_name_summary); |
| mBroadcastNameEdit = getDialogView().requireViewById(R.id.broadcast_name_edit); |
| mBroadcastNameEdit.setOnClickListener(v -> { |
| launchBroadcastUpdatedDialog(false, mBroadcastName.getText().toString()); |
| }); |
| mBroadcastCode = getDialogView().requireViewById(R.id.broadcast_code_summary); |
| mBroadcastCode.setTransformationMethod(PasswordTransformationMethod.getInstance()); |
| mBroadcastCodeEye = getDialogView().requireViewById(R.id.broadcast_code_eye); |
| mBroadcastCodeEye.setOnClickListener(v -> { |
| updateBroadcastCodeVisibility(); |
| }); |
| mBroadcastCodeEdit = getDialogView().requireViewById(R.id.broadcast_code_edit); |
| mBroadcastCodeEdit.setOnClickListener(v -> { |
| launchBroadcastUpdatedDialog(true, mBroadcastCode.getText().toString()); |
| }); |
| |
| refreshUi(); |
| } |
| |
| private void refreshUi() { |
| setQrCodeView(); |
| |
| mCurrentBroadcastName = getBroadcastMetadataInfo(METADATA_BROADCAST_NAME); |
| mCurrentBroadcastCode = getBroadcastMetadataInfo(METADATA_BROADCAST_CODE); |
| mBroadcastName.setText(mCurrentBroadcastName); |
| mBroadcastCode.setText(mCurrentBroadcastCode); |
| } |
| |
| private void inflateBroadcastInfoArea() { |
| mBroadcastInfoArea = getDialogView().requireViewById(R.id.broadcast_qrcode); |
| mBroadcastInfoArea.inflate(); |
| } |
| |
| private void setQrCodeView() { |
| //get the Metadata, and convert to BT QR code format. |
| String broadcastMetadata = getBroadcastMetadata(); |
| if (broadcastMetadata.isEmpty()) { |
| //TDOD(b/226708424) Error handling for unable to generate the QR code bitmap |
| return; |
| } |
| try { |
| final int qrcodeSize = getContext().getResources().getDimensionPixelSize( |
| R.dimen.media_output_qrcode_size); |
| final Bitmap bmp = QrCodeGenerator.encodeQrCode(broadcastMetadata, qrcodeSize); |
| mBroadcastQrCodeView.setImageBitmap(bmp); |
| } catch (WriterException e) { |
| //TDOD(b/226708424) Error handling for unable to generate the QR code bitmap |
| Log.e(TAG, "Error generatirng QR code bitmap " + e); |
| } |
| } |
| |
| private void updateBroadcastCodeVisibility() { |
| mBroadcastCode.setTransformationMethod( |
| mIsPasswordHide ? HideReturnsTransformationMethod.getInstance() |
| : PasswordTransformationMethod.getInstance()); |
| mIsPasswordHide = !mIsPasswordHide; |
| } |
| |
| private void launchBroadcastUpdatedDialog(boolean isBroadcastCode, String editString) { |
| final View layout = LayoutInflater.from(mContext).inflate( |
| R.layout.media_output_broadcast_update_dialog, null); |
| final EditText editText = layout.requireViewById(R.id.broadcast_edit_text); |
| editText.setText(editString); |
| mBroadcastErrorMessage = layout.requireViewById(R.id.broadcast_error_message); |
| mAlertDialog = new Builder(mContext) |
| .setTitle(isBroadcastCode ? R.string.media_output_broadcast_code |
| : R.string.media_output_broadcast_name) |
| .setView(layout) |
| .setNegativeButton(android.R.string.cancel, null) |
| .setPositiveButton(R.string.media_output_broadcast_dialog_save, |
| (d, w) -> { |
| updateBroadcastInfo(isBroadcastCode, editText.getText().toString()); |
| }) |
| .create(); |
| |
| mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); |
| SystemUIDialog.setShowForAllUsers(mAlertDialog, true); |
| SystemUIDialog.registerDismissListener(mAlertDialog); |
| mAlertDialog.show(); |
| } |
| |
| private String getBroadcastMetadata() { |
| return mMediaOutputController.getBroadcastMetadata(); |
| } |
| |
| private void updateBroadcastInfo(boolean isBroadcastCode, String updatedString) { |
| Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE); |
| if (positiveBtn != null) { |
| positiveBtn.setEnabled(false); |
| } |
| |
| if (isBroadcastCode) { |
| /* If the user wants to update the Broadcast Code, the Broadcast session should be |
| * stopped then used the new Broadcast code to start the Broadcast. |
| */ |
| mIsStopbyUpdateBroadcastCode = true; |
| mMediaOutputController.setBroadcastCode(updatedString); |
| if (!mMediaOutputController.stopBluetoothLeBroadcast()) { |
| handleLeBroadcastStopFailed(); |
| return; |
| } |
| } else { |
| /* If the user wants to update the Broadcast Name, we don't need to stop the Broadcast |
| * session. Only use the new Broadcast name to update the broadcast session. |
| */ |
| mMediaOutputController.setBroadcastName(updatedString); |
| if (!mMediaOutputController.updateBluetoothLeBroadcast()) { |
| handleLeBroadcastUpdateFailed(); |
| } |
| } |
| } |
| |
| @Override |
| public void handleLeBroadcastStarted() { |
| mRetryCount = 0; |
| if (mAlertDialog != null) { |
| mAlertDialog.dismiss(); |
| } |
| refreshUi(); |
| } |
| |
| @Override |
| public void handleLeBroadcastStartFailed() { |
| mMediaOutputController.setBroadcastCode(mCurrentBroadcastCode); |
| mRetryCount++; |
| |
| handleUpdateFailedUi(); |
| } |
| |
| @Override |
| public void handleLeBroadcastMetadataChanged() { |
| refreshUi(); |
| } |
| |
| @Override |
| public void handleLeBroadcastUpdated() { |
| mRetryCount = 0; |
| if (mAlertDialog != null) { |
| mAlertDialog.dismiss(); |
| } |
| refreshUi(); |
| } |
| |
| @Override |
| public void handleLeBroadcastUpdateFailed() { |
| //Change the value in shared preferences back to it original value |
| mMediaOutputController.setBroadcastName(mCurrentBroadcastName); |
| mRetryCount++; |
| |
| handleUpdateFailedUi(); |
| } |
| |
| @Override |
| public void handleLeBroadcastStopped() { |
| if (mIsStopbyUpdateBroadcastCode) { |
| mIsStopbyUpdateBroadcastCode = false; |
| mRetryCount = 0; |
| if (!mMediaOutputController.startBluetoothLeBroadcast()) { |
| handleLeBroadcastStartFailed(); |
| return; |
| } |
| } else { |
| dismiss(); |
| } |
| } |
| |
| @Override |
| public void handleLeBroadcastStopFailed() { |
| //Change the value in shared preferences back to it original value |
| mMediaOutputController.setBroadcastCode(mCurrentBroadcastCode); |
| mRetryCount++; |
| |
| handleUpdateFailedUi(); |
| } |
| |
| private void handleUpdateFailedUi() { |
| final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE); |
| mBroadcastErrorMessage.setVisibility(View.VISIBLE); |
| if (mRetryCount < MAX_BROADCAST_INFO_UPDATE) { |
| if (positiveBtn != null) { |
| positiveBtn.setEnabled(true); |
| } |
| mBroadcastErrorMessage.setText(R.string.media_output_broadcast_update_error); |
| } else { |
| mRetryCount = 0; |
| mBroadcastErrorMessage.setText(R.string.media_output_broadcast_last_update_error); |
| } |
| } |
| } |