mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
Much progress
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
56aeb30923
commit
15586bc0cc
229
.idea/codeStyleSettings.xml
Normal file
229
.idea/codeStyleSettings.xml
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectCodeStyleSettingsManager">
|
||||||
|
<option name="PER_PROJECT_SETTINGS">
|
||||||
|
<value>
|
||||||
|
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||||
|
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||||
|
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||||
|
<value />
|
||||||
|
</option>
|
||||||
|
<option name="IMPORT_LAYOUT_TABLE">
|
||||||
|
<value>
|
||||||
|
<package name="android" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="com" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="junit" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="net" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="org" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="java" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="javax" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="" withSubpackages="true" static="true" />
|
||||||
|
<emptyLine />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="RIGHT_MARGIN" value="100" />
|
||||||
|
<AndroidXmlCodeStyleSettings>
|
||||||
|
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
||||||
|
</AndroidXmlCodeStyleSettings>
|
||||||
|
<Objective-C-extensions>
|
||||||
|
<file>
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||||
|
</file>
|
||||||
|
<class>
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||||
|
</class>
|
||||||
|
<extensions>
|
||||||
|
<pair source="cpp" header="h" />
|
||||||
|
<pair source="c" header="h" />
|
||||||
|
</extensions>
|
||||||
|
</Objective-C-extensions>
|
||||||
|
<XML>
|
||||||
|
<option name="XML_KEEP_LINE_BREAKS" value="false" />
|
||||||
|
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
|
||||||
|
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||||
|
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||||
|
</XML>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<rules>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:android</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:.*</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:layout_width</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:layout_height</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:layout_.*</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:width</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:height</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -77,8 +77,8 @@ dependencies {
|
|||||||
implementation 'com.bluelinelabs:conductor-support:2.1.4'
|
implementation 'com.bluelinelabs:conductor-support:2.1.4'
|
||||||
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
|
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
|
||||||
implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.6.0'
|
implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.9.0'
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'
|
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
|
||||||
|
|
||||||
implementation 'com.bluelinelabs:logansquare:1.3.7'
|
implementation 'com.bluelinelabs:logansquare:1.3.7'
|
||||||
annotationProcessor 'com.bluelinelabs:logansquare-compiler:1.3.7'
|
annotationProcessor 'com.bluelinelabs:logansquare-compiler:1.3.7'
|
||||||
|
@ -29,21 +29,27 @@ import android.content.res.Resources;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
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;
|
||||||
|
|
||||||
|
import com.bluelinelabs.logansquare.LoganSquare;
|
||||||
import com.nextcloud.talk.R;
|
import com.nextcloud.talk.R;
|
||||||
import com.nextcloud.talk.api.NcApi;
|
import com.nextcloud.talk.api.NcApi;
|
||||||
import com.nextcloud.talk.api.helpers.api.ApiHelper;
|
import com.nextcloud.talk.api.helpers.api.ApiHelper;
|
||||||
|
import com.nextcloud.talk.api.models.json.call.CallOverall;
|
||||||
|
import com.nextcloud.talk.api.models.json.generic.GenericOverall;
|
||||||
|
import com.nextcloud.talk.api.models.json.participants.Participant;
|
||||||
|
import com.nextcloud.talk.api.models.json.signaling.NCMessageWrapper;
|
||||||
|
import com.nextcloud.talk.api.models.json.signaling.Signaling;
|
||||||
import com.nextcloud.talk.api.models.json.signaling.SignalingOverall;
|
import com.nextcloud.talk.api.models.json.signaling.SignalingOverall;
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||||
import com.nextcloud.talk.persistence.entities.UserEntity;
|
import com.nextcloud.talk.persistence.entities.UserEntity;
|
||||||
import com.nextcloud.talk.webrtc.MagicPeerConnectionObserver;
|
import com.nextcloud.talk.webrtc.MagicPeerConnectionObserver;
|
||||||
import com.nextcloud.talk.webrtc.MagicSdpObserver;
|
import com.nextcloud.talk.webrtc.MagicSdpObserver;
|
||||||
|
|
||||||
import org.parceler.Parcels;
|
|
||||||
import org.webrtc.AudioSource;
|
import org.webrtc.AudioSource;
|
||||||
import org.webrtc.AudioTrack;
|
import org.webrtc.AudioTrack;
|
||||||
import org.webrtc.Camera1Enumerator;
|
import org.webrtc.Camera1Enumerator;
|
||||||
@ -67,7 +73,7 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import io.reactivex.functions.BooleanSupplier;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import autodagger.AutoInjector;
|
import autodagger.AutoInjector;
|
||||||
@ -76,6 +82,7 @@ import butterknife.ButterKnife;
|
|||||||
import io.reactivex.Observer;
|
import io.reactivex.Observer;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.functions.BooleanSupplier;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import ru.alexbykov.nopermission.PermissionHelper;
|
import ru.alexbykov.nopermission.PermissionHelper;
|
||||||
|
|
||||||
@ -91,10 +98,6 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
NcApi ncApi;
|
NcApi ncApi;
|
||||||
|
|
||||||
private String roomToken;
|
|
||||||
private UserEntity userEntity;
|
|
||||||
|
|
||||||
PeerConnectionFactory peerConnectionFactory;
|
PeerConnectionFactory peerConnectionFactory;
|
||||||
MediaConstraints audioConstraints;
|
MediaConstraints audioConstraints;
|
||||||
MediaConstraints videoConstraints;
|
MediaConstraints videoConstraints;
|
||||||
@ -104,20 +107,25 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
AudioSource audioSource;
|
AudioSource audioSource;
|
||||||
AudioTrack localAudioTrack;
|
AudioTrack localAudioTrack;
|
||||||
VideoCapturer videoCapturer;
|
VideoCapturer videoCapturer;
|
||||||
|
|
||||||
VideoRenderer localRenderer;
|
VideoRenderer localRenderer;
|
||||||
VideoRenderer remoteRenderer;
|
VideoRenderer remoteRenderer;
|
||||||
|
|
||||||
PeerConnection localPeer, remotePeer;
|
PeerConnection localPeer, remotePeer;
|
||||||
|
boolean leavingCall = false;
|
||||||
boolean inCall = true;
|
BooleanSupplier booleanSupplier = () -> leavingCall;
|
||||||
|
|
||||||
BooleanSupplier booleanSupplier = () -> inCall;
|
|
||||||
|
|
||||||
Disposable signalingDisposable;
|
Disposable signalingDisposable;
|
||||||
Disposable pingDisposable;
|
Disposable pingDisposable;
|
||||||
|
|
||||||
List<PeerConnection.IceServer> iceServers;
|
List<PeerConnection.IceServer> iceServers;
|
||||||
|
private String roomToken;
|
||||||
|
private UserEntity userEntity;
|
||||||
|
private String callSession;
|
||||||
|
|
||||||
|
private String credentials;
|
||||||
|
|
||||||
|
private static int getSystemUiVisibility() {
|
||||||
|
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||||
|
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -135,8 +143,10 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
roomToken = getIntent().getExtras().getString("roomToken", "");
|
roomToken = getIntent().getExtras().getString("roomToken", "");
|
||||||
userEntity = Parcels.unwrap(getIntent().getExtras().getParcelable("userEntity"));
|
userEntity = getIntent().getExtras().getParcelable("userEntity");
|
||||||
|
callSession = getIntent().getExtras().getString("callSession", "");
|
||||||
|
|
||||||
|
credentials = ApiHelper.getCredentials(userEntity.getUsername(), userEntity.getToken());
|
||||||
initViews();
|
initViews();
|
||||||
|
|
||||||
PermissionHelper permissionHelper = new PermissionHelper(this);
|
PermissionHelper permissionHelper = new PermissionHelper(this);
|
||||||
@ -157,8 +167,6 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private VideoCapturer createVideoCapturer() {
|
private VideoCapturer createVideoCapturer() {
|
||||||
videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
|
videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
|
||||||
return videoCapturer;
|
return videoCapturer;
|
||||||
@ -256,119 +264,202 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true"));
|
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true"));
|
||||||
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
||||||
|
|
||||||
|
//creating localPeer
|
||||||
|
localPeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints,
|
||||||
|
new MagicPeerConnectionObserver() {
|
||||||
|
@Override
|
||||||
|
public void onIceCandidate(IceCandidate iceCandidate) {
|
||||||
|
super.onIceCandidate(iceCandidate);
|
||||||
|
onIceCandidateReceived(localPeer, iceCandidate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//creating local mediastream
|
//creating local mediastream
|
||||||
MediaStream stream = peerConnectionFactory.createLocalMediaStream("102");
|
MediaStream stream = peerConnectionFactory.createLocalMediaStream("102");
|
||||||
stream.addTrack(localAudioTrack);
|
stream.addTrack(localAudioTrack);
|
||||||
stream.addTrack(localVideoTrack);
|
stream.addTrack(localVideoTrack);
|
||||||
localPeer.addStream(stream);
|
localPeer.addStream(stream);
|
||||||
|
|
||||||
// Start pulling signaling messages
|
ncApi.joinRoom(credentials, ApiHelper.getUrlForJoinRoom(userEntity.getBaseUrl(), roomToken))
|
||||||
ncApi.pullSignalingMessages(ApiHelper.getCredentials(userEntity.getUsername(),
|
|
||||||
userEntity.getToken()), ApiHelper.getUrlForSignaling(userEntity.getBaseUrl()))
|
|
||||||
.subscribeOn(Schedulers.newThread())
|
.subscribeOn(Schedulers.newThread())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.repeatWhen(observable -> observable.delay(1500, TimeUnit.MILLISECONDS))
|
.subscribe(new Observer<CallOverall>() {
|
||||||
.repeatUntil(booleanSupplier)
|
|
||||||
.retry(3)
|
|
||||||
.subscribe(new Observer<SignalingOverall>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
signalingDisposable = d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNext(SignalingOverall signalingOverall) {
|
public void onNext(CallOverall callOverall) {
|
||||||
if (signalingOverall.getOcs().getSignalings() != null) {
|
ncApi.joinCall(credentials,
|
||||||
for (int i = 0; i < signalingOverall.getOcs().getSignalings().size(); i++) {
|
ApiHelper.getUrlForCall(userEntity.getBaseUrl(), roomToken))
|
||||||
try {
|
.subscribeOn(Schedulers.newThread())
|
||||||
receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i));
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
} catch (IOException e) {
|
.subscribe(new Observer<GenericOverall>() {
|
||||||
e.printStackTrace();
|
@Override
|
||||||
}
|
public void onSubscribe(Disposable d) {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(GenericOverall genericOverall) {
|
||||||
|
callSession = callOverall.getOcs().getData().getSessionId();
|
||||||
|
|
||||||
|
// start pinging the call
|
||||||
|
ncApi.pingCall(ApiHelper.getCredentials(userEntity.getUsername(), userEntity.getToken()),
|
||||||
|
ApiHelper.getUrlForCallPing(userEntity.getBaseUrl(), roomToken))
|
||||||
|
.subscribeOn(Schedulers.newThread())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.repeatWhen(observable -> observable.delay(5000, TimeUnit.MILLISECONDS))
|
||||||
|
.repeatUntil(booleanSupplier)
|
||||||
|
.retry(3)
|
||||||
|
.subscribe(new Observer<GenericOverall>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) {
|
||||||
|
pingDisposable = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(GenericOverall genericOverall) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
dispose(pingDisposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
dispose(pingDisposable);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start pulling signaling messages
|
||||||
|
ncApi.pullSignalingMessages(ApiHelper.getCredentials(userEntity.getUsername(),
|
||||||
|
userEntity.getToken()), ApiHelper.getUrlForSignaling(userEntity.getBaseUrl()))
|
||||||
|
.subscribeOn(Schedulers.newThread())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.repeatWhen(observable -> observable.delay(1500, TimeUnit.MILLISECONDS))
|
||||||
|
.repeatUntil(booleanSupplier)
|
||||||
|
.retry(3)
|
||||||
|
.subscribe(new Observer<SignalingOverall>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) {
|
||||||
|
signalingDisposable = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(SignalingOverall signalingOverall) {
|
||||||
|
if (signalingOverall.getOcs().getSignalings() != null) {
|
||||||
|
for (int i = 0; i < signalingOverall.getOcs().getSignalings().size(); i++) {
|
||||||
|
try {
|
||||||
|
receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
dispose(signalingDisposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
dispose(signalingDisposable);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable e) {
|
public void onError(Throwable e) {
|
||||||
dispose(signalingDisposable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
dispose(signalingDisposable);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// start pinging the call
|
private void receivedSignalingMessage(Signaling signaling) throws IOException {
|
||||||
ncApi.pingCall(ApiHelper.getCredentials(userEntity.getUsername(), userEntity.getToken()),
|
String messageType = signaling.getType();
|
||||||
ApiHelper.getUrlForCallPing(userEntity.getBaseUrl(), roomToken))
|
|
||||||
.subscribeOn(Schedulers.newThread())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.repeatWhen(observable -> observable.delay(5000, TimeUnit.MILLISECONDS))
|
|
||||||
.repeatUntil(booleanSupplier)
|
|
||||||
.retry(3)
|
|
||||||
.subscribe(new Observer<Void>() {
|
|
||||||
@Override
|
|
||||||
public void onSubscribe(Disposable d) {
|
|
||||||
pingDisposable = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (leavingCall) {
|
||||||
public void onNext(Void aVoid) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
if ("usersInRoom".equals(messageType)) {
|
||||||
|
processUsersInRoom((List<Participant>) signaling.getMessageWrapper());
|
||||||
@Override
|
} else if ("message".equals(messageType)) {
|
||||||
public void onError(Throwable e) {
|
NCMessageWrapper ncSignalingMessage = LoganSquare.parse(signaling.getMessageWrapper().toString(),
|
||||||
dispose(pingDisposable);
|
NCMessageWrapper.class);
|
||||||
}
|
if (ncSignalingMessage.getSignalingMessage().getRoomType().equals("video")) {
|
||||||
|
switch (ncSignalingMessage.getSignalingMessage().getType()) {
|
||||||
@Override
|
case "offer":
|
||||||
public void onComplete() {
|
break;
|
||||||
dispose(pingDisposable);
|
case "answer":
|
||||||
}
|
break;
|
||||||
});
|
case "candidate":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Something went very very wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processUsersInRoom(List<Participant> users) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void call() {
|
private void call() {
|
||||||
|
|
||||||
//creating localPeer
|
|
||||||
localPeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints,
|
|
||||||
new MagicPeerConnectionObserver() {
|
|
||||||
@Override
|
|
||||||
public void onIceCandidate(IceCandidate iceCandidate) {
|
|
||||||
super.onIceCandidate(iceCandidate);
|
|
||||||
onIceCandidateReceived(localPeer, iceCandidate);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//creating remotePeer
|
//creating remotePeer
|
||||||
remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints,
|
remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints,
|
||||||
new MagicPeerConnectionObserver() {
|
new MagicPeerConnectionObserver() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIceCandidate(IceCandidate iceCandidate) {
|
public void onIceCandidate(IceCandidate iceCandidate) {
|
||||||
super.onIceCandidate(iceCandidate);
|
super.onIceCandidate(iceCandidate);
|
||||||
onIceCandidateReceived(remotePeer, iceCandidate);
|
onIceCandidateReceived(remotePeer, iceCandidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onAddStream(MediaStream mediaStream) {
|
public void onAddStream(MediaStream mediaStream) {
|
||||||
super.onAddStream(mediaStream);
|
super.onAddStream(mediaStream);
|
||||||
gotRemoteStream(mediaStream);
|
gotRemoteStream(mediaStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
|
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
|
||||||
super.onIceGatheringChange(iceGatheringState);
|
super.onIceGatheringChange(iceGatheringState);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//creating Offer
|
//creating Offer
|
||||||
localPeer.createOffer(new MagicSdpObserver(){
|
localPeer.createOffer(new MagicSdpObserver() {
|
||||||
@Override
|
@Override
|
||||||
public void onCreateSuccess(SessionDescription sessionDescription) {
|
public void onCreateSuccess(SessionDescription sessionDescription) {
|
||||||
//we have localOffer. Set it as local desc for localpeer and remote desc for remote peer.
|
//we have localOffer. Set it as local desc for localpeer and remote desc for remote peer.
|
||||||
@ -390,10 +481,9 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
}, sdpConstraints);
|
}, sdpConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void hangup() {
|
private void hangup() {
|
||||||
|
|
||||||
inCall = false;
|
leavingCall = true;
|
||||||
|
|
||||||
dispose(null);
|
dispose(null);
|
||||||
|
|
||||||
@ -413,6 +503,32 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
pipVideoView.release();
|
pipVideoView.release();
|
||||||
fullScreenVideoView.release();
|
fullScreenVideoView.release();
|
||||||
|
|
||||||
|
String credentials = ApiHelper.getCredentials(userEntity.getUsername(), userEntity.getToken());
|
||||||
|
ncApi.leaveCall(credentials, ApiHelper.getUrlForCall(userEntity.getBaseUrl(), roomToken))
|
||||||
|
.subscribeOn(Schedulers.newThread())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(new Observer<GenericOverall>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(GenericOverall genericOverall) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gotRemoteStream(MediaStream stream) {
|
private void gotRemoteStream(MediaStream stream) {
|
||||||
@ -444,14 +560,8 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
hangup();
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
hangup();
|
||||||
|
|
||||||
private static int getSystemUiVisibility() {
|
|
||||||
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
|
|
||||||
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
|
||||||
return flags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispose(@Nullable Disposable disposable) {
|
private void dispose(@Nullable Disposable disposable) {
|
||||||
|
@ -111,7 +111,7 @@ public final class MainActivity extends AppCompatActivity implements ActionBarPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showCertificateDialog(X509Certificate cert, MagicTrustManager magicTrustManager,
|
public void showCertificateDialog(X509Certificate cert, MagicTrustManager magicTrustManager,
|
||||||
@Nullable SslErrorHandler sslErrorHandler) {
|
@Nullable SslErrorHandler sslErrorHandler) {
|
||||||
DateFormat formatter = DateFormat.getDateInstance(DateFormat.LONG);
|
DateFormat formatter = DateFormat.getDateInstance(DateFormat.LONG);
|
||||||
String validFrom = formatter.format(cert.getNotBefore());
|
String validFrom = formatter.format(cert.getNotBefore());
|
||||||
String validUntil = formatter.format(cert.getNotAfter());
|
String validUntil = formatter.format(cert.getNotAfter());
|
||||||
@ -140,8 +140,8 @@ public final class MainActivity extends AppCompatActivity implements ActionBarPr
|
|||||||
issuedBy, issuedFor, validFrom, validUntil);
|
issuedBy, issuedFor, validFrom, validUntil);
|
||||||
|
|
||||||
new LovelyStandardDialog(this)
|
new LovelyStandardDialog(this)
|
||||||
.setTopColorRes(R.color.darkRed)
|
.setTopColorRes(R.color.nc_darkRed)
|
||||||
.setNegativeButtonColorRes(R.color.darkRed)
|
.setNegativeButtonColorRes(R.color.nc_darkRed)
|
||||||
.setPositiveButtonColorRes(R.color.colorPrimaryDark)
|
.setPositiveButtonColorRes(R.color.colorPrimaryDark)
|
||||||
.setIcon(R.drawable.ic_security_white_24dp)
|
.setIcon(R.drawable.ic_security_white_24dp)
|
||||||
.setTitle(R.string.nc_certificate_dialog_title)
|
.setTitle(R.string.nc_certificate_dialog_title)
|
||||||
|
@ -133,23 +133,30 @@ public interface NcApi {
|
|||||||
@GET
|
@GET
|
||||||
Observable<ParticipantsOverall> getPeersForCall(@Header("Authorization") String authorization, @Url String url);
|
Observable<ParticipantsOverall> getPeersForCall(@Header("Authorization") String authorization, @Url String url);
|
||||||
|
|
||||||
|
@POST
|
||||||
|
Observable<CallOverall> joinRoom(@Header("Authorization") String authorization, @Url String url);
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
Observable<GenericOverall> leaveRoom(@Header("Authorization") String authorization, @Url String url);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken
|
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
Observable<CallOverall> joinCall(@Header("Authorization") String authorization, @Url String url);
|
Observable<GenericOverall> joinCall(@Header("Authorization") String authorization, @Url String url);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
Observable<GenericOverall> leaveCall(@Header("Authorization") String authorization, @Url String url);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken/ping
|
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken/ping
|
||||||
*/
|
*/
|
||||||
@POST
|
@POST
|
||||||
Observable<Void> pingCall(@Header("Authorization") String authorization, @Url String url);
|
Observable<GenericOverall> pingCall(@Header("Authorization") String authorization, @Url String url);
|
||||||
|
|
||||||
/*
|
|
||||||
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken
|
|
||||||
*/
|
|
||||||
@DELETE
|
|
||||||
Observable<Void> leaveCall(@Header("Authorization") String authorization, @Url String url);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
QueryMap items are as follows:
|
QueryMap items are as follows:
|
||||||
@ -220,7 +227,7 @@ public interface NcApi {
|
|||||||
*/
|
*/
|
||||||
@DELETE
|
@DELETE
|
||||||
Observable<Void> unregisterDeviceForNotificationsWithProxy(@Header("Authorization") String authorization,
|
Observable<Void> unregisterDeviceForNotificationsWithProxy(@Header("Authorization") String authorization,
|
||||||
@Url String url,
|
@Url String url,
|
||||||
@QueryMap Map<String, String> fields);
|
@QueryMap Map<String, String> fields);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,10 @@ public class ApiHelper {
|
|||||||
return retrofitBucket;
|
return retrofitBucket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUrlForJoinRoom(String baseUrl, String token) {
|
||||||
|
return getRoom(baseUrl, token) + "/participants/active";
|
||||||
|
}
|
||||||
|
|
||||||
public static String getUrlForGetRooms(String baseUrl) {
|
public static String getUrlForGetRooms(String baseUrl) {
|
||||||
return baseUrl + ocsApiVersion + spreedApiVersion + "/room";
|
return baseUrl + ocsApiVersion + spreedApiVersion + "/room";
|
||||||
}
|
}
|
||||||
@ -115,6 +119,7 @@ public class ApiHelper {
|
|||||||
|
|
||||||
public static String getUrlForCall(String baseUrl, String token) {
|
public static String getUrlForCall(String baseUrl, String token) {
|
||||||
return baseUrl + ocsApiVersion + spreedApiVersion + "/call/" + token;
|
return baseUrl + ocsApiVersion + spreedApiVersion + "/call/" + token;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getUrlForCallPing(String baseUrl, String token) {
|
public static String getUrlForCallPing(String baseUrl, String token) {
|
||||||
|
@ -35,7 +35,7 @@ public class GenericMeta {
|
|||||||
String status;
|
String status;
|
||||||
|
|
||||||
@JsonField(name = "statuscode")
|
@JsonField(name = "statuscode")
|
||||||
String statusCode;
|
int statusCode;
|
||||||
|
|
||||||
@JsonField(name = "message")
|
@JsonField(name = "message")
|
||||||
String message;
|
String message;
|
||||||
|
@ -42,4 +42,7 @@ public class Participant {
|
|||||||
|
|
||||||
@JsonField(name = "roomId")
|
@JsonField(name = "roomId")
|
||||||
long roomId;
|
long roomId;
|
||||||
|
|
||||||
|
@JsonField(name = "inCall")
|
||||||
|
boolean inCall;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ import lombok.Data;
|
|||||||
public class Signaling {
|
public class Signaling {
|
||||||
@JsonField(name = "type")
|
@JsonField(name = "type")
|
||||||
String type;
|
String type;
|
||||||
//can be NCSignalingMessage or List<Participant>
|
//can be NCMessageWrapper or List<Participant>
|
||||||
@JsonField(name = "data")
|
@JsonField(name = "data")
|
||||||
Object signalingMessage;
|
Object messageWrapper;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,7 @@ public class NextcloudTalkApplication extends MultiDexApplication {
|
|||||||
|
|
||||||
new JobRequest.Builder(PushRegistrationJob.TAG).setUpdateCurrent(true).startNow().build().schedule();
|
new JobRequest.Builder(PushRegistrationJob.TAG).setUpdateCurrent(true).startNow().build().schedule();
|
||||||
new JobRequest.Builder(AccountRemovalJob.TAG).setUpdateCurrent(true).startNow().build().schedule();
|
new JobRequest.Builder(AccountRemovalJob.TAG).setUpdateCurrent(true).startNow().build().schedule();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,14 +47,13 @@ import android.view.inputmethod.EditorInfo;
|
|||||||
|
|
||||||
import com.bluelinelabs.conductor.RouterTransaction;
|
import com.bluelinelabs.conductor.RouterTransaction;
|
||||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler;
|
|
||||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
|
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
|
||||||
|
import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler;
|
||||||
import com.nextcloud.talk.R;
|
import com.nextcloud.talk.R;
|
||||||
import com.nextcloud.talk.activities.CallActivity;
|
import com.nextcloud.talk.activities.CallActivity;
|
||||||
import com.nextcloud.talk.adapters.items.RoomItem;
|
import com.nextcloud.talk.adapters.items.RoomItem;
|
||||||
import com.nextcloud.talk.api.NcApi;
|
import com.nextcloud.talk.api.NcApi;
|
||||||
import com.nextcloud.talk.api.helpers.api.ApiHelper;
|
import com.nextcloud.talk.api.helpers.api.ApiHelper;
|
||||||
import com.nextcloud.talk.api.models.json.call.CallOverall;
|
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||||
import com.nextcloud.talk.controllers.base.BaseController;
|
import com.nextcloud.talk.controllers.base.BaseController;
|
||||||
import com.nextcloud.talk.persistence.entities.UserEntity;
|
import com.nextcloud.talk.persistence.entities.UserEntity;
|
||||||
@ -73,9 +72,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
|
|||||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
|
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.functions.Consumer;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import okhttp3.Credentials;
|
|
||||||
import retrofit2.HttpException;
|
import retrofit2.HttpException;
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication.class)
|
@AutoInjector(NextcloudTalkApplication.class)
|
||||||
@ -110,26 +107,15 @@ public class CallsListController extends BaseController implements SearchView.On
|
|||||||
@Override
|
@Override
|
||||||
public boolean onItemClick(int position) {
|
public boolean onItemClick(int position) {
|
||||||
if (roomItems.size() > position) {
|
if (roomItems.size() > position) {
|
||||||
|
overridePushHandler(new NoOpControllerChangeHandler());
|
||||||
|
overridePopHandler(new NoOpControllerChangeHandler());
|
||||||
RoomItem roomItem = roomItems.get(position);
|
RoomItem roomItem = roomItems.get(position);
|
||||||
ncApi.joinCall(Credentials.basic(userEntity.getUsername(), userEntity.getToken()),
|
Intent callIntent = new Intent(getActivity(), CallActivity.class);
|
||||||
ApiHelper.getUrlForCall(userEntity.getBaseUrl(), roomItem.getModel().getToken()))
|
BundleBuilder bundleBuilder = new BundleBuilder(new Bundle());
|
||||||
.subscribeOn(Schedulers.newThread())
|
bundleBuilder.putString("roomToken", roomItem.getModel().getToken());
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
bundleBuilder.putParcelable("userEntity", userEntity);
|
||||||
.subscribe(new Consumer<CallOverall>() {
|
callIntent.putExtras(bundleBuilder.build());
|
||||||
@Override
|
startActivity(callIntent);
|
||||||
public void accept(CallOverall callOverall) throws Exception {
|
|
||||||
|
|
||||||
overridePushHandler(new SimpleSwapChangeHandler());
|
|
||||||
overridePopHandler(new SimpleSwapChangeHandler());
|
|
||||||
|
|
||||||
Intent callIntent = new Intent(getActivity(), CallActivity.class);
|
|
||||||
BundleBuilder bundleBuilder = new BundleBuilder(new Bundle());
|
|
||||||
bundleBuilder.putString("roomToken", roomItem.getModel().getToken());
|
|
||||||
bundleBuilder.putParcelable("userEntity", userEntity);
|
|
||||||
callIntent.putExtras(bundleBuilder.build());
|
|
||||||
startActivity(callIntent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -340,7 +340,7 @@ public class SettingsController extends BaseController {
|
|||||||
messageView.setVisibility(View.VISIBLE);
|
messageView.setVisibility(View.VISIBLE);
|
||||||
break;
|
break;
|
||||||
case WRONG_ACCOUNT:
|
case WRONG_ACCOUNT:
|
||||||
messageText.setTextColor(getResources().getColor(R.color.darkRed));
|
messageText.setTextColor(getResources().getColor(R.color.nc_darkRed));
|
||||||
messageText.setText(getResources().getString(R.string.nc_settings_wrong_account));
|
messageText.setText(getResources().getString(R.string.nc_settings_wrong_account));
|
||||||
messageView.setVisibility(View.VISIBLE);
|
messageView.setVisibility(View.VISIBLE);
|
||||||
break;
|
break;
|
||||||
@ -424,6 +424,11 @@ public class SettingsController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTitle() {
|
||||||
|
return getResources().getString(R.string.nc_app_name);
|
||||||
|
}
|
||||||
|
|
||||||
private class ProxyCredentialsChangeListener implements OnPreferenceValueChangedListener<Boolean> {
|
private class ProxyCredentialsChangeListener implements OnPreferenceValueChangedListener<Boolean> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -462,9 +467,4 @@ public class SettingsController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getTitle() {
|
|
||||||
return getResources().getString(R.string.nc_app_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ public class SwitchAccountController extends BaseController {
|
|||||||
User user;
|
User user;
|
||||||
UserEntity currentUserEntity = userUtils.getCurrentUser();
|
UserEntity currentUserEntity = userUtils.getCurrentUser();
|
||||||
|
|
||||||
for(Object userEntityObject : userUtils.getUsers()) {
|
for (Object userEntityObject : userUtils.getUsers()) {
|
||||||
userEntity = (UserEntity) userEntityObject;
|
userEntity = (UserEntity) userEntityObject;
|
||||||
if (!userEntity.equals(currentUserEntity)) {
|
if (!userEntity.equals(currentUserEntity)) {
|
||||||
user = new User();
|
user = new User();
|
||||||
|
@ -236,7 +236,7 @@ public class WebViewLoginController extends BaseController {
|
|||||||
if (userUtils.checkIfUserIsScheduledForDeletion(loginData.getUsername(), baseUrl)) {
|
if (userUtils.checkIfUserIsScheduledForDeletion(loginData.getUsername(), baseUrl)) {
|
||||||
ErrorMessageHolder.getInstance().setMessageType(
|
ErrorMessageHolder.getInstance().setMessageType(
|
||||||
ErrorMessageHolder.ErrorMessageType.ACCOUNT_SCHEDULED_FOR_DELETION);
|
ErrorMessageHolder.ErrorMessageType.ACCOUNT_SCHEDULED_FOR_DELETION);
|
||||||
getRouter().popToRoot();
|
getRouter().popToRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use the URL user entered because one provided by the server is NOT reliable
|
// We use the URL user entered because one provided by the server is NOT reliable
|
||||||
|
@ -30,6 +30,7 @@ import com.nextcloud.talk.controllers.base.providers.ActionBarProvider;
|
|||||||
public abstract class BaseController extends RefWatchingController {
|
public abstract class BaseController extends RefWatchingController {
|
||||||
|
|
||||||
private static final String TAG = "BaseController";
|
private static final String TAG = "BaseController";
|
||||||
|
|
||||||
protected BaseController() {
|
protected BaseController() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ import com.nextcloud.talk.utils.ssl.MagicTrustManager;
|
|||||||
import com.nextcloud.talk.utils.ssl.SSLSocketFactoryCompat;
|
import com.nextcloud.talk.utils.ssl.SSLSocketFactoryCompat;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.CookieManager;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -47,6 +48,7 @@ import okhttp3.Authenticator;
|
|||||||
import okhttp3.Cache;
|
import okhttp3.Cache;
|
||||||
import okhttp3.Credentials;
|
import okhttp3.Credentials;
|
||||||
import okhttp3.Interceptor;
|
import okhttp3.Interceptor;
|
||||||
|
import okhttp3.JavaNetCookieJar;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
@ -121,6 +123,8 @@ public class RestModule {
|
|||||||
httpClient.readTimeout(30, TimeUnit.SECONDS);
|
httpClient.readTimeout(30, TimeUnit.SECONDS);
|
||||||
httpClient.writeTimeout(30, TimeUnit.SECONDS);
|
httpClient.writeTimeout(30, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
httpClient.cookieJar(new JavaNetCookieJar(new CookieManager()));
|
||||||
|
|
||||||
int cacheSize = 128 * 1024 * 1024; // 128 MB
|
int cacheSize = 128 * 1024 * 1024; // 128 MB
|
||||||
|
|
||||||
httpClient.cache(new Cache(NextcloudTalkApplication.getSharedApplication().getCacheDir(), cacheSize));
|
httpClient.cache(new Cache(NextcloudTalkApplication.getSharedApplication().getCacheDir(), cacheSize));
|
||||||
|
@ -30,7 +30,8 @@ import java.security.cert.X509Certificate;
|
|||||||
public class CertificateEvent {
|
public class CertificateEvent {
|
||||||
private final X509Certificate x509Certificate;
|
private final X509Certificate x509Certificate;
|
||||||
private final MagicTrustManager magicTrustManager;
|
private final MagicTrustManager magicTrustManager;
|
||||||
@Nullable private final SslErrorHandler sslErrorHandler;
|
@Nullable
|
||||||
|
private final SslErrorHandler sslErrorHandler;
|
||||||
|
|
||||||
public CertificateEvent(X509Certificate x509Certificate, MagicTrustManager magicTrustManager,
|
public CertificateEvent(X509Certificate x509Certificate, MagicTrustManager magicTrustManager,
|
||||||
@Nullable SslErrorHandler sslErrorHandler) {
|
@Nullable SslErrorHandler sslErrorHandler) {
|
||||||
|
@ -61,7 +61,7 @@ public class AccountRemovalJob extends Job {
|
|||||||
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
|
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
|
||||||
|
|
||||||
PushConfigurationState pushConfigurationState;
|
PushConfigurationState pushConfigurationState;
|
||||||
for(Object userEntityObject : userUtils.getUsersScheduledForDeletion()) {
|
for (Object userEntityObject : userUtils.getUsersScheduledForDeletion()) {
|
||||||
UserEntity userEntity = (UserEntity) userEntityObject;
|
UserEntity userEntity = (UserEntity) userEntityObject;
|
||||||
try {
|
try {
|
||||||
if (!TextUtils.isEmpty(userEntity.getPushConfigurationState())) {
|
if (!TextUtils.isEmpty(userEntity.getPushConfigurationState())) {
|
||||||
@ -78,8 +78,8 @@ public class AccountRemovalJob extends Job {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNext(GenericOverall genericOverall) {
|
public void onNext(GenericOverall genericOverall) {
|
||||||
if (genericOverall.getOcs().getMeta().getStatusCode().equals("200")
|
if (genericOverall.getOcs().getMeta().getStatusCode() == 200
|
||||||
|| genericOverall.getOcs().getMeta().getStatusCode().equals("202")) {
|
|| genericOverall.getOcs().getMeta().getStatusCode() == 202) {
|
||||||
HashMap<String, String> queryMap = new HashMap<>();
|
HashMap<String, String> queryMap = new HashMap<>();
|
||||||
queryMap.put("deviceIdentifier", finalPushConfigurationState.deviceIdentifier);
|
queryMap.put("deviceIdentifier", finalPushConfigurationState.deviceIdentifier);
|
||||||
queryMap.put("userPublicKey", finalPushConfigurationState.getUserPublicKey());
|
queryMap.put("userPublicKey", finalPushConfigurationState.getUserPublicKey());
|
||||||
@ -158,7 +158,7 @@ public class AccountRemovalJob extends Job {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch(IOException e) {
|
} catch (IOException e) {
|
||||||
Log.d(TAG, "Something went wrong while removing job at parsing PushConfigurationState");
|
Log.d(TAG, "Something went wrong while removing job at parsing PushConfigurationState");
|
||||||
userUtils.deleteUser(userEntity.getUsername(),
|
userUtils.deleteUser(userEntity.getUsername(),
|
||||||
userEntity.getBaseUrl());
|
userEntity.getBaseUrl());
|
||||||
|
@ -28,10 +28,10 @@ public class DisplayHelper {
|
|||||||
|
|
||||||
private static final String TAG = "DIsplayHelper";
|
private static final String TAG = "DIsplayHelper";
|
||||||
|
|
||||||
public static float convertDpToPixel(float dp, Context context){
|
public static float convertDpToPixel(float dp, Context context) {
|
||||||
Resources resources = context.getResources();
|
Resources resources = context.getResources();
|
||||||
DisplayMetrics metrics = resources.getDisplayMetrics();
|
DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||||
float px = dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
float px = dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
||||||
return px;
|
return px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,9 @@ package com.nextcloud.talk.utils;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
public class ErrorMessageHolder {
|
public class ErrorMessageHolder {
|
||||||
public enum ErrorMessageType {
|
private static final ErrorMessageHolder holder = new ErrorMessageHolder();
|
||||||
WRONG_ACCOUNT, ACCOUNT_UPDATED_NOT_ADDED, ACCOUNT_SCHEDULED_FOR_DELETION
|
|
||||||
}
|
|
||||||
|
|
||||||
private ErrorMessageType errorMessageType;
|
private ErrorMessageType errorMessageType;
|
||||||
|
|
||||||
private static final ErrorMessageHolder holder = new ErrorMessageHolder();
|
|
||||||
public static ErrorMessageHolder getInstance() {
|
public static ErrorMessageHolder getInstance() {
|
||||||
return holder;
|
return holder;
|
||||||
}
|
}
|
||||||
@ -42,6 +38,9 @@ public class ErrorMessageHolder {
|
|||||||
this.errorMessageType = errorMessageType;
|
this.errorMessageType = errorMessageType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ErrorMessageType {
|
||||||
|
WRONG_ACCOUNT, ACCOUNT_UPDATED_NOT_ADDED, ACCOUNT_SCHEDULED_FOR_DELETION
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -335,7 +335,8 @@ public class PushUtils {
|
|||||||
|
|
||||||
if (readPublicKey) {
|
if (readPublicKey) {
|
||||||
keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----",
|
keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----",
|
||||||
"").replace("-----END PUBLIC KEY-----", "");;
|
"").replace("-----END PUBLIC KEY-----", "");
|
||||||
|
;
|
||||||
} else {
|
} else {
|
||||||
keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----",
|
keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----",
|
||||||
"").replace("-----END PRIVATE KEY-----", "");
|
"").replace("-----END PRIVATE KEY-----", "");
|
||||||
|
@ -65,7 +65,6 @@ public class UserUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public UserEntity getAnyUserAndSetAsActive() {
|
public UserEntity getAnyUserAndSetAsActive() {
|
||||||
Result findUserQueryResult = dataStore.select(User.class)
|
Result findUserQueryResult = dataStore.select(User.class)
|
||||||
.where(UserEntity.SCHEDULED_FOR_DELETION.eq(false))
|
.where(UserEntity.SCHEDULED_FOR_DELETION.eq(false))
|
||||||
@ -125,6 +124,7 @@ public class UserUtils {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getIfUserWithUsernameAndServer(String username, String server) {
|
public boolean getIfUserWithUsernameAndServer(String username, String server) {
|
||||||
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username)
|
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username)
|
||||||
.and(UserEntity.BASE_URL.eq(server.toLowerCase())))
|
.and(UserEntity.BASE_URL.eq(server.toLowerCase())))
|
||||||
|
17
app/src/main/res/drawable/ic_logo.xml
Normal file
17
app/src/main/res/drawable/ic_logo.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#fff"
|
||||||
|
android:strokeWidth="0.14"
|
||||||
|
android:pathData="M7.9992,0.999 A6.9994,6.9993,0,0,0,1,7.9986 A6.9994,6.9993,0,0,0,7.9992,14.998
|
||||||
|
A6.9994,6.9993,0,0,0,11.63,13.974 C12.4902,14.3158,14.4171,15.33,14.8757,14.8919
|
||||||
|
C15.3549,14.4343,14.3131,12.2803,14.0633,11.4799
|
||||||
|
A6.9994,6.9993,0,0,0,14.9983,7.9985 A6.9994,6.9993,0,0,0,7.9992,0.9992 Z
|
||||||
|
M8,3.6601 A4.3401,4.34,0,0,1,12.34,8.0002 A4.3401,4.34,0,0,1,8,12.34
|
||||||
|
A4.3401,4.34,0,0,1,3.66,8.0002 A4.3401,4.34,0,0,1,8,3.6601 Z" />
|
||||||
|
</vector>
|
@ -23,7 +23,7 @@
|
|||||||
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:background="@color/nc_background_color">
|
android:background="@color/nc_white_color">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress_bar"
|
android:id="@+id/progress_bar"
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
android:id="@+id/swipe_refresh_layout"
|
android:id="@+id/swipe_refresh_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/nc_background_color">
|
android:background="@color/nc_white_color">
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/recycler_view"
|
android:id="@+id/recycler_view"
|
||||||
|
@ -23,7 +23,16 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/nc_background_color">
|
android:background="@color/colorPrimary">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="96dp"
|
||||||
|
android:layout_height="96dp"
|
||||||
|
android:layout_above="@id/text_field_boxes"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_marginBottom="36dp"
|
||||||
|
android:background="@drawable/ic_logo"
|
||||||
|
/>
|
||||||
|
|
||||||
<studio.carbonylgroup.textfieldboxes.TextFieldBoxes
|
<studio.carbonylgroup.textfieldboxes.TextFieldBoxes
|
||||||
android:id="@+id/text_field_boxes"
|
android:id="@+id/text_field_boxes"
|
||||||
@ -34,16 +43,18 @@
|
|||||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
||||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||||
app:errorColor="@color/A400red"
|
app:errorColor="@color/nc_white_color_complete"
|
||||||
app:helperText=" "
|
app:helperText=" "
|
||||||
app:primaryColor="@color/colorPrimary">
|
app:primaryColor="@color/nc_white_color_complete"
|
||||||
|
>
|
||||||
|
|
||||||
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
|
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
|
||||||
android:id="@+id/extended_edit_text"
|
android:id="@+id/extended_edit_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:imeOptions="actionDone"
|
android:imeOptions="actionDone"
|
||||||
android:singleLine="true"/>
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/nc_white_color_complete"/>
|
||||||
|
|
||||||
</studio.carbonylgroup.textfieldboxes.TextFieldBoxes>
|
</studio.carbonylgroup.textfieldboxes.TextFieldBoxes>
|
||||||
|
|
||||||
@ -59,7 +70,7 @@
|
|||||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||||
android:layout_marginTop="@dimen/padding_between_elements"
|
android:layout_marginTop="@dimen/padding_between_elements"
|
||||||
android:indeterminate="true"
|
android:indeterminate="true"
|
||||||
android:progressTint="@color/colorPrimary"
|
android:progressTint="@color/nc_white_color"
|
||||||
android:visibility="invisible"/>
|
android:visibility="invisible"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#0082c9</color>
|
<color name="colorPrimary">#0082C9</color>
|
||||||
<color name="colorPrimaryDark">#006AA3</color>
|
<color name="colorPrimaryDark">#006AA3</color>
|
||||||
<color name="colorAccent">#007CC2</color>
|
<color name="colorAccent">#007CC2</color>
|
||||||
|
|
||||||
<color name="darkRed">#D32F2F</color>
|
<color name="nc_darkRed">#D32F2F</color>
|
||||||
|
<color name="nc_white_color">@color/per70white</color>
|
||||||
|
<color name="nc_white_color_complete">#FFFFFF</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
<string name="nc_app_name">Nextcloud Talk</string>
|
<string name="nc_app_name">Nextcloud Talk</string>
|
||||||
<string name="nc_server_product_name">Nextcloud</string>
|
<string name="nc_server_product_name">Nextcloud</string>
|
||||||
|
|
||||||
<color name="nc_background_color">@color/per70white</color>
|
|
||||||
<string name="nc_push_server_url">https://push-notifications.nextcloud.com</string>
|
<string name="nc_push_server_url">https://push-notifications.nextcloud.com</string>
|
||||||
|
|
||||||
<!-- Will not be shown if empty -->
|
<!-- Will not be shown if empty -->
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
Loading…
Reference in New Issue
Block a user