mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-13 15:54:59 +01:00
Work on webrtc
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
f26c38444b
commit
d3e603a5ce
@ -138,6 +138,8 @@ dependencies {
|
|||||||
implementation 'com.yarolegovich:lovelyinput:1.0.2'
|
implementation 'com.yarolegovich:lovelyinput:1.0.2'
|
||||||
implementation 'com.yarolegovich:mp:1.0.8'
|
implementation 'com.yarolegovich:mp:1.0.8'
|
||||||
|
|
||||||
|
compile 'gun0912.ted:tedpermission:2.0.3'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation ('com.android.support.test.espresso:espresso-core:3.0.1', {
|
androidTestImplementation ('com.android.support.test.espresso:espresso-core:3.0.1', {
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
exclude group: 'com.android.support', module: 'support-annotations'
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera.any"/>
|
<uses-feature android:name="android.hardware.camera.any"/>
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus"/>
|
<uses-feature android:name="android.hardware.camera.autofocus"/>
|
||||||
|
<uses-feature android:name="android.hardware.camera2"/>
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:glEsVersion="0x00020000"
|
android:glEsVersion="0x00020000"
|
||||||
android:required="true"/>
|
android:required="true"/>
|
||||||
|
@ -16,13 +16,19 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Inspired by:
|
||||||
|
* - Google samples
|
||||||
|
* - https://github.com/vivek1794/webrtc-android-codelab (MIT licence)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.nextcloud.talk.activities;
|
package com.nextcloud.talk.activities;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.TypedValue;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@ -38,6 +44,7 @@ import org.webrtc.AudioSource;
|
|||||||
import org.webrtc.AudioTrack;
|
import org.webrtc.AudioTrack;
|
||||||
import org.webrtc.Camera1Enumerator;
|
import org.webrtc.Camera1Enumerator;
|
||||||
import org.webrtc.CameraEnumerator;
|
import org.webrtc.CameraEnumerator;
|
||||||
|
import org.webrtc.EglBase;
|
||||||
import org.webrtc.IceCandidate;
|
import org.webrtc.IceCandidate;
|
||||||
import org.webrtc.Logging;
|
import org.webrtc.Logging;
|
||||||
import org.webrtc.MediaConstraints;
|
import org.webrtc.MediaConstraints;
|
||||||
@ -79,6 +86,7 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
VideoTrack localVideoTrack;
|
VideoTrack localVideoTrack;
|
||||||
AudioSource audioSource;
|
AudioSource audioSource;
|
||||||
AudioTrack localAudioTrack;
|
AudioTrack localAudioTrack;
|
||||||
|
VideoCapturer videoCapturer;
|
||||||
|
|
||||||
VideoRenderer localRenderer;
|
VideoRenderer localRenderer;
|
||||||
VideoRenderer remoteRenderer;
|
VideoRenderer remoteRenderer;
|
||||||
@ -116,6 +124,8 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
initViews();
|
||||||
|
|
||||||
TedPermission.with(this)
|
TedPermission.with(this)
|
||||||
.setPermissionListener(permissionlistener)
|
.setPermissionListener(permissionlistener)
|
||||||
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
|
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
|
||||||
@ -123,13 +133,13 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.ACCESS_NETWORK_STATE,
|
Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.ACCESS_NETWORK_STATE,
|
||||||
Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.INTERNET)
|
Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.INTERNET)
|
||||||
.check();
|
.check();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private VideoCapturer createVideoCapturer() {
|
private VideoCapturer createVideoCapturer() {
|
||||||
VideoCapturer videoCapturer;
|
|
||||||
videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
|
videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
|
||||||
return videoCapturer;
|
return videoCapturer;
|
||||||
}
|
}
|
||||||
@ -166,6 +176,16 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void initViews() {
|
||||||
|
pipVideoView.setMirror(true);
|
||||||
|
fullScreenVideoView.setMirror(false);
|
||||||
|
EglBase rootEglBase = EglBase.create();
|
||||||
|
pipVideoView.init(rootEglBase.getEglBaseContext(), null);
|
||||||
|
pipVideoView.setZOrderMediaOverlay(true);
|
||||||
|
fullScreenVideoView.init(rootEglBase.getEglBaseContext(), null);
|
||||||
|
fullScreenVideoView.setZOrderMediaOverlay(true);
|
||||||
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
//Initialize PeerConnectionFactory globals.
|
//Initialize PeerConnectionFactory globals.
|
||||||
//Params are context, initAudio,initVideo and videoCodecHwAcceleration
|
//Params are context, initAudio,initVideo and videoCodecHwAcceleration
|
||||||
@ -191,12 +211,17 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
|
audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
|
||||||
localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
|
localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
|
||||||
|
|
||||||
|
Resources r = getResources();
|
||||||
|
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120, r.getDisplayMetrics());
|
||||||
|
videoCapturerAndroid.startCapture(px, px, 30);
|
||||||
|
|
||||||
//create a videoRenderer based on SurfaceViewRenderer instance
|
//create a videoRenderer based on SurfaceViewRenderer instance
|
||||||
localRenderer = new VideoRenderer(pipVideoView);
|
localRenderer = new VideoRenderer(pipVideoView);
|
||||||
// And finally, with our VideoRenderer ready, we
|
// And finally, with our VideoRenderer ready, we
|
||||||
// can add our renderer to the VideoTrack.
|
// can add our renderer to the VideoTrack.
|
||||||
localVideoTrack.addRenderer(localRenderer);
|
localVideoTrack.addRenderer(localRenderer);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -268,9 +293,9 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
localPeer.setRemoteDescription(new MagicSdpObserver(), sessionDescription);
|
localPeer.setRemoteDescription(new MagicSdpObserver(), sessionDescription);
|
||||||
|
|
||||||
}
|
}
|
||||||
},new MediaConstraints());
|
}, new MediaConstraints());
|
||||||
}
|
}
|
||||||
},sdpConstraints);
|
}, sdpConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -284,6 +309,13 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
remotePeer.close();
|
remotePeer.close();
|
||||||
remotePeer = null;
|
remotePeer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (videoCapturer != null) {
|
||||||
|
videoCapturer.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
pipVideoView.release();
|
||||||
|
fullScreenVideoView.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gotRemoteStream(MediaStream stream) {
|
private void gotRemoteStream(MediaStream stream) {
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Nextcloud Talk application
|
|
||||||
*
|
|
||||||
* @author Mario Danic
|
|
||||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.nextcloud.talk.controllers;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import com.nextcloud.talk.R;
|
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
|
||||||
import com.nextcloud.talk.controllers.base.RefWatchingController;
|
|
||||||
|
|
||||||
import org.webrtc.SurfaceViewRenderer;
|
|
||||||
|
|
||||||
import autodagger.AutoInjector;
|
|
||||||
import butterknife.BindView;
|
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication.class)
|
|
||||||
public class CallController extends RefWatchingController {
|
|
||||||
private static final String TAG = "CallController";
|
|
||||||
|
|
||||||
@BindView(R.id.fullscreen_video_view)
|
|
||||||
SurfaceViewRenderer fullScreenVideo;
|
|
||||||
|
|
||||||
@BindView(R.id.pip_video_view)
|
|
||||||
SurfaceViewRenderer pipVideoView;
|
|
||||||
|
|
||||||
private String roomToken;
|
|
||||||
private String userDisplayName;
|
|
||||||
|
|
||||||
public CallController(Bundle args) {
|
|
||||||
super(args);
|
|
||||||
this.roomToken = args.getString("roomToken", "");
|
|
||||||
this.userDisplayName = args.getString("userDisplayName");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
|
||||||
return inflater.inflate(R.layout.controller_call, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onViewBound(@NonNull View view) {
|
|
||||||
super.onViewBound(view);
|
|
||||||
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
|
public class DisplayHelper {
|
||||||
|
|
||||||
|
private static final String TAG = "DIsplayHelper";
|
||||||
|
|
||||||
|
public static float convertDpToPixel(float dp, Context context){
|
||||||
|
Resources resources = context.getResources();
|
||||||
|
DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||||
|
float px = dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
||||||
|
return px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.webrtc;
|
||||||
|
|
||||||
|
import org.webrtc.DataChannel;
|
||||||
|
import org.webrtc.IceCandidate;
|
||||||
|
import org.webrtc.MediaStream;
|
||||||
|
import org.webrtc.PeerConnection;
|
||||||
|
import org.webrtc.RtpReceiver;
|
||||||
|
|
||||||
|
|
||||||
|
public class MagicPeerConnectionObserver implements PeerConnection.Observer {
|
||||||
|
private static final String TAG = "MagicPeerConnectionObserver";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIceConnectionReceivingChange(boolean b) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIceCandidate(IceCandidate iceCandidate) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddStream(MediaStream mediaStream) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoveStream(MediaStream mediaStream) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDataChannel(DataChannel dataChannel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRenegotiationNeeded() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.webrtc;
|
||||||
|
|
||||||
|
import org.webrtc.SdpObserver;
|
||||||
|
import org.webrtc.SessionDescription;
|
||||||
|
|
||||||
|
public class MagicSdpObserver implements SdpObserver {
|
||||||
|
private static final String TAG = "MagicSdpObserver";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateSuccess(SessionDescription sessionDescription) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetSuccess() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateFailure(String s) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetFailure(String s) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -20,16 +20,23 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
tools:context=".activities.CallActivity">
|
tools:context=".activities.CallActivity">
|
||||||
|
|
||||||
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
|
<org.webrtc.SurfaceViewRenderer
|
||||||
android:id="@+id/controller_container"
|
android:id="@+id/pip_video_view"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_margin="16dp"/>
|
||||||
|
|
||||||
|
<org.webrtc.SurfaceViewRenderer
|
||||||
|
android:id="@+id/fullscreen_video_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"/>
|
||||||
/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Nextcloud Talk application
|
|
||||||
~
|
|
||||||
~ @author Mario Danic
|
|
||||||
~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<org.webrtc.SurfaceViewRenderer
|
|
||||||
android:id="@+id/fullscreen_video_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"/>
|
|
||||||
|
|
||||||
<org.webrtc.SurfaceViewRenderer
|
|
||||||
android:id="@+id/pip_video_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="144dp"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:layout_margin="16dp"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
Loading…
Reference in New Issue
Block a user