dummy Jingle activity
|
@ -10,6 +10,7 @@
|
|||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
@ -286,6 +287,7 @@
|
|||
<activity
|
||||
android:name=".ui.ChannelDiscoveryActivity"
|
||||
android:label="@string/discover_channels" />
|
||||
<activity android:name=".ui.RtpSessionActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -19,6 +19,7 @@ import eu.siacs.conversations.xml.Element;
|
|||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.xmpp.chatstate.ChatState;
|
||||
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
|
||||
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
|
||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||
import rocks.xmpp.addr.Jid;
|
||||
|
||||
|
@ -233,12 +234,14 @@ public class MessageGenerator extends AbstractGenerator {
|
|||
return packet;
|
||||
}
|
||||
|
||||
public MessagePacket sessionProposal(JingleConnectionManager.RtpSessionProposal proposal) {
|
||||
public MessagePacket sessionProposal(final JingleConnectionManager.RtpSessionProposal proposal) {
|
||||
final MessagePacket packet = new MessagePacket();
|
||||
packet.setTo(proposal.with);
|
||||
packet.setId(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX+proposal.sessionId);
|
||||
final Element propose = packet.addChild("propose", Namespace.JINGLE_MESSAGE);
|
||||
propose.setAttribute("id", proposal.sessionId);
|
||||
propose.addChild("description", Namespace.JINGLE_APPS_RTP);
|
||||
packet.addChild("request", "urn:xmpp:receipts");
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ import eu.siacs.conversations.xml.Element;
|
|||
import eu.siacs.conversations.xmpp.InvalidJid;
|
||||
import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
|
||||
import eu.siacs.conversations.xmpp.chatstate.ChatState;
|
||||
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
|
||||
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
|
||||
import eu.siacs.conversations.xmpp.pep.Avatar;
|
||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||
import rocks.xmpp.addr.Jid;
|
||||
|
@ -301,11 +303,18 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
|
||||
private boolean handleErrorMessage(Account account, MessagePacket packet) {
|
||||
if (packet.getType() == MessagePacket.TYPE_ERROR) {
|
||||
Jid from = packet.getFrom();
|
||||
if (from != null) {
|
||||
final Jid from = packet.getFrom();
|
||||
final String id = packet.getId();
|
||||
if (from != null && id != null) {
|
||||
if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX)) {
|
||||
final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX.length());
|
||||
mXmppConnectionService.getJingleConnectionManager()
|
||||
.updateProposedSessionDiscovered(account, from, sessionId, JingleConnectionManager.DeviceDiscoveryState.FAILED);
|
||||
return true;
|
||||
}
|
||||
mXmppConnectionService.markMessage(account,
|
||||
from.asBareJid(),
|
||||
packet.getId(),
|
||||
id,
|
||||
Message.STATUS_SEND_FAILED,
|
||||
extractErrorMessage(packet));
|
||||
final Element error = packet.findChild("error");
|
||||
|
@ -815,7 +824,11 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
if (!isTypeGroupChat) {
|
||||
for (Element child : packet.getChildren()) {
|
||||
if (Namespace.JINGLE_MESSAGE.equals(child.getNamespace()) && JINGLE_MESSAGE_ELEMENT_NAMES.contains(child.getName())) {
|
||||
if (!account.getJid().asBareJid().equals(from.asBareJid())) {
|
||||
processMessageReceipts(account, packet, query);
|
||||
}
|
||||
mXmppConnectionService.getJingleConnectionManager().deliverMessage(account, packet.getTo(), packet.getFrom(), child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -831,8 +844,14 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
if (query != null && id != null && packet.getTo() != null) {
|
||||
query.removePendingReceiptRequest(new ReceiptRequest(packet.getTo(), id));
|
||||
}
|
||||
} else {
|
||||
mXmppConnectionService.markMessage(account, from.asBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED);
|
||||
} else if (id != null) {
|
||||
if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX)) {
|
||||
final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX.length());
|
||||
mXmppConnectionService.getJingleConnectionManager()
|
||||
.updateProposedSessionDiscovered(account, from, sessionId, JingleConnectionManager.DeviceDiscoveryState.DISCOVERED);
|
||||
} else {
|
||||
mXmppConnectionService.markMessage(account, from.asBareJid(), id, Message.STATUS_SEND_RECEIVED);
|
||||
}
|
||||
}
|
||||
}
|
||||
Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0");
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package eu.siacs.conversations.ui;
|
||||
|
||||
import android.databinding.DataBindingUtil;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.databinding.ActivityRtpSessionBinding;
|
||||
|
||||
public class RtpSessionActivity extends XmppActivity {
|
||||
|
||||
public static final String EXTRA_WITH = "with";
|
||||
|
||||
private ActivityRtpSessionBinding binding;
|
||||
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
getWindow().addFlags(
|
||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
|
||||
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
super.onCreate(savedInstanceState);
|
||||
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
|
||||
this.binding.acceptCall.setOnClickListener(this::acceptCall);
|
||||
this.binding.rejectCall.setOnClickListener(this::rejectCall);
|
||||
}
|
||||
|
||||
private void rejectCall(View view) {
|
||||
|
||||
}
|
||||
|
||||
private void acceptCall(View view) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshUiReal() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void onBackendConnected() {
|
||||
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ import rocks.xmpp.addr.Jid;
|
|||
|
||||
public abstract class AbstractJingleConnection {
|
||||
|
||||
public static final String JINGLE_MESSAGE_ID_PREFIX = "jm-propose-";
|
||||
|
||||
protected final JingleConnectionManager jingleConnectionManager;
|
||||
protected final XmppConnectionService xmppConnectionService;
|
||||
protected final Id id;
|
||||
|
|
|
@ -30,7 +30,7 @@ import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
|||
import rocks.xmpp.addr.Jid;
|
||||
|
||||
public class JingleConnectionManager extends AbstractConnectionManager {
|
||||
private final Set<RtpSessionProposal> rtpSessionProposals = new HashSet<>();
|
||||
private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals = new HashMap<>();
|
||||
private final Map<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>();
|
||||
|
||||
private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
|
||||
|
@ -108,7 +108,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
}
|
||||
final RtpSessionProposal proposal = new RtpSessionProposal(account, with.asBareJid(), sessionId);
|
||||
synchronized (rtpSessionProposals) {
|
||||
if (rtpSessionProposals.remove(proposal)) {
|
||||
if (rtpSessionProposals.remove(proposal) != null) {
|
||||
final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, account.getJid());
|
||||
this.connections.put(id, rtpConnection);
|
||||
rtpConnection.transitionOrThrow(AbstractJingleConnection.State.PROPOSED);
|
||||
|
@ -190,7 +190,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
public void proposeJingleRtpSession(final Account account, final Contact contact) {
|
||||
final RtpSessionProposal proposal = RtpSessionProposal.of(account, contact.getJid().asBareJid());
|
||||
synchronized (this.rtpSessionProposals) {
|
||||
this.rtpSessionProposals.add(proposal);
|
||||
this.rtpSessionProposals.put(proposal, DeviceDiscoveryState.SEARCHING);
|
||||
final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionProposal(proposal);
|
||||
Log.d(Config.LOGTAG,messagePacket.toString());
|
||||
mXmppConnectionService.sendMessagePacket(account, messagePacket);
|
||||
|
@ -244,6 +244,23 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void updateProposedSessionDiscovered(Account account, Jid from, String sessionId, final DeviceDiscoveryState target) {
|
||||
final RtpSessionProposal sessionProposal = new RtpSessionProposal(account,from.asBareJid(),sessionId);
|
||||
synchronized (this.rtpSessionProposals) {
|
||||
final DeviceDiscoveryState currentState = rtpSessionProposals.get(sessionProposal);
|
||||
if (currentState == null) {
|
||||
Log.d(Config.LOGTAG,"unable to find session proposal for session id "+sessionId);
|
||||
return;
|
||||
}
|
||||
if (currentState == DeviceDiscoveryState.DISCOVERED) {
|
||||
Log.d(Config.LOGTAG,"session proposal already at discovered. not going to fall back");
|
||||
return;
|
||||
}
|
||||
this.rtpSessionProposals.put(sessionProposal, target);
|
||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": flagging session "+sessionId+" as "+target);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RtpSessionProposal {
|
||||
private final Account account;
|
||||
public final Jid with;
|
||||
|
@ -274,4 +291,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
return Objects.hashCode(account.getJid(), with, sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DeviceDiscoveryState {
|
||||
SEARCHING, DISCOVERED, FAILED
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package eu.siacs.conversations.xmpp.jingle;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
@ -15,6 +16,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.ui.RtpSessionActivity;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
||||
|
@ -217,13 +219,21 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
if (originatedFromMyself) {
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": saw proposal from mysql. ignoring");
|
||||
} else if (transition(State.PROPOSED)) {
|
||||
//TODO start ringing or something
|
||||
pickUpCall();
|
||||
startRinging();
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, id.account.getJid() + ": ignoring session proposal because already in " + state);
|
||||
}
|
||||
}
|
||||
|
||||
private void startRinging() {
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received call from " + id.with + ". start ringing");
|
||||
final Intent intent = new Intent(xmppConnectionService, RtpSessionActivity.class);
|
||||
intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString());
|
||||
intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString());
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
xmppConnectionService.startActivity(intent);
|
||||
}
|
||||
|
||||
private void receiveProceed(final Jid from, final Element proceed) {
|
||||
if (from.equals(id.with)) {
|
||||
if (isInitiator()) {
|
||||
|
|
|
@ -11,6 +11,9 @@ import com.google.common.util.concurrent.SettableFuture;
|
|||
|
||||
import org.webrtc.AudioSource;
|
||||
import org.webrtc.AudioTrack;
|
||||
import org.webrtc.Camera1Capturer;
|
||||
import org.webrtc.Camera1Enumerator;
|
||||
import org.webrtc.CameraVideoCapturer;
|
||||
import org.webrtc.DataChannel;
|
||||
import org.webrtc.IceCandidate;
|
||||
import org.webrtc.MediaConstraints;
|
||||
|
@ -20,6 +23,9 @@ import org.webrtc.PeerConnectionFactory;
|
|||
import org.webrtc.RtpReceiver;
|
||||
import org.webrtc.SdpObserver;
|
||||
import org.webrtc.SessionDescription;
|
||||
import org.webrtc.VideoCapturer;
|
||||
import org.webrtc.VideoSource;
|
||||
import org.webrtc.VideoTrack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -30,6 +36,9 @@ import eu.siacs.conversations.Config;
|
|||
|
||||
public class WebRTCWrapper {
|
||||
|
||||
private VideoTrack localVideoTrack = null;
|
||||
private VideoTrack remoteVideoTrack = null;
|
||||
|
||||
private final EventCallback eventCallback;
|
||||
|
||||
private final PeerConnection.Observer peerConnectionObserver = new PeerConnection.Observer() {
|
||||
|
@ -75,6 +84,11 @@ public class WebRTCWrapper {
|
|||
for(AudioTrack audioTrack : mediaStream.audioTracks) {
|
||||
Log.d(Config.LOGTAG,"remote? - audioTrack enabled:"+audioTrack.enabled()+" state="+audioTrack.state());
|
||||
}
|
||||
final List<VideoTrack> videoTracks = mediaStream.videoTracks;
|
||||
if (videoTracks.size() > 0) {
|
||||
Log.d(Config.LOGTAG, "more than zero remote video tracks found. using first");
|
||||
remoteVideoTrack = videoTracks.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,15 +126,65 @@ public class WebRTCWrapper {
|
|||
}
|
||||
|
||||
public void initializePeerConnection() {
|
||||
final PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
|
||||
PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory();
|
||||
|
||||
CameraVideoCapturer capturer = null;
|
||||
Camera1Enumerator camera1Enumerator = new Camera1Enumerator();
|
||||
for(String deviceName : camera1Enumerator.getDeviceNames()) {
|
||||
Log.d(Config.LOGTAG,"camera device name: "+deviceName);
|
||||
if (camera1Enumerator.isFrontFacing(deviceName)) {
|
||||
capturer = camera1Enumerator.createCapturer(deviceName, new CameraVideoCapturer.CameraEventsHandler() {
|
||||
@Override
|
||||
public void onCameraError(String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraDisconnected() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraFreezed(String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraOpening(String s) {
|
||||
Log.d(Config.LOGTAG,"onCameraOpening");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstFrameAvailable() {
|
||||
Log.d(Config.LOGTAG,"onFirstFrameAvailable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraClosed() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*if (capturer != null) {
|
||||
capturer.initialize();
|
||||
Log.d(Config.LOGTAG,"start capturing");
|
||||
capturer.startCapture(800,600,30);
|
||||
}*/
|
||||
|
||||
final VideoSource videoSource = peerConnectionFactory.createVideoSource(false);
|
||||
final VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("my-video-track", videoSource);
|
||||
|
||||
final AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
|
||||
|
||||
final AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("my-audio-track", audioSource);
|
||||
Log.d(Config.LOGTAG,"audioTrack enabled:"+audioTrack.enabled()+" state="+audioTrack.state());
|
||||
final MediaStream stream = peerConnectionFactory.createLocalMediaStream("my-media-stream");
|
||||
stream.addTrack(audioTrack);
|
||||
//stream.addTrack(videoTrack);
|
||||
|
||||
this.localVideoTrack = videoTrack;
|
||||
|
||||
|
||||
final List<PeerConnection.IceServer> iceServers = ImmutableList.of(
|
||||
|
@ -136,6 +200,8 @@ public class WebRTCWrapper {
|
|||
this.peerConnection = peerConnection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ListenableFuture<SessionDescription> createOffer() {
|
||||
return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
|
||||
final SettableFuture<SessionDescription> future = SettableFuture.create();
|
||||
|
|
BIN
src/main/res/drawable-hdpi/ic_call_end_white_48dp.png
Normal file
After Width: | Height: | Size: 553 B |
BIN
src/main/res/drawable-hdpi/ic_call_white_48dp.png
Normal file
After Width: | Height: | Size: 597 B |
BIN
src/main/res/drawable-mdpi/ic_call_end_white_48dp.png
Normal file
After Width: | Height: | Size: 389 B |
BIN
src/main/res/drawable-mdpi/ic_call_white_48dp.png
Normal file
After Width: | Height: | Size: 420 B |
BIN
src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png
Normal file
After Width: | Height: | Size: 712 B |
BIN
src/main/res/drawable-xhdpi/ic_call_white_48dp.png
Normal file
After Width: | Height: | Size: 778 B |
BIN
src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
src/main/res/drawable-xxhdpi/ic_call_white_48dp.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_call_white_48dp.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
75
src/main/res/layout/activity_rtp_session.xml
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:background="?colorPrimary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="32dp"
|
||||
android:paddingBottom="32dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical">
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
tools:text="Incoming call"
|
||||
android:textColor="@color/white"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Conversations.Title"/>
|
||||
<TextView
|
||||
android:id="@+id/with"
|
||||
android:textColor="@color/white"
|
||||
android:layout_below="@id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Conversations.Display2"
|
||||
tools:text="Juliet Capulet"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="288dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/reject_call"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_call_end_white_48dp"
|
||||
app:backgroundTint="@color/red700"
|
||||
app:elevation="4dp"
|
||||
app:fabCustomSize="72dp"
|
||||
app:maxImageSize="36dp" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/accept_call"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_call_white_48dp"
|
||||
app:backgroundTint="@color/green700"
|
||||
app:elevation="4dp"
|
||||
app:fabCustomSize="72dp"
|
||||
app:maxImageSize="36dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</layout>
|
|
@ -19,6 +19,7 @@
|
|||
<color name="grey800">#ff424242</color>
|
||||
<color name="grey900">#ff282828</color>
|
||||
<color name="red500">#fff44336</color>
|
||||
<color name="red700">#ffD32F2F</color>
|
||||
<color name="red_a700">#ffd50000</color>
|
||||
<color name="red_a100">#ffff8a80</color>
|
||||
<color name="red800">#ffc62828</color>
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<style name="TextAppearance.Conversations.Display2.Monospace" parent="TextAppearance.AppCompat.Display2">
|
||||
<item name="android:textSize">?TextSizeDisplay2</item>
|
||||
<style name="TextAppearance.Conversations.Display2.Monospace" parent="TextAppearance.Conversations.Display2">
|
||||
<item name="android:fontFamily" tools:targetApi="jelly_bean">monospace</item>
|
||||
<item name="android:typeface">monospace</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Conversations.Display2" parent="TextAppearance.AppCompat.Display2">
|
||||
<item name="android:textSize">?TextSizeDisplay2</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Conversations.Title" parent="TextAppearance.AppCompat.Title">
|
||||
<item name="android:textSize">?TextSizeTitle</item>
|
||||
</style>
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
<item name="TextSizeBody2">14sp</item>
|
||||
<item name="TextSizeSubhead">16sp</item>
|
||||
<item name="TextSizeTitle">20sp</item>
|
||||
<item name="TextSizeDisplay2">45sp</item>
|
||||
<item name="TextSizeInput">16sp</item>
|
||||
<item name="TextSeparation">5sp</item>
|
||||
<item name="IconSize">18sp</item>
|
||||
|
|