mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 12:09:45 +01:00
Merge pull request #1794 from nextcloud/feature/1761/selectAudioOutput
Feature/1761/select audio output
This commit is contained in:
commit
f076a81427
@ -82,6 +82,7 @@ import com.nextcloud.talk.models.json.signaling.Signaling;
|
||||
import com.nextcloud.talk.models.json.signaling.SignalingOverall;
|
||||
import com.nextcloud.talk.models.json.signaling.settings.IceServer;
|
||||
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall;
|
||||
import com.nextcloud.talk.ui.dialog.AudioOutputDialog;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.NotificationUtils;
|
||||
@ -142,6 +143,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import autodagger.AutoInjector;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Observer;
|
||||
@ -170,6 +173,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
public static final String TAG = "CallActivity";
|
||||
|
||||
public MagicAudioManager audioManager;
|
||||
|
||||
private static final String[] PERMISSIONS_CALL = {
|
||||
android.Manifest.permission.CAMERA,
|
||||
android.Manifest.permission.RECORD_AUDIO,
|
||||
@ -195,7 +200,6 @@ public class CallActivity extends CallBaseActivity {
|
||||
private MediaConstraints videoConstraints;
|
||||
private MediaConstraints sdpConstraints;
|
||||
private MediaConstraints sdpConstraintsForMCU;
|
||||
private MagicAudioManager audioManager;
|
||||
private VideoSource videoSource;
|
||||
private VideoTrack localVideoTrack;
|
||||
private AudioSource audioSource;
|
||||
@ -252,6 +256,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
private CallActivityBinding binding;
|
||||
|
||||
private AudioOutputDialog audioOutputDialog;
|
||||
|
||||
@Parcel
|
||||
public enum CallStatus {
|
||||
CONNECTING, CALLING_TIMEOUT, JOINED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED
|
||||
@ -327,15 +333,9 @@ public class CallActivity extends CallBaseActivity {
|
||||
private void initClickListeners() {
|
||||
binding.pictureInPictureButton.setOnClickListener(l -> enterPipMode());
|
||||
|
||||
binding.speakerButton.setOnClickListener(l -> {
|
||||
if (audioManager != null) {
|
||||
audioManager.toggleUseSpeakerphone();
|
||||
if (audioManager.isSpeakerphoneAutoOn()) {
|
||||
binding.speakerButton.getHierarchy().setPlaceholderImage(R.drawable.ic_volume_up_white_24dp);
|
||||
} else {
|
||||
binding.speakerButton.getHierarchy().setPlaceholderImage(R.drawable.ic_volume_mute_white_24dp);
|
||||
}
|
||||
}
|
||||
binding.audioOutputButton.setOnClickListener(v -> {
|
||||
audioOutputDialog = new AudioOutputDialog(this);
|
||||
audioOutputDialog.show();
|
||||
});
|
||||
|
||||
binding.microphoneButton.setOnClickListener(l -> onMicrophoneClick());
|
||||
@ -377,8 +377,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
boolean camera2EnumeratorIsSupported = false;
|
||||
try {
|
||||
camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(this);
|
||||
} catch (final Throwable throwable) {
|
||||
Log.w(TAG, "Camera2Enumator threw an error");
|
||||
} catch (final Throwable t) {
|
||||
Log.w(TAG, "Camera2Enumerator threw an error", t);
|
||||
}
|
||||
|
||||
if (camera2EnumeratorIsSupported) {
|
||||
@ -412,12 +412,18 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
// Create and audio manager that will take care of audio routing,
|
||||
// audio modes, audio device enumeration etc.
|
||||
audioManager = MagicAudioManager.create(getApplicationContext(), !isVoiceOnlyCall);
|
||||
audioManager = MagicAudioManager.create(getApplicationContext(), isVoiceOnlyCall);
|
||||
// Store existing audio settings and change audio mode to
|
||||
// MODE_IN_COMMUNICATION for best possible VoIP performance.
|
||||
Log.d(TAG, "Starting the audio manager...");
|
||||
audioManager.start(this::onAudioManagerDevicesChanged);
|
||||
|
||||
if (isVoiceOnlyCall) {
|
||||
setAudioOutputChannel(MagicAudioManager.AudioDevice.EARPIECE);
|
||||
} else {
|
||||
setAudioOutputChannel(MagicAudioManager.AudioDevice.SPEAKER_PHONE);
|
||||
}
|
||||
|
||||
iceServers = new ArrayList<>();
|
||||
|
||||
//create sdpConstraints
|
||||
@ -448,6 +454,38 @@ public class CallActivity extends CallBaseActivity {
|
||||
microphoneInitialization();
|
||||
}
|
||||
|
||||
public void setAudioOutputChannel(MagicAudioManager.AudioDevice selectedAudioDevice) {
|
||||
if (audioManager != null) {
|
||||
audioManager.selectAudioDevice(selectedAudioDevice);
|
||||
updateAudioOutputButton(audioManager.getCurrentAudioDevice());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAudioOutputButton(MagicAudioManager.AudioDevice activeAudioDevice) {
|
||||
switch (activeAudioDevice) {
|
||||
case BLUETOOTH:
|
||||
binding.audioOutputButton.getHierarchy().setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_baseline_bluetooth_audio_24));
|
||||
break;
|
||||
case SPEAKER_PHONE:
|
||||
binding.audioOutputButton.getHierarchy().setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_volume_up_white_24dp));
|
||||
break;
|
||||
case EARPIECE:
|
||||
binding.audioOutputButton.getHierarchy().setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_baseline_phone_in_talk_24));
|
||||
break;
|
||||
case WIRED_HEADSET:
|
||||
binding.audioOutputButton.getHierarchy().setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_baseline_headset_mic_24));
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Icon for audio output not available");
|
||||
break;
|
||||
}
|
||||
DrawableCompat.setTint(binding.audioOutputButton.getDrawable(), Color.WHITE);
|
||||
}
|
||||
|
||||
private void handleFromNotification() {
|
||||
int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1});
|
||||
|
||||
@ -496,7 +534,6 @@ public class CallActivity extends CallBaseActivity {
|
||||
}
|
||||
|
||||
if (isVoiceOnlyCall) {
|
||||
binding.speakerButton.setVisibility(View.VISIBLE);
|
||||
binding.switchSelfVideoButton.setVisibility(View.GONE);
|
||||
binding.cameraButton.setVisibility(View.GONE);
|
||||
binding.selfVideoRenderer.setVisibility(View.GONE);
|
||||
@ -513,7 +550,6 @@ public class CallActivity extends CallBaseActivity {
|
||||
params.setMargins(0, 0, 0, 0);
|
||||
binding.gridview.setLayoutParams(params);
|
||||
|
||||
binding.speakerButton.setVisibility(View.GONE);
|
||||
if (cameraEnumerator.getDeviceNames().length < 2) {
|
||||
binding.switchSelfVideoButton.setVisibility(View.GONE);
|
||||
}
|
||||
@ -713,19 +749,25 @@ public class CallActivity extends CallBaseActivity {
|
||||
}
|
||||
|
||||
private void onAudioManagerDevicesChanged(
|
||||
final MagicAudioManager.AudioDevice device, final Set<MagicAudioManager.AudioDevice> availableDevices) {
|
||||
final MagicAudioManager.AudioDevice currentDevice,
|
||||
final Set<MagicAudioManager.AudioDevice> availableDevices) {
|
||||
Log.d(TAG, "onAudioManagerDevicesChanged: " + availableDevices + ", "
|
||||
+ "selected: " + device);
|
||||
+ "currentDevice: " + currentDevice);
|
||||
|
||||
final boolean shouldDisableProximityLock = (device.equals(MagicAudioManager.AudioDevice.WIRED_HEADSET)
|
||||
|| device.equals(MagicAudioManager.AudioDevice.SPEAKER_PHONE)
|
||||
|| device.equals(MagicAudioManager.AudioDevice.BLUETOOTH));
|
||||
final boolean shouldDisableProximityLock = (currentDevice.equals(MagicAudioManager.AudioDevice.WIRED_HEADSET)
|
||||
|| currentDevice.equals(MagicAudioManager.AudioDevice.SPEAKER_PHONE)
|
||||
|| currentDevice.equals(MagicAudioManager.AudioDevice.BLUETOOTH));
|
||||
|
||||
if (shouldDisableProximityLock) {
|
||||
powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.WITHOUT_PROXIMITY_SENSOR_LOCK);
|
||||
} else {
|
||||
powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.WITH_PROXIMITY_SENSOR_LOCK);
|
||||
}
|
||||
|
||||
if (audioOutputDialog != null) {
|
||||
audioOutputDialog.updateOutputDeviceList();
|
||||
}
|
||||
updateAudioOutputButton(currentDevice);
|
||||
}
|
||||
|
||||
|
||||
@ -1641,10 +1683,10 @@ public class CallActivity extends CallBaseActivity {
|
||||
Log.d(TAG, " currentSessionId is " + currentSessionId);
|
||||
|
||||
for (HashMap<String, Object> participant : users) {
|
||||
long inCallFlag = (long)participant.get("inCall");
|
||||
long inCallFlag = (long) participant.get("inCall");
|
||||
if (!participant.get("sessionId").equals(currentSessionId)) {
|
||||
boolean isNewSession;
|
||||
Log.d(TAG, " inCallFlag of participant " + participant.get("sessionId").toString().substring(0,4) + " : " + inCallFlag);
|
||||
Log.d(TAG, " inCallFlag of participant " + participant.get("sessionId").toString().substring(0, 4) + " : " + inCallFlag);
|
||||
isNewSession = inCallFlag != 0;
|
||||
|
||||
if (isNewSession) {
|
||||
@ -1654,7 +1696,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, " inCallFlag of currentSessionId: " + inCallFlag);
|
||||
if (inCallFlag == 0){
|
||||
if (inCallFlag == 0) {
|
||||
Log.d(TAG, "Most probably a moderator ended the call for all.");
|
||||
hangup(true);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public class MenuItem extends AbstractFlexibleItem<MenuItem.MenuItemViewHolder>
|
||||
this.title = title;
|
||||
this.tag = tag;
|
||||
this.icon = icon;
|
||||
padding = (int) DisplayUtils.convertDpToPixel(16,
|
||||
padding = (int) DisplayUtils.convertDpToPixel(32,
|
||||
NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.ui.dialog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.activities.CallActivity
|
||||
import com.nextcloud.talk.databinding.DialogAudioOutputBinding
|
||||
import com.nextcloud.talk.webrtc.MagicAudioManager
|
||||
|
||||
class AudioOutputDialog(val callActivity: CallActivity) : BottomSheetDialog(callActivity) {
|
||||
|
||||
private lateinit var dialogAudioOutputBinding: DialogAudioOutputBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
dialogAudioOutputBinding = DialogAudioOutputBinding.inflate(layoutInflater)
|
||||
setContentView(dialogAudioOutputBinding.root)
|
||||
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
updateOutputDeviceList()
|
||||
initClickListeners()
|
||||
}
|
||||
|
||||
fun updateOutputDeviceList() {
|
||||
if (callActivity.audioManager?.audioDevices?.contains(MagicAudioManager.AudioDevice.BLUETOOTH) == false) {
|
||||
dialogAudioOutputBinding.audioOutputBluetooth.visibility = View.GONE
|
||||
} else {
|
||||
dialogAudioOutputBinding.audioOutputBluetooth.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (callActivity.audioManager?.audioDevices?.contains(MagicAudioManager.AudioDevice.EARPIECE) == false) {
|
||||
dialogAudioOutputBinding.audioOutputEarspeaker.visibility = View.GONE
|
||||
} else {
|
||||
dialogAudioOutputBinding.audioOutputEarspeaker.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (callActivity.audioManager?.audioDevices?.contains(MagicAudioManager.AudioDevice.SPEAKER_PHONE) == false) {
|
||||
dialogAudioOutputBinding.audioOutputSpeaker.visibility = View.GONE
|
||||
} else {
|
||||
dialogAudioOutputBinding.audioOutputSpeaker.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (callActivity.audioManager?.currentAudioDevice?.equals(
|
||||
MagicAudioManager.AudioDevice.WIRED_HEADSET
|
||||
) == true
|
||||
) {
|
||||
dialogAudioOutputBinding.audioOutputEarspeaker.visibility = View.GONE
|
||||
dialogAudioOutputBinding.audioOutputSpeaker.visibility = View.GONE
|
||||
dialogAudioOutputBinding.audioOutputWiredHeadset.visibility = View.VISIBLE
|
||||
} else {
|
||||
dialogAudioOutputBinding.audioOutputWiredHeadset.visibility = View.GONE
|
||||
}
|
||||
|
||||
highlightActiveOutputChannel()
|
||||
}
|
||||
|
||||
private fun highlightActiveOutputChannel() {
|
||||
when (callActivity.audioManager?.currentAudioDevice) {
|
||||
MagicAudioManager.AudioDevice.BLUETOOTH -> {
|
||||
dialogAudioOutputBinding.audioOutputBluetoothIcon.setColorFilter(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.colorPrimary
|
||||
),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
dialogAudioOutputBinding.audioOutputBluetoothText.setTextColor(
|
||||
callActivity.resources.getColor(R.color.colorPrimary)
|
||||
)
|
||||
}
|
||||
|
||||
MagicAudioManager.AudioDevice.SPEAKER_PHONE -> {
|
||||
dialogAudioOutputBinding.audioOutputSpeakerIcon.setColorFilter(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.colorPrimary
|
||||
),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
dialogAudioOutputBinding.audioOutputSpeakerText.setTextColor(
|
||||
callActivity.resources.getColor(R.color.colorPrimary)
|
||||
)
|
||||
}
|
||||
|
||||
MagicAudioManager.AudioDevice.EARPIECE -> {
|
||||
dialogAudioOutputBinding.audioOutputEarspeakerIcon.setColorFilter(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.colorPrimary
|
||||
),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
dialogAudioOutputBinding.audioOutputEarspeakerText.setTextColor(
|
||||
callActivity.resources.getColor(R.color.colorPrimary)
|
||||
)
|
||||
}
|
||||
|
||||
MagicAudioManager.AudioDevice.WIRED_HEADSET -> {
|
||||
dialogAudioOutputBinding.audioOutputWiredHeadsetIcon.setColorFilter(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.colorPrimary
|
||||
),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
dialogAudioOutputBinding.audioOutputWiredHeadsetText.setTextColor(
|
||||
callActivity.resources.getColor(R.color.colorPrimary)
|
||||
)
|
||||
}
|
||||
|
||||
else -> Log.d(TAG, "AudioOutputDialog doesn't know this AudioDevice")
|
||||
}
|
||||
}
|
||||
|
||||
private fun initClickListeners() {
|
||||
dialogAudioOutputBinding.audioOutputBluetooth.setOnClickListener {
|
||||
callActivity.setAudioOutputChannel(MagicAudioManager.AudioDevice.BLUETOOTH)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
dialogAudioOutputBinding.audioOutputSpeaker.setOnClickListener {
|
||||
callActivity.setAudioOutputChannel(MagicAudioManager.AudioDevice.SPEAKER_PHONE)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
dialogAudioOutputBinding.audioOutputEarspeaker.setOnClickListener {
|
||||
callActivity.setAudioOutputChannel(MagicAudioManager.AudioDevice.EARPIECE)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val bottomSheet = findViewById<View>(R.id.design_bottom_sheet)
|
||||
val behavior = BottomSheetBehavior.from(bottomSheet as View)
|
||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "AudioOutputDialog"
|
||||
}
|
||||
}
|
@ -41,8 +41,10 @@ import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nextcloud.talk.events.PeerConnectionEvent;
|
||||
import com.nextcloud.talk.utils.power.PowerManagerUtils;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.webrtc.ThreadUtils;
|
||||
|
||||
@ -55,45 +57,25 @@ import java.util.Set;
|
||||
*/
|
||||
public class MagicAudioManager {
|
||||
private static final String TAG = "MagicAudioManager";
|
||||
private static final String SPEAKERPHONE_AUTO = "auto";
|
||||
private static final String SPEAKERPHONE_FALSE = "false";
|
||||
private final Context magicContext;
|
||||
// Handles all tasks related to Bluetooth headset devices.
|
||||
private final MagicBluetoothManager bluetoothManager;
|
||||
// Contains speakerphone setting: auto, true or false
|
||||
private String useSpeakerphone;
|
||||
private boolean useProximitySensor;
|
||||
private AudioManager audioManager;
|
||||
private AudioManagerEvents audioManagerEvents;
|
||||
private AudioManagerListener audioManagerListener;
|
||||
private AudioManagerState amState;
|
||||
private int savedAudioMode = AudioManager.MODE_INVALID;
|
||||
private boolean savedIsSpeakerPhoneOn = false;
|
||||
private boolean savedIsMicrophoneMute = false;
|
||||
private boolean hasWiredHeadset = false;
|
||||
// Default audio device; speaker phone for video calls or earpiece for audio
|
||||
// only calls.
|
||||
private AudioDevice defaultAudioDevice;
|
||||
// Contains the currently selected audio device.
|
||||
// This device is changed automatically using a certain scheme where e.g.
|
||||
// a wired headset "wins" over speaker phone. It is also possible for a
|
||||
// user to explicitly select a device (and overrid any predefined scheme).
|
||||
// See |userSelectedAudioDevice| for details.
|
||||
private AudioDevice selectedAudioDevice;
|
||||
// Contains the user-selected audio device which overrides the predefined
|
||||
// selection scheme.
|
||||
// TODO(henrika): always set to AudioDevice.NONE today. Add support for
|
||||
// explicit selection based on choice by userSelectedAudioDevice.
|
||||
|
||||
private AudioDevice userSelectedAudioDevice;
|
||||
// Proximity sensor object. It measures the proximity of an object in cm
|
||||
// relative to the view screen of a device and can therefore be used to
|
||||
// assist device switching (close to ear <=> use headset earpiece if
|
||||
// available, far from ear <=> use speaker phone).
|
||||
private AudioDevice currentAudioDevice;
|
||||
|
||||
private MagicProximitySensor proximitySensor = null;
|
||||
// Contains a list of available audio devices. A Set collection is used to
|
||||
// avoid duplicate elements.
|
||||
|
||||
private Set<AudioDevice> audioDevices = new HashSet<>();
|
||||
// Broadcast receiver for wired headset intent broadcasts.
|
||||
|
||||
private BroadcastReceiver wiredHeadsetReceiver;
|
||||
// Callback method for changes in audio focus.
|
||||
private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;
|
||||
|
||||
private PowerManagerUtils powerManagerUtils;
|
||||
@ -110,18 +92,8 @@ public class MagicAudioManager {
|
||||
powerManagerUtils = new PowerManagerUtils();
|
||||
powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.WITH_PROXIMITY_SENSOR_LOCK);
|
||||
|
||||
if (useProximitySensor) {
|
||||
useSpeakerphone = SPEAKERPHONE_AUTO;
|
||||
} else {
|
||||
useSpeakerphone = SPEAKERPHONE_FALSE;
|
||||
}
|
||||
|
||||
|
||||
if (useSpeakerphone.equals(SPEAKERPHONE_FALSE)) {
|
||||
defaultAudioDevice = AudioDevice.EARPIECE;
|
||||
} else {
|
||||
defaultAudioDevice = AudioDevice.SPEAKER_PHONE;
|
||||
}
|
||||
this.useProximitySensor = useProximitySensor;
|
||||
updateAudioDeviceState();
|
||||
|
||||
// Create and initialize the proximity sensor.
|
||||
// Tablet devices (e.g. Nexus 7) does not support proximity sensors.
|
||||
@ -134,8 +106,6 @@ public class MagicAudioManager {
|
||||
onProximitySensorChangedState();
|
||||
}
|
||||
});
|
||||
|
||||
Log.d(TAG, "defaultAudioDevice: " + defaultAudioDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,57 +115,38 @@ public class MagicAudioManager {
|
||||
return new MagicAudioManager(context, useProximitySensor);
|
||||
}
|
||||
|
||||
public void toggleUseSpeakerphone() {
|
||||
if (useSpeakerphone.equals(SPEAKERPHONE_FALSE)) {
|
||||
useSpeakerphone = SPEAKERPHONE_AUTO;
|
||||
setDefaultAudioDevice(AudioDevice.SPEAKER_PHONE);
|
||||
} else {
|
||||
useSpeakerphone = SPEAKERPHONE_FALSE;
|
||||
setDefaultAudioDevice(AudioDevice.EARPIECE);
|
||||
}
|
||||
|
||||
updateAudioDeviceState();
|
||||
}
|
||||
|
||||
public boolean isSpeakerphoneAutoOn() {
|
||||
return (useSpeakerphone.equals(SPEAKERPHONE_AUTO));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when the proximity sensor reports a state change,
|
||||
* e.g. from "NEAR to FAR" or from "FAR to NEAR".
|
||||
* This method is called when the proximity sensor reports a state change, e.g. from "NEAR to FAR" or from "FAR to
|
||||
* NEAR".
|
||||
*/
|
||||
private void onProximitySensorChangedState() {
|
||||
|
||||
if (!useSpeakerphone.equals(SPEAKERPHONE_AUTO)) {
|
||||
if (!useProximitySensor) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The proximity sensor should only be activated when there are exactly two
|
||||
// available audio devices.
|
||||
if (audioDevices.size() == 2 && audioDevices.contains(MagicAudioManager.AudioDevice.EARPIECE)
|
||||
&& audioDevices.contains(MagicAudioManager.AudioDevice.SPEAKER_PHONE)) {
|
||||
if (userSelectedAudioDevice.equals(AudioDevice.SPEAKER_PHONE)
|
||||
&& audioDevices.contains(AudioDevice.EARPIECE)
|
||||
&& audioDevices.contains(AudioDevice.SPEAKER_PHONE)) {
|
||||
|
||||
if (proximitySensor.sensorReportsNearState()) {
|
||||
// Sensor reports that a "handset is being held up to a person's ear",
|
||||
// or "something is covering the light sensor".
|
||||
setAudioDeviceInternal(MagicAudioManager.AudioDevice.EARPIECE);
|
||||
setAudioDeviceInternal(AudioDevice.EARPIECE);
|
||||
Log.d(TAG, "switched to EARPIECE because userSelectedAudioDevice was SPEAKER_PHONE and proximity=near");
|
||||
|
||||
EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
|
||||
.SENSOR_NEAR, null, null, null, null));
|
||||
.SENSOR_NEAR, null, null, null, null));
|
||||
|
||||
} else {
|
||||
// Sensor reports that a "handset is removed from a person's ear", or
|
||||
// "the light sensor is no longer covered".
|
||||
setAudioDeviceInternal(MagicAudioManager.AudioDevice.SPEAKER_PHONE);
|
||||
Log.d(TAG, "switched to SPEAKER_PHONE because userSelectedAudioDevice was SPEAKER_PHONE and proximity=far");
|
||||
|
||||
EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
|
||||
.SENSOR_FAR, null, null, null, null));
|
||||
.SENSOR_FAR, null, null, null, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
public void start(AudioManagerEvents audioManagerEvents) {
|
||||
public void start(AudioManagerListener audioManagerListener) {
|
||||
Log.d(TAG, "start");
|
||||
ThreadUtils.checkIsOnMainThread();
|
||||
if (amState == AudioManagerState.RUNNING) {
|
||||
@ -205,7 +156,7 @@ public class MagicAudioManager {
|
||||
// TODO(henrika): perhaps call new method called preInitAudio() here if UNINITIALIZED.
|
||||
|
||||
Log.d(TAG, "AudioManager starts...");
|
||||
this.audioManagerEvents = audioManagerEvents;
|
||||
this.audioManagerListener = audioManagerListener;
|
||||
amState = AudioManagerState.RUNNING;
|
||||
|
||||
// Store current audio state so we can restore it when stop() is called.
|
||||
@ -257,7 +208,7 @@ public class MagicAudioManager {
|
||||
|
||||
// Request audio playout focus (without ducking) and install listener for changes in focus.
|
||||
int result = audioManager.requestAudioFocus(audioFocusChangeListener,
|
||||
AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
|
||||
AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||
Log.d(TAG, "Audio focus request granted for VOICE_CALL streams");
|
||||
} else {
|
||||
@ -274,7 +225,7 @@ public class MagicAudioManager {
|
||||
|
||||
// Set initial device states.
|
||||
userSelectedAudioDevice = AudioDevice.NONE;
|
||||
selectedAudioDevice = AudioDevice.NONE;
|
||||
currentAudioDevice = AudioDevice.NONE;
|
||||
audioDevices.clear();
|
||||
|
||||
// Initialize and start Bluetooth if a BT device is available or initiate
|
||||
@ -324,7 +275,7 @@ public class MagicAudioManager {
|
||||
|
||||
powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.IDLE);
|
||||
|
||||
audioManagerEvents = null;
|
||||
audioManagerListener = null;
|
||||
Log.d(TAG, "AudioManager stopped");
|
||||
}
|
||||
|
||||
@ -333,21 +284,16 @@ public class MagicAudioManager {
|
||||
/**
|
||||
* Changes selection of the currently active audio device.
|
||||
*/
|
||||
private void setAudioDeviceInternal(AudioDevice device) {
|
||||
Log.d(TAG, "setAudioDeviceInternal(device=" + device + ")");
|
||||
private void setAudioDeviceInternal(AudioDevice audioDevice) {
|
||||
Log.d(TAG, "setAudioDeviceInternal(device=" + audioDevice + ")");
|
||||
|
||||
if (audioDevices.contains(device)) {
|
||||
|
||||
switch (device) {
|
||||
if (audioDevices.contains(audioDevice)) {
|
||||
switch (audioDevice) {
|
||||
case SPEAKER_PHONE:
|
||||
setSpeakerphoneOn(true);
|
||||
break;
|
||||
case EARPIECE:
|
||||
setSpeakerphoneOn(false);
|
||||
break;
|
||||
case WIRED_HEADSET:
|
||||
setSpeakerphoneOn(false);
|
||||
break;
|
||||
case BLUETOOTH:
|
||||
setSpeakerphoneOn(false);
|
||||
break;
|
||||
@ -355,35 +301,10 @@ public class MagicAudioManager {
|
||||
Log.e(TAG, "Invalid audio device selection");
|
||||
break;
|
||||
}
|
||||
selectedAudioDevice = device;
|
||||
currentAudioDevice = audioDevice;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes default audio device.
|
||||
* TODO(henrika): add usage of this method in the AppRTCMobile client.
|
||||
*/
|
||||
public void setDefaultAudioDevice(AudioDevice defaultDevice) {
|
||||
ThreadUtils.checkIsOnMainThread();
|
||||
switch (defaultDevice) {
|
||||
case SPEAKER_PHONE:
|
||||
defaultAudioDevice = defaultDevice;
|
||||
break;
|
||||
case EARPIECE:
|
||||
if (hasEarpiece()) {
|
||||
defaultAudioDevice = defaultDevice;
|
||||
} else {
|
||||
defaultAudioDevice = AudioDevice.SPEAKER_PHONE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Invalid default audio device selection");
|
||||
break;
|
||||
}
|
||||
Log.d(TAG, "setDefaultAudioDevice(device=" + defaultAudioDevice + ")");
|
||||
updateAudioDeviceState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes selection of the currently active audio device.
|
||||
*/
|
||||
@ -407,9 +328,9 @@ public class MagicAudioManager {
|
||||
/**
|
||||
* Returns the currently selected audio device.
|
||||
*/
|
||||
public AudioDevice getSelectedAudioDevice() {
|
||||
public AudioDevice getCurrentAudioDevice() {
|
||||
ThreadUtils.checkIsOnMainThread();
|
||||
return selectedAudioDevice;
|
||||
return currentAudioDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -456,11 +377,9 @@ public class MagicAudioManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a wired headset is connected or not.
|
||||
* This is not a valid indication that audio playback is actually over
|
||||
* the wired headset as audio routing depends on other conditions. We
|
||||
* only use it as an early indicator (during initialization) of an attached
|
||||
* wired headset.
|
||||
* Checks whether a wired headset is connected or not. This is not a valid indication that audio playback is
|
||||
* actually over the wired headset as audio routing depends on other conditions. We only use it as an early
|
||||
* indicator (during initialization) of an attached wired headset.
|
||||
*/
|
||||
@Deprecated
|
||||
private boolean hasWiredHeadset() {
|
||||
@ -482,35 +401,27 @@ public class MagicAudioManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates list of possible audio devices and make new device selection.
|
||||
* TODO(henrika): add unit test to verify all state transitions.
|
||||
*/
|
||||
public void updateAudioDeviceState() {
|
||||
ThreadUtils.checkIsOnMainThread();
|
||||
Log.d(TAG, "--- updateAudioDeviceState: "
|
||||
+ "wired headset=" + hasWiredHeadset + ", "
|
||||
+ "BT state=" + bluetoothManager.getState());
|
||||
+ "wired headset=" + hasWiredHeadset + ", "
|
||||
+ "BT state=" + bluetoothManager.getState());
|
||||
Log.d(TAG, "Device status: "
|
||||
+ "available=" + audioDevices + ", "
|
||||
+ "selected=" + selectedAudioDevice + ", "
|
||||
+ "user selected=" + userSelectedAudioDevice);
|
||||
+ "available=" + audioDevices + ", "
|
||||
+ "current=" + currentAudioDevice + ", "
|
||||
+ "user selected=" + userSelectedAudioDevice);
|
||||
|
||||
// Check if any Bluetooth headset is connected. The internal BT state will
|
||||
// change accordingly.
|
||||
// TODO(henrika): perhaps wrap required state into BT manager.
|
||||
if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_UNAVAILABLE
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_DISCONNECTING) {
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_UNAVAILABLE
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_DISCONNECTING) {
|
||||
bluetoothManager.updateDevice();
|
||||
}
|
||||
|
||||
// Update the set of available audio devices.
|
||||
Set<AudioDevice> newAudioDevices = new HashSet<>();
|
||||
|
||||
if (bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE) {
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE) {
|
||||
newAudioDevices.add(AudioDevice.BLUETOOTH);
|
||||
}
|
||||
|
||||
@ -518,55 +429,50 @@ public class MagicAudioManager {
|
||||
// If a wired headset is connected, then it is the only possible option.
|
||||
newAudioDevices.add(AudioDevice.WIRED_HEADSET);
|
||||
} else {
|
||||
// No wired headset, hence the audio-device list can contain speaker
|
||||
// phone (on a tablet), or speaker phone and earpiece (on mobile phone).
|
||||
newAudioDevices.add(AudioDevice.SPEAKER_PHONE);
|
||||
if (hasEarpiece()) {
|
||||
newAudioDevices.add(AudioDevice.EARPIECE);
|
||||
}
|
||||
}
|
||||
// Store state which is set to true if the device list has changed.
|
||||
|
||||
boolean audioDeviceSetUpdated = !audioDevices.equals(newAudioDevices);
|
||||
// Update the existing audio device set.
|
||||
audioDevices = newAudioDevices;
|
||||
|
||||
|
||||
// Correct user selected audio devices if needed.
|
||||
if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_UNAVAILABLE
|
||||
&& userSelectedAudioDevice == AudioDevice.BLUETOOTH) {
|
||||
// If BT is not available, it can't be the user selection.
|
||||
userSelectedAudioDevice = AudioDevice.NONE;
|
||||
}
|
||||
if (hasWiredHeadset && userSelectedAudioDevice == AudioDevice.SPEAKER_PHONE) {
|
||||
// If user selected speaker phone, but then plugged wired headset then make
|
||||
// wired headset as user selected device.
|
||||
userSelectedAudioDevice = AudioDevice.WIRED_HEADSET;
|
||||
}
|
||||
if (!hasWiredHeadset && userSelectedAudioDevice == AudioDevice.WIRED_HEADSET) {
|
||||
// If user selected wired headset, but then unplugged wired headset then make
|
||||
// speaker phone as user selected device.
|
||||
if (userSelectedAudioDevice == AudioDevice.BLUETOOTH
|
||||
&& bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_UNAVAILABLE) {
|
||||
userSelectedAudioDevice = AudioDevice.SPEAKER_PHONE;
|
||||
}
|
||||
if (userSelectedAudioDevice == AudioDevice.SPEAKER_PHONE && hasWiredHeadset) {
|
||||
userSelectedAudioDevice = AudioDevice.WIRED_HEADSET;
|
||||
}
|
||||
if (userSelectedAudioDevice == AudioDevice.WIRED_HEADSET && !hasWiredHeadset) {
|
||||
userSelectedAudioDevice = AudioDevice.SPEAKER_PHONE;
|
||||
}
|
||||
|
||||
|
||||
// Need to start Bluetooth if it is available and user either selected it explicitly or
|
||||
// user did not select any output device.
|
||||
boolean needBluetoothAudioStart =
|
||||
bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE
|
||||
&& (userSelectedAudioDevice == AudioDevice.NONE
|
||||
|| userSelectedAudioDevice == AudioDevice.BLUETOOTH);
|
||||
bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE
|
||||
&& (userSelectedAudioDevice == AudioDevice.NONE
|
||||
|| userSelectedAudioDevice == AudioDevice.BLUETOOTH);
|
||||
|
||||
// Need to stop Bluetooth audio if user selected different device and
|
||||
// Bluetooth SCO connection is established or in the process.
|
||||
boolean needBluetoothAudioStop =
|
||||
(bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING)
|
||||
&& (userSelectedAudioDevice != AudioDevice.NONE
|
||||
&& userSelectedAudioDevice != AudioDevice.BLUETOOTH);
|
||||
(bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING)
|
||||
&& (userSelectedAudioDevice != AudioDevice.NONE
|
||||
&& userSelectedAudioDevice != AudioDevice.BLUETOOTH);
|
||||
|
||||
if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED) {
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING
|
||||
|| bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED) {
|
||||
Log.d(TAG, "Need BT audio: start=" + needBluetoothAudioStart + ", "
|
||||
+ "stop=" + needBluetoothAudioStop + ", "
|
||||
+ "BT state=" + bluetoothManager.getState());
|
||||
+ "stop=" + needBluetoothAudioStop + ", "
|
||||
+ "BT state=" + bluetoothManager.getState());
|
||||
}
|
||||
|
||||
// Start or stop Bluetooth SCO connection given states set earlier.
|
||||
@ -577,8 +483,8 @@ public class MagicAudioManager {
|
||||
|
||||
// Attempt to start Bluetooth SCO audio (takes a few second to start).
|
||||
if (needBluetoothAudioStart &&
|
||||
!needBluetoothAudioStop &&
|
||||
!bluetoothManager.startScoAudio()) {
|
||||
!needBluetoothAudioStop &&
|
||||
!bluetoothManager.startScoAudio()) {
|
||||
// Remove BLUETOOTH from list of available devices since SCO failed.
|
||||
audioDevices.remove(AudioDevice.BLUETOOTH);
|
||||
audioDeviceSetUpdated = true;
|
||||
@ -586,42 +492,41 @@ public class MagicAudioManager {
|
||||
|
||||
|
||||
// Update selected audio device.
|
||||
AudioDevice newAudioDevice = selectedAudioDevice;
|
||||
AudioDevice newCurrentAudioDevice;
|
||||
|
||||
if (bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED) {
|
||||
// If a Bluetooth is connected, then it should be used as output audio
|
||||
// device. Note that it is not sufficient that a headset is available;
|
||||
// an active SCO channel must also be up and running.
|
||||
newAudioDevice = AudioDevice.BLUETOOTH;
|
||||
newCurrentAudioDevice = AudioDevice.BLUETOOTH;
|
||||
} else if (hasWiredHeadset) {
|
||||
// If a wired headset is connected, but Bluetooth is not, then wired headset is used as
|
||||
// audio device.
|
||||
newAudioDevice = AudioDevice.WIRED_HEADSET;
|
||||
newCurrentAudioDevice = AudioDevice.WIRED_HEADSET;
|
||||
} else {
|
||||
// No wired headset and no Bluetooth, hence the audio-device list can contain speaker
|
||||
// phone (on a tablet), or speaker phone and earpiece (on mobile phone).
|
||||
// |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or AudioDevice.EARPIECE
|
||||
// depending on the user's selection.
|
||||
newAudioDevice = defaultAudioDevice;
|
||||
newCurrentAudioDevice = userSelectedAudioDevice;
|
||||
}
|
||||
// Switch to new device but only if there has been any changes.
|
||||
if (newAudioDevice != selectedAudioDevice || audioDeviceSetUpdated) {
|
||||
if (newCurrentAudioDevice != currentAudioDevice || audioDeviceSetUpdated) {
|
||||
// Do the required device switch.
|
||||
setAudioDeviceInternal(newAudioDevice);
|
||||
setAudioDeviceInternal(newCurrentAudioDevice);
|
||||
Log.d(TAG, "New device status: "
|
||||
+ "available=" + audioDevices + ", "
|
||||
+ "selected=" + newAudioDevice);
|
||||
if (audioManagerEvents != null) {
|
||||
+ "available=" + audioDevices + ", "
|
||||
+ "current(new)=" + newCurrentAudioDevice);
|
||||
if (audioManagerListener != null) {
|
||||
// Notify a listening client that audio device has been changed.
|
||||
audioManagerEvents.onAudioDeviceChanged(selectedAudioDevice, audioDevices);
|
||||
audioManagerListener.onAudioDeviceChanged(currentAudioDevice, audioDevices);
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "--- updateAudioDeviceState done");
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioDevice is the names of possible audio devices that we currently
|
||||
* support.
|
||||
* AudioDevice is the names of possible audio devices that we currently support.
|
||||
*/
|
||||
public enum AudioDevice {
|
||||
SPEAKER_PHONE, WIRED_HEADSET, EARPIECE, BLUETOOTH, NONE
|
||||
@ -639,10 +544,10 @@ public class MagicAudioManager {
|
||||
/**
|
||||
* Selected audio device change event.
|
||||
*/
|
||||
public static interface AudioManagerEvents {
|
||||
public static interface AudioManagerListener {
|
||||
// Callback fired once audio device is changed or list of available audio devices changed.
|
||||
void onAudioDeviceChanged(
|
||||
AudioDevice selectedAudioDevice, Set<AudioDevice> availableAudioDevices);
|
||||
AudioDevice selectedAudioDevice, Set<AudioDevice> availableAudioDevices);
|
||||
}
|
||||
|
||||
/* Receiver which handles changes in wired headset availability. */
|
||||
|
@ -1,26 +0,0 @@
|
||||
<!--
|
||||
@author Google LLC
|
||||
Copyright (C) 2021 Google LLC
|
||||
|
||||
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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M2,12.5C2,9.46 4.46,7 7.5,7H18c2.21,0 4,1.79 4,4s-1.79,4 -4,4H9.5C8.12,15 7,13.88 7,12.5S8.12,10 9.5,10H17v2H9.41c-0.55,0 -0.55,1 0,1H18c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2H7.5C5.57,9 4,10.57 4,12.5S5.57,16 7.5,16H17v2H7.5C4.46,18 2,15.54 2,12.5z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_baseline_bluetooth_audio_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_bluetooth_audio_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82 -0.16,-1.59 -0.43,-2.31l-2.33,2.32zM19.53,6.71l-1.26,1.26c0.63,1.21 0.98,2.57 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89 -0.55,-3.67 -1.48,-5.19zM15.71,7.71L10,2L9,2v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41L9,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59L11,5.83zM12.88,16.29L11,18.17v-3.76l1.88,1.88z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_baseline_headset_mic_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_headset_mic_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h4v1h-7v2h6c1.66,0 3,-1.34 3,-3V10c0,-4.97 -4.03,-9 -9,-9z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_baseline_phone_in_talk_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_phone_in_talk_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1L4,3c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM19,12h2c0,-4.97 -4.03,-9 -9,-9v2c3.87,0 7,3.13 7,7zM15,12h2c0,-2.76 -2.24,-5 -5,-5v2c1.66,0 3,1.34 3,3z"/>
|
||||
</vector>
|
@ -140,59 +140,66 @@
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="5">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/pictureInPictureButton"
|
||||
android:layout_width="60dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:elevation="10dp"
|
||||
app:backgroundImage="@color/call_buttons_background"
|
||||
app:placeholderImage="@drawable/ic_baseline_picture_in_picture_alt_24"
|
||||
app:roundAsCircle="true" />
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/speakerButton"
|
||||
android:layout_width="60dp"
|
||||
android:id="@+id/audioOutputButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:backgroundImage="@color/call_buttons_background"
|
||||
app:placeholderImage="@drawable/ic_volume_mute_white_24dp"
|
||||
app:roundAsCircle="true" />
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/cameraButton"
|
||||
android:layout_width="60dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:alpha="0.7"
|
||||
app:backgroundImage="@color/call_buttons_background"
|
||||
app:placeholderImage="@drawable/ic_videocam_white_24px"
|
||||
app:roundAsCircle="true" />
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/microphoneButton"
|
||||
android:layout_width="60dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:alpha="0.7"
|
||||
app:backgroundImage="@color/call_buttons_background"
|
||||
app:placeholderImage="@drawable/ic_mic_off_white_24px"
|
||||
app:roundAsCircle="true" />
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/hangupButton"
|
||||
android:layout_width="60dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:backgroundImage="@color/nc_darkRed"
|
||||
app:placeholderImage="@drawable/ic_call_end_white_24px"
|
||||
app:roundAsCircle="true" />
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
@ -27,13 +27,15 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/bg_bottom_sheet"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/standard_padding">
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingEnd="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/upload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="@string/nc_add_file"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/medium_emphasis_text"
|
||||
@ -42,13 +44,10 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_contact"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingRight="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
@ -64,7 +63,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:text="@string/nc_share_contact"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
@ -75,13 +75,10 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_share_location"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingRight="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
@ -97,7 +94,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:text="@string/nc_share_location"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
@ -108,13 +106,10 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_picture_from_cam"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingRight="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
@ -130,7 +125,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:text="@string/nc_upload_picture_from_cam"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
@ -141,13 +137,10 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_file_from_local"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingRight="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
@ -163,7 +156,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:text="@string/nc_upload_local_file"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
@ -174,13 +168,10 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_file_from_cloud"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingRight="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_padding"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
@ -196,7 +187,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="@dimen/bottom_sheet_text_size"
|
||||
|
165
app/src/main/res/layout/dialog_audio_output.xml
Normal file
165
app/src/main/res/layout/dialog_audio_output.xml
Normal file
@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Marcel Hibbe
|
||||
~ Copyright (C) 2022 Marcel Hibbe <marcel.hibbe@nextcloud.com>
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/bg_call_screen_dialog"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingEnd="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/upload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="@string/audio_output_dialog_headline"
|
||||
android:textColor="@color/medium_emphasis_text_dark_background"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/audio_output_bluetooth"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/audio_output_bluetooth_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_baseline_bluetooth_audio_24"
|
||||
app:tint="@color/grey_600" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/audio_output_bluetooth_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:text="@string/audio_output_bluetooth"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text_dark_background"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/audio_output_speaker"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/audio_output_speaker_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_volume_up_white_24dp"
|
||||
app:tint="@color/grey_600" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/audio_output_speaker_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:text="@string/audio_output_speaker"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text_dark_background"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/audio_output_earspeaker"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/audio_output_earspeaker_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_baseline_phone_in_talk_24"
|
||||
app:tint="@color/grey_600" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/audio_output_earspeaker_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:text="@string/audio_output_phone"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text_dark_background"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/audio_output_wired_headset"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/audio_output_wired_headset_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_baseline_headset_mic_24"
|
||||
app:tint="@color/grey_600" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/audio_output_wired_headset_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:text="@string/audio_output_wired_headset"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text_dark_background"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -2,6 +2,8 @@
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ @author Andy Scherzinger
|
||||
~ Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
~ Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
@ -18,27 +20,36 @@
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/MD_ListItem">
|
||||
<RelativeLayout
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="56dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="center"
|
||||
android:layout_centerVertical="true"
|
||||
tools:src="@drawable/ic_delete_grey600_24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:scaleType="center"
|
||||
app:tint="@color/grey_600"
|
||||
tools:ignore="ContentDescription"
|
||||
/>
|
||||
tools:src="@drawable/ic_delete_grey600_24dp" />
|
||||
|
||||
<com.afollestad.materialdialogs.internal.rtl.RtlTextView
|
||||
android:id="@+id/title"
|
||||
tools:text="Item"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
style="@style/MD_ListItemText" />
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/standard_padding"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="@dimen/bottom_sheet_text_size"
|
||||
tools:text="Menu item" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
@ -24,7 +24,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/bg_default">
|
||||
android:background="@color/bg_default"
|
||||
android:minHeight="@dimen/bottom_sheet_item_height">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/menu_text"
|
||||
@ -35,9 +36,9 @@
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/conversation_item_header"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="16sp"
|
||||
tools:drawablePadding="16dp"
|
||||
tools:drawablePadding="32dp"
|
||||
tools:drawableStart="@drawable/ic_add_grey600_24px"
|
||||
tools:text="Start a new conversation" />
|
||||
</RelativeLayout>
|
||||
|
@ -40,6 +40,10 @@
|
||||
<color name="medium_emphasis_text">#99000000</color>
|
||||
<color name="low_emphasis_text">#61000000</color>
|
||||
|
||||
<!-- general text colors for dark background -->
|
||||
<color name="high_emphasis_text_dark_background">#deffffff</color>
|
||||
<color name="medium_emphasis_text_dark_background">#99ffffff</color>
|
||||
|
||||
<!-- Text color of sent messages -->
|
||||
<color name="nc_outcoming_text_default">#FFFFFF</color>
|
||||
<!-- Text color of received messages -->
|
||||
@ -78,6 +82,8 @@
|
||||
<color name="bg_message_list_outcoming_bubble_deleted">#800082C9</color>
|
||||
|
||||
<color name="bg_bottom_sheet">#46ffffff</color>
|
||||
<color name="bg_call_screen_dialog">#121212</color>
|
||||
<color name="call_screen_text">#ffffffff</color>
|
||||
|
||||
<color name="call_buttons_background">#BF999999</color>
|
||||
<color name="favorite_icon_tint">#FFCC00</color>
|
||||
|
@ -23,6 +23,7 @@
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
|
||||
<dimen name="item_height">72dp</dimen>
|
||||
<dimen name="bottom_sheet_item_height">56dp</dimen>
|
||||
<dimen name="small_item_height">48dp</dimen>
|
||||
|
||||
<dimen name="min_size_clickable_area">48dp</dimen>
|
||||
|
@ -494,5 +494,10 @@
|
||||
<string name="take_photo_send">Send</string>
|
||||
<string name="take_photo_error_deleting_picture">Error taking picture</string>
|
||||
<string name="take_photo_permission">Taking a photo is not possible without permissions</string>
|
||||
<string name="audio_output_bluetooth">Bluetooth</string>
|
||||
<string name="audio_output_speaker">Speaker</string>
|
||||
<string name="audio_output_phone">Phone</string>
|
||||
<string name="audio_output_dialog_headline">Audio output</string>
|
||||
<string name="audio_output_wired_headset">Wired headset</string>
|
||||
|
||||
</resources>
|
||||
|
@ -39,7 +39,7 @@ buildscript {
|
||||
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
|
||||
classpath 'gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.7.5'
|
||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.18.1"
|
||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.19.0"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
@ -1 +1 @@
|
||||
554
|
||||
552
|
@ -1,2 +1,2 @@
|
||||
DO NOT TOUCH; GENERATED BY DRONE
|
||||
<span class="mdl-layout-title">Lint Report: 1 error and 222 warnings</span>
|
||||
<span class="mdl-layout-title">Lint Report: 1 error and 223 warnings</span>
|
||||
|
Loading…
Reference in New Issue
Block a user