Merge branch 'dev' into version

This commit is contained in:
Niels Andriesse 2020-03-05 14:15:05 +11:00
commit 44fd72dfac
47 changed files with 300 additions and 1336 deletions

2
.gitignore vendored
View File

@ -24,3 +24,5 @@ obj/
jni/libspeex/.deps/ jni/libspeex/.deps/
*.sh *.sh
pkcs11.password pkcs11.password
fabric.properties
play

View File

@ -38,58 +38,36 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" /> <uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="network.loki.messenger.ACCESS_SECRETS" /> <uses-permission android:name="network.loki.messenger.ACCESS_SECRETS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.WRITE_PROFILE" />
<uses-permission
android:name="android.permission.BROADCAST_WAP_PUSH"
tools:ignore="ProtectedPermissions" />
<!--
Loki - We don't need these at all
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<!--
Loki - Enable again once we have location sharing
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
-->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <!-- <uses-permission android:name="android.permission.READ_CALL_STATE"/> --> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- For sending/receiving events -->
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.READ_CALENDAR" /> <!-- Normal -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <!-- So we can add a TextSecure 'Account' -->
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> <!-- For conversation 'shortcuts' on the desktop -->
<uses-permission android:name="android.permission.USE_CREDENTIALS" /> <!-- For conversation 'shortcuts' on the desktop -->
<uses-permission android:name="android.permission.INSTALL_SHORTCUT" /> <uses-permission android:name="android.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" /> <!-- For fixing MMS --> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- Set image as wallpaper -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- Set image as wallpaper -->
<uses-permission android:name="android.permission.SET_WALLPAPER" /> <uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" /> <!-- <uses-permission android:name="android.permission.CALL_PHONE" /> --> <uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.RAISED_THREAD_PRIORITY" /> <uses-permission android:name="android.permission.RAISED_THREAD_PRIORITY" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- Unused permissions that need to be removed -->
<uses-permission android:name="android.permission.BLUETOOTH" tools:node="remove"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" tools:node="remove"/>
<application <application
android:name="org.thoughtcrime.securesms.ApplicationContext" android:name="org.thoughtcrime.securesms.ApplicationContext"
android:allowBackup="false" android:allowBackup="false"
@ -101,22 +79,13 @@
android:theme="@style/Session.DarkTheme" android:theme="@style/Session.DarkTheme"
tools:replace="android:allowBackup"> tools:replace="android:allowBackup">
<meta-data <!-- Disable analytics -->
android:name="io.fabric.ApiKey"
android:value="d0c4d13f424a96b9064aa0a9ecafabdb0db4287f" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U" />
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<!--
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
-->
<meta-data <meta-data
android:name="firebase_analytics_collection_deactivated" android:name="firebase_analytics_collection_deactivated"
android:value="true" /> android:value="true" />
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
<meta-data <meta-data
android:name="google_analytics_adid_collection_enabled" android:name="google_analytics_adid_collection_enabled"
android:value="false" /> android:value="false" />
@ -462,10 +431,6 @@
android:noHistory="true" android:noHistory="true"
android:stateNotNeeded="true" android:stateNotNeeded="true"
android:theme="@android:style/Theme.NoDisplay" /> android:theme="@android:style/Theme.NoDisplay" />
<activity
android:name="org.thoughtcrime.securesms.PlayServicesProblemActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/TextSecure.DialogActivity" />
<activity android:name="org.thoughtcrime.securesms.SmsSendtoActivity"> <activity android:name="org.thoughtcrime.securesms.SmsSendtoActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SENDTO" /> <action android:name="android.intent.action.SENDTO" />
@ -590,20 +555,6 @@
android:name="android.accounts.AccountAuthenticator" android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" /> android:resource="@xml/authenticator" />
</service> </service>
<service
android:name="org.thoughtcrime.securesms.service.ContactsSyncAdapterService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
<meta-data
android:name="android.provider.CONTACTS_STRUCTURE"
android:resource="@xml/contactsformat" />
</service>
<service <service
android:name="org.thoughtcrime.securesms.service.DirectShareService" android:name="org.thoughtcrime.securesms.service.DirectShareService"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE"> android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
@ -612,11 +563,6 @@
</intent-filter> </intent-filter>
</service> </service>
<service android:name="org.thoughtcrime.securesms.service.GenericForegroundService" /> <service android:name="org.thoughtcrime.securesms.service.GenericForegroundService" />
<service android:name="org.thoughtcrime.securesms.gcm.FcmService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<receiver <receiver
android:name="org.thoughtcrime.securesms.service.SmsListener" android:name="org.thoughtcrime.securesms.service.SmsListener"

View File

@ -11,13 +11,11 @@ buildscript {
mavenLocal() mavenLocal()
google() google()
mavenCentral() mavenCentral()
maven { url 'https://maven.fabric.io/public' }
} }
dependencies { dependencies {
classpath "com.android.tools.build:gradle:$gradle_version" classpath "com.android.tools.build:gradle:$gradle_version"
classpath files('libs/gradle-witness.jar') classpath files('libs/gradle-witness.jar')
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "io.fabric.tools:gradle:1.+"
} }
} }
@ -25,7 +23,6 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'witness' apply plugin: 'witness'
apply plugin: 'io.fabric'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
repositories { repositories {
@ -62,7 +59,6 @@ repositories {
} }
google() google()
jcenter() jcenter()
maven { url 'https://maven.fabric.io/public' }
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
} }
@ -91,16 +87,6 @@ dependencies {
implementation 'android.arch.lifecycle:extensions:1.1.1' implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'android.arch.lifecycle:common-java8:1.1.1' implementation 'android.arch.lifecycle:common-java8:1.1.1'
implementation('com.google.firebase:firebase-messaging:17.3.4') {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
implementation 'com.google.android.gms:play-services-maps:16.0.0'
implementation 'com.google.android.gms:play-services-places:16.0.0'
implementation 'com.google.android.gms:play-services-auth:16.0.1'
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1' implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1' implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
@ -186,7 +172,6 @@ dependencies {
// Remote: // Remote:
// implementation "com.github.loki-project:loki-messenger-android-service:dev-SNAPSHOT" // implementation "com.github.loki-project:loki-messenger-android-service:dev-SNAPSHOT"
implementation "com.google.protobuf:protobuf-java:2.5.0" implementation "com.google.protobuf:protobuf-java:2.5.0"
implementation "com.googlecode.libphonenumber:libphonenumber:8.10.7"
implementation "com.fasterxml.jackson.core:jackson-databind:2.9.8" implementation "com.fasterxml.jackson.core:jackson-databind:2.9.8"
implementation "com.squareup.okhttp3:okhttp:3.12.1" implementation "com.squareup.okhttp3:okhttp:3.12.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -199,8 +184,8 @@ dependencies {
implementation "com.github.ybq:Android-SpinKit:1.4.0" implementation "com.github.ybq:Android-SpinKit:1.4.0"
} }
def canonicalVersionCode = 36 def canonicalVersionCode = 39
def canonicalVersionName = "1.0.2" def canonicalVersionName = "1.0.5"
def postFixSize = 10 def postFixSize = 10
def abiPostFix = ['armeabi-v7a' : 1, def abiPostFix = ['armeabi-v7a' : 1,
@ -279,8 +264,6 @@ android {
debug { debug {
minifyEnabled true minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-firebase-messaging.pro',
'proguard-google-play-services.pro',
'proguard-dagger.pro', 'proguard-dagger.pro',
'proguard-jackson.pro', 'proguard-jackson.pro',
'proguard-sqlite.pro', 'proguard-sqlite.pro',
@ -371,12 +354,12 @@ android {
def assembleWebsiteDescriptor = { variant, file -> def assembleWebsiteDescriptor = { variant, file ->
if (file.exists()) { if (file.exists()) {
MessageDigest md = MessageDigest.getInstance("SHA-256"); MessageDigest md = MessageDigest.getInstance("SHA-256")
file.eachByte 4096, {bytes, size -> file.eachByte 4096, {bytes, size ->
md.update(bytes, 0, size); md.update(bytes, 0, size)
} }
String digest = md.digest().collect {String.format "%02x", it}.join(); String digest = md.digest().collect {String.format "%02x", it}.join()
String url = variant.productFlavors.get(0).ext.websiteUpdateUrl String url = variant.productFlavors.get(0).ext.websiteUpdateUrl
String apkName = file.getName() String apkName = file.getName()

View File

@ -1 +0,0 @@
-dontwarn com.google.firebase.analytics.connector.AnalyticsConnector

View File

@ -1,19 +0,0 @@
## Google Play Services 4.3.23 specific rules ##
## https://developer.android.com/google/play-services/setup.html#Proguard ##
-keep class * extends java.util.ListResourceBundle {
protected Object[][] getContents();
}
-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
public static final *** NULL;
}
-keepnames @com.google.android.gms.common.annotation.KeepName class *
-keepclassmembernames class * {
@com.google.android.gms.common.annotation.KeepName *;
}
-keepnames class * implements android.os.Parcelable {
public static final ** CREATOR;
}

View File

@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge <merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
@ -39,23 +37,6 @@
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/footer_sim_info"
android:autoLink="none"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:maxWidth="140dp"
android:maxLines="1"
android:ellipsize="end"
android:linksClickable="false"
style="@style/Signal.Text.Caption.MessageSent"
android:layout_gravity="end|bottom"
android:fontFamily="sans-serif-light"
android:visibility="gone"
tools:visibility="visible"
tools:text="to SIM1" />
<ImageView <ImageView
android:id="@+id/footer_insecure_indicator" android:id="@+id/footer_insecure_indicator"
android:layout_width="12dp" android:layout_width="12dp"

View File

@ -5,11 +5,13 @@
<FrameLayout android:layout_width="match_parent" <FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1"> android:layout_weight="1">
<!--
<com.google.android.gms.maps.MapView <com.google.android.gms.maps.MapView
android:id="@+id/map_view" android:id="@+id/map_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone"/> android:visibility="gone"/>
-->
<ImageView android:id="@+id/image_view" <ImageView android:id="@+id/image_view"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="google_app_id" translatable="false">1:312334754206:android:a9297b152879f266</string>
<string name="gcm_defaultSenderId" translatable="false">312334754206</string>
<string name="default_web_client_id" translatable="false">312334754206-dg1p1mtekis8ivja3ica50vonmrlunh4.apps.googleusercontent.com</string>
<string name="firebase_database_url" translatable="false">https://api-project-312334754206.firebaseio.com</string>
<string name="google_api_key" translatable="false">AIzaSyDrfzNAPBPzX6key51hqo3p5LZXF5Y-yxU</string>
<string name="google_crash_reporting_api_key" translatable="false">AIzaSyDrfzNAPBPzX6key51hqo3p5LZXF5Y-yxU</string>
<string name="project_id" translatable="false">api-project-312334754206</string>
</resources>

View File

@ -16,7 +16,6 @@
*/ */
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.arch.lifecycle.DefaultLifecycleObserver; import android.arch.lifecycle.DefaultLifecycleObserver;
import android.arch.lifecycle.LifecycleOwner; import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.ProcessLifecycleOwner; import android.arch.lifecycle.ProcessLifecycleOwner;
@ -31,8 +30,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.multidex.MultiDexApplication; import android.support.multidex.MultiDexApplication;
import com.google.android.gms.security.ProviderInstaller;
import org.conscrypt.Conscrypt; import org.conscrypt.Conscrypt;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.signal.aesgcmprovider.AesGcmProvider; import org.signal.aesgcmprovider.AesGcmProvider;
@ -53,7 +50,6 @@ import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer; import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.FastJobStorage; import org.thoughtcrime.securesms.jobs.FastJobStorage;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.JobManagerFactories; import org.thoughtcrime.securesms.jobs.JobManagerFactories;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob; import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
@ -115,7 +111,6 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import dagger.ObjectGraph; import dagger.ObjectGraph;
import kotlin.Unit; import kotlin.Unit;
@ -176,10 +171,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
initializeExpiringMessageManager(); initializeExpiringMessageManager();
initializeTypingStatusRepository(); initializeTypingStatusRepository();
initializeTypingStatusSender(); initializeTypingStatusSender();
initializeGcmCheck();
initializeSignedPreKeyCheck(); initializeSignedPreKeyCheck();
initializePeriodicTasks(); initializePeriodicTasks();
initializeCircumvention();
initializeWebRtc(); initializeWebRtc();
initializePendingMessages(); initializePendingMessages();
initializeUnidentifiedDeliveryAbilityRefresh(); initializeUnidentifiedDeliveryAbilityRefresh();
@ -194,6 +187,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
if (userHexEncodedPublicKey != null) { if (userHexEncodedPublicKey != null) {
if (TextSecurePreferences.getNeedsIsRevokedSlaveDeviceCheck(this)) { if (TextSecurePreferences.getNeedsIsRevokedSlaveDeviceCheck(this)) {
MultiDeviceUtilities.checkIsRevokedSlaveDevice(this); MultiDeviceUtilities.checkIsRevokedSlaveDevice(this);
} else {
// We always update our current device links onto the server in case we failed to do so upon linking
MultiDeviceUtilities.updateDeviceLinksOnServer(this);
} }
} }
} }
@ -337,16 +333,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
this.objectGraph = ObjectGraph.create(communicationModule, new AxolotlStorageModule(this)); this.objectGraph = ObjectGraph.create(communicationModule, new AxolotlStorageModule(this));
} }
private void initializeGcmCheck() {
if (TextSecurePreferences.isPushRegistered(this)) {
long nextSetTime = TextSecurePreferences.getFcmTokenLastSetTime(this) + TimeUnit.HOURS.toMillis(6);
if (TextSecurePreferences.getFcmToken(this) == null || nextSetTime <= System.currentTimeMillis()) {
this.jobManager.add(new FcmRefreshJob());
}
}
}
private void initializeSignedPreKeyCheck() { private void initializeSignedPreKeyCheck() {
if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) { if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) {
jobManager.add(new CreateSignedPreKeyJob(this)); jobManager.add(new CreateSignedPreKeyJob(this));
@ -413,25 +399,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
} }
} }
@SuppressLint("StaticFieldLeak")
private void initializeCircumvention() {
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
if (new SignalServiceNetworkAccess(ApplicationContext.this).isCensored(ApplicationContext.this)) {
try {
ProviderInstaller.installIfNeeded(ApplicationContext.this);
} catch (Throwable t) {
Log.w(TAG, t);
}
}
return null;
}
};
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void executePendingContactSync() { private void executePendingContactSync() {
if (TextSecurePreferences.needsFullContactSync(this)) { if (TextSecurePreferences.needsFullContactSync(this)) {
ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true)); ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true));

View File

@ -1,30 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* 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 org.thoughtcrime.securesms;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class PlayServicesProblemActivity extends FragmentActivity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
PlayServicesProblemFragment fragment = new PlayServicesProblemFragment();
fragment.show(getSupportFragmentManager(), "dialog");
}
}

View File

@ -1,67 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* 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 org.thoughtcrime.securesms;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import com.google.android.gms.common.GoogleApiAvailability;
import network.loki.messenger.R;
public class PlayServicesProblemFragment extends DialogFragment {
@Override
public @NonNull Dialog onCreateDialog(@Nullable Bundle bundle) {
int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getActivity());
Dialog dialog = GoogleApiAvailability.getInstance().getErrorDialog(getActivity(), code, 9111);
if (dialog == null) {
return new AlertDialog.Builder(requireActivity())
.setNegativeButton(android.R.string.ok, null)
.setMessage(R.string.PlayServicesProblemFragment_the_version_of_google_play_services_you_have_installed_is_not_functioning)
.create();
} else {
return dialog;
}
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
finish();
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
finish();
}
private void finish() {
Activity activity = getActivity();
if (activity != null) activity.finish();
}
}

View File

@ -3,10 +3,8 @@ package org.thoughtcrime.securesms;
import android.Manifest; import android.Manifest;
import android.animation.Animator; import android.animation.Animator;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -29,17 +27,6 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.dd.CircularProgressButton; import com.dd.CircularProgressButton;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.tasks.Task;
import com.google.i18n.phonenumbers.AsYouTypeFormatter;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabase;
@ -62,7 +49,6 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.NoExternalStorageException; import org.thoughtcrime.securesms.database.NoExternalStorageException;
import org.thoughtcrime.securesms.gcm.FcmUtil;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.RotateCertificateJob; import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
import org.thoughtcrime.securesms.lock.RegistrationLockReminders; import org.thoughtcrime.securesms.lock.RegistrationLockReminders;
@ -73,12 +59,9 @@ import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.registration.CaptchaActivity; import org.thoughtcrime.securesms.registration.CaptchaActivity;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener; import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.VerificationCodeParser;
import org.thoughtcrime.securesms.util.BackupUtil; import org.thoughtcrime.securesms.util.BackupUtil;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.PlayServicesUtil;
import org.thoughtcrime.securesms.util.PlayServicesUtil.PlayServicesStatus;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -121,7 +104,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
private static final String TAG = RegistrationActivity.class.getSimpleName(); private static final String TAG = RegistrationActivity.class.getSimpleName();
private AsYouTypeFormatter countryFormatter;
private ArrayAdapter<String> countrySpinnerAdapter; private ArrayAdapter<String> countrySpinnerAdapter;
private Spinner countrySpinner; private Spinner countrySpinner;
private LabeledEditText countryCode; private LabeledEditText countryCode;
@ -149,7 +131,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
private VerificationPinKeyboard keyboard; private VerificationPinKeyboard keyboard;
private VerificationCodeView verificationCodeView; private VerificationCodeView verificationCodeView;
private RegistrationState registrationState; private RegistrationState registrationState;
private SmsRetrieverReceiver smsRetrieverReceiver;
private SignalServiceAccountManager accountManager; private SignalServiceAccountManager accountManager;
private int debugTapCounter; private int debugTapCounter;
@ -163,13 +144,11 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
initializeSpinner(); initializeSpinner();
initializeNumber(); initializeNumber();
initializeBackupDetection(); initializeBackupDetection();
initializeChallengeListener();
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
shutdownChallengeListener();
markAsVerifying(false); markAsVerifying(false);
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
} }
@ -179,7 +158,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
if (requestCode == PICK_COUNTRY && resultCode == RESULT_OK && data != null) { if (requestCode == PICK_COUNTRY && resultCode == RESULT_OK && data != null) {
this.countryCode.setText(String.valueOf(data.getIntExtra("country_code", 1))); this.countryCode.setText(String.valueOf(data.getIntExtra("country_code", 1)));
setCountryDisplay(data.getStringExtra("country_name")); setCountryDisplay(data.getStringExtra("country_name"));
setCountryFormatter(data.getIntExtra("country_code", 1));
} else if (requestCode == CAPTCHA && resultCode == RESULT_OK && data != null) { } else if (requestCode == CAPTCHA && resultCode == RESULT_OK && data != null) {
registrationState = new RegistrationState(Optional.fromNullable(data.getStringExtra(CaptchaActivity.KEY_TOKEN)), registrationState); registrationState = new RegistrationState(Optional.fromNullable(data.getStringExtra(CaptchaActivity.KEY_TOKEN)), registrationState);
@ -286,24 +264,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
}); });
} }
@SuppressLint("MissingPermission")
private void initializeNumber() { private void initializeNumber() {
Optional<Phonenumber.PhoneNumber> localNumber = Optional.absent();
if (Permissions.hasAll(this, Manifest.permission.READ_PHONE_STATE)) {
localNumber = Util.getDeviceNumber(this);
}
if (localNumber.isPresent()) {
this.countryCode.setText(String.valueOf(localNumber.get().getCountryCode()));
this.number.setText(String.valueOf(localNumber.get().getNationalNumber()));
} else {
Optional<String> simCountryIso = Util.getSimCountryIso(this);
if (simCountryIso.isPresent() && !TextUtils.isEmpty(simCountryIso.get())) {
this.countryCode.setText(String.valueOf(PhoneNumberUtil.getInstance().getCountryCodeForRegion(simCountryIso.get())));
}
}
} }
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
@ -338,14 +299,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
this.countrySpinnerAdapter.add(value); this.countrySpinnerAdapter.add(value);
} }
private void setCountryFormatter(int countryCode) {
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
String regionCode = util.getRegionCodeForCountryCode(countryCode);
if (regionCode == null) this.countryFormatter = null;
else this.countryFormatter = util.getAsYouTypeFormatter(regionCode);
}
private String getConfiguredE164Number() { private String getConfiguredE164Number() {
return PhoneNumberFormatter.formatE164(countryCode.getText().toString(), return PhoneNumberFormatter.formatE164(countryCode.getText().toString(),
number.getText().toString()); number.getText().toString());
@ -436,20 +389,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
Dialogs.showAlertDialog(this, Dialogs.showAlertDialog(this,
getString(R.string.RegistrationActivity_invalid_number), getString(R.string.RegistrationActivity_invalid_number),
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), e164number)); String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), e164number));
return;
}
PlayServicesStatus gcmStatus = PlayServicesUtil.getPlayServicesStatus(this);
if (gcmStatus == PlayServicesStatus.SUCCESS) {
handleRequestVerification(e164number, true);
} else if (gcmStatus == PlayServicesStatus.MISSING) {
handlePromptForNoPlayServices(e164number);
} else if (gcmStatus == PlayServicesStatus.NEEDS_UPDATE) {
GoogleApiAvailability.getInstance().getErrorDialog(this, ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED, 0).show();
} else {
Dialogs.showAlertDialog(this, getString(R.string.RegistrationActivity_play_services_error),
getString(R.string.RegistrationActivity_google_play_services_is_updating_or_unavailable));
} }
} }
@ -457,22 +396,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
createButton.setIndeterminateProgressMode(true); createButton.setIndeterminateProgressMode(true);
createButton.setProgress(50); createButton.setProgress(50);
if (gcmSupported) {
SmsRetrieverClient client = SmsRetriever.getClient(this);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(none -> {
Log.i(TAG, "Successfully registered SMS listener.");
requestVerificationCode(e164number, true, true);
});
task.addOnFailureListener(e -> {
Log.w(TAG, "Failed to register SMS listener.", e);
requestVerificationCode(e164number, true, false);
});
} else {
requestVerificationCode(e164number, false, false); requestVerificationCode(e164number, false, false);
}
} }
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
@ -485,13 +409,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
String password = Util.getSecret(18); String password = Util.getSecret(18);
Optional<String> fcmToken; Optional<String> fcmToken = Optional.absent();
if (gcmSupported) {
fcmToken = FcmUtil.getToken();
} else {
fcmToken = Optional.absent();
}
accountManager = AccountManagerFactory.createManager(RegistrationActivity.this, e164number, password); accountManager = AccountManagerFactory.createManager(RegistrationActivity.this, e164number, password);
accountManager.requestSmsVerificationCode(smsRetrieverSupported, registrationState.captchaToken); accountManager.requestSmsVerificationCode(smsRetrieverSupported, registrationState.captchaToken);
@ -939,19 +857,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
dialog.show(); dialog.show();
} }
private void initializeChallengeListener() {
smsRetrieverReceiver = new SmsRetrieverReceiver();
IntentFilter filter = new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION);
registerReceiver(smsRetrieverReceiver, filter);
}
private void shutdownChallengeListener() {
if (smsRetrieverReceiver != null) {
unregisterReceiver(smsRetrieverReceiver);
smsRetrieverReceiver = null;
}
}
private void markAsVerifying(boolean verifying) { private void markAsVerifying(boolean verifying) {
TextSecurePreferences.setVerifying(this, verifying); TextSecurePreferences.setVerifying(this, verifying);
@ -961,12 +866,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
} }
private String formatNumber(@NonNull String e164Number) { private String formatNumber(@NonNull String e164Number) {
try { return e164Number;
Phonenumber.PhoneNumber number = PhoneNumberUtil.getInstance().parse(e164Number, null);
return PhoneNumberUtil.getInstance().format(number, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL);
} catch (NumberParseException e) {
return e164Number;
}
} }
private void onWrongNumberClicked() { private void onWrongNumberClicked() {
@ -980,53 +880,16 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
else restoreBackupProgress.setText(getString(R.string.RegistrationActivity_d_messages_so_far, event.getCount())); else restoreBackupProgress.setText(getString(R.string.RegistrationActivity_d_messages_so_far, event.getCount()));
} }
private class SmsRetrieverReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "SmsRetrieverReceiver received a broadcast...");
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch (status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
Optional<String> code = VerificationCodeParser.parse(context, (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE));
if (code.isPresent()) {
Log.i(TAG, "Received verification code.");
handleVerificationCodeReceived(code.get());
} else {
Log.w(TAG, "Could not parse verification code.");
}
break;
case CommonStatusCodes.TIMEOUT:
Log.w(TAG, "Hit a timeout waiting for the SMS to arrive.");
break;
}
} else {
Log.w(TAG, "SmsRetrieverReceiver received the wrong action?");
}
}
}
private class CountryCodeChangedListener implements TextWatcher { private class CountryCodeChangedListener implements TextWatcher {
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
if (TextUtils.isEmpty(s) || !TextUtils.isDigitsOnly(s)) { if (TextUtils.isEmpty(s) || !TextUtils.isDigitsOnly(s)) {
setCountryDisplay(getString(R.string.RegistrationActivity_select_your_country)); setCountryDisplay(getString(R.string.RegistrationActivity_select_your_country));
countryFormatter = null;
return; return;
} }
int countryCode = Integer.parseInt(s.toString()); int countryCode = Integer.parseInt(s.toString());
String regionCode = PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(countryCode); setCountryDisplay("N/A");
setCountryFormatter(countryCode);
setCountryDisplay(PhoneNumberFormatter.getRegionDisplayName(regionCode));
if (!TextUtils.isEmpty(regionCode) && !regionCode.equals("ZZ")) {
number.requestFocus();
}
} }
@Override @Override
@ -1042,24 +905,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
if (countryFormatter == null)
return;
if (TextUtils.isEmpty(s))
return;
countryFormatter.clear();
String number = s.toString().replaceAll("[^\\d.]", "");
String formattedNumber = null;
for (int i=0;i<number.length();i++) {
formattedNumber = countryFormatter.inputDigit(number.charAt(i));
}
if (formattedNumber != null && !s.toString().equals(formattedNumber)) {
s.replace(0, s.length(), formattedNumber);
}
} }
@Override @Override

View File

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.components; package org.thoughtcrime.securesms.components;
import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
@ -14,22 +13,18 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Locale; import java.util.Locale;
import network.loki.messenger.R;
public class ConversationItemFooter extends LinearLayout { public class ConversationItemFooter extends LinearLayout {
private TextView dateView; private TextView dateView;
private TextView simView;
private ExpirationTimerView timerView; private ExpirationTimerView timerView;
private ImageView insecureIndicatorView; private ImageView insecureIndicatorView;
private DeliveryStatusView deliveryStatusView; private DeliveryStatusView deliveryStatusView;
@ -53,7 +48,6 @@ public class ConversationItemFooter extends LinearLayout {
inflate(getContext(), R.layout.conversation_item_footer, this); inflate(getContext(), R.layout.conversation_item_footer, this);
dateView = findViewById(R.id.footer_date); dateView = findViewById(R.id.footer_date);
simView = findViewById(R.id.footer_sim_info);
timerView = findViewById(R.id.footer_expiration_timer); timerView = findViewById(R.id.footer_expiration_timer);
insecureIndicatorView = findViewById(R.id.footer_insecure_indicator); insecureIndicatorView = findViewById(R.id.footer_insecure_indicator);
deliveryStatusView = findViewById(R.id.footer_delivery_status); deliveryStatusView = findViewById(R.id.footer_delivery_status);
@ -74,7 +68,6 @@ public class ConversationItemFooter extends LinearLayout {
public void setMessageRecord(@NonNull MessageRecord messageRecord, @NonNull Locale locale) { public void setMessageRecord(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
presentDate(messageRecord, locale); presentDate(messageRecord, locale);
presentSimInfo(messageRecord);
presentTimer(messageRecord); presentTimer(messageRecord);
presentInsecureIndicator(messageRecord); presentInsecureIndicator(messageRecord);
presentDeliveryStatus(messageRecord); presentDeliveryStatus(messageRecord);
@ -82,7 +75,6 @@ public class ConversationItemFooter extends LinearLayout {
public void setTextColor(int color) { public void setTextColor(int color) {
dateView.setTextColor(color); dateView.setTextColor(color);
simView.setTextColor(color);
} }
public void setIconColor(int color) { public void setIconColor(int color) {
@ -103,26 +95,6 @@ public class ConversationItemFooter extends LinearLayout {
} }
} }
private void presentSimInfo(@NonNull MessageRecord messageRecord) {
SubscriptionManagerCompat subscriptionManager = new SubscriptionManagerCompat(getContext());
if (messageRecord.isPush() || messageRecord.getSubscriptionId() == -1 || !Permissions.hasAll(getContext(), Manifest.permission.READ_PHONE_STATE) || !subscriptionManager.isMultiSim()) {
simView.setVisibility(View.GONE);
} else {
Optional<SubscriptionInfoCompat> subscriptionInfo = subscriptionManager.getActiveSubscriptionInfo(messageRecord.getSubscriptionId());
if (subscriptionInfo.isPresent() && messageRecord.isOutgoing()) {
simView.setText(getContext().getString(R.string.ConversationItem_from_s, subscriptionInfo.get().getDisplayName()));
simView.setVisibility(View.VISIBLE);
} else if (subscriptionInfo.isPresent()) {
simView.setText(getContext().getString(R.string.ConversationItem_to_s, subscriptionInfo.get().getDisplayName()));
simView.setVisibility(View.VISIBLE);
} else {
simView.setVisibility(View.GONE);
}
}
}
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
private void presentTimer(@NonNull final MessageRecord messageRecord) { private void presentTimer(@NonNull final MessageRecord messageRecord) {
if (messageRecord.getExpiresIn() > 0 && !messageRecord.isPending()) { if (messageRecord.getExpiresIn() > 0 && !messageRecord.isPending()) {

View File

@ -7,26 +7,18 @@ import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.MarkerOptions;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture; import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import network.loki.messenger.R;
public class SignalMapView extends LinearLayout { public class SignalMapView extends LinearLayout {
private MapView mapView;
private ImageView imageView; private ImageView imageView;
private TextView textView; private TextView textView;
@ -49,7 +41,6 @@ public class SignalMapView extends LinearLayout {
setOrientation(LinearLayout.VERTICAL); setOrientation(LinearLayout.VERTICAL);
LayoutInflater.from(context).inflate(R.layout.signal_map_view, this, true); LayoutInflater.from(context).inflate(R.layout.signal_map_view, this, true);
this.mapView = ViewUtil.findById(this, R.id.map_view);
this.imageView = ViewUtil.findById(this, R.id.image_view); this.imageView = ViewUtil.findById(this, R.id.image_view);
this.textView = ViewUtil.findById(this, R.id.address_view); this.textView = ViewUtil.findById(this, R.id.address_view);
} }
@ -57,39 +48,8 @@ public class SignalMapView extends LinearLayout {
public ListenableFuture<Bitmap> display(final SignalPlace place) { public ListenableFuture<Bitmap> display(final SignalPlace place) {
final SettableFuture<Bitmap> future = new SettableFuture<>(); final SettableFuture<Bitmap> future = new SettableFuture<>();
this.mapView.onCreate(null);
this.mapView.onResume();
this.mapView.setVisibility(View.VISIBLE);
this.imageView.setVisibility(View.GONE); this.imageView.setVisibility(View.GONE);
this.mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(final GoogleMap googleMap) {
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(place.getLatLong(), 13));
googleMap.addMarker(new MarkerOptions().position(place.getLatLong()));
googleMap.setBuildingsEnabled(true);
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
googleMap.getUiSettings().setAllGesturesEnabled(false);
googleMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
googleMap.snapshot(new GoogleMap.SnapshotReadyCallback() {
@Override
public void onSnapshotReady(Bitmap bitmap) {
future.set(bitmap);
imageView.setImageBitmap(bitmap);
imageView.setVisibility(View.VISIBLE);
mapView.setVisibility(View.GONE);
mapView.onPause();
mapView.onDestroy();
}
});
}
});
}
});
this.textView.setText(place.getDescription()); this.textView.setText(place.getDescription());
return future; return future;

View File

@ -7,8 +7,6 @@ import android.text.TextUtils;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.maps.model.LatLng;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.JsonUtils;
@ -17,6 +15,22 @@ import java.io.IOException;
public class SignalPlace { public class SignalPlace {
/* Loki - Temporary Placeholders */
class LatLng {
double latitude;
double longitude;
LatLng(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
}
class Place {
public CharSequence getName() { return ""; }
public CharSequence getAddress() { return ""; }
LatLng getLatLng() { return new LatLng(0, 0); }
}
private static final String URL = "https://maps.google.com/maps"; private static final String URL = "https://maps.google.com/maps";
private static final String TAG = SignalPlace.class.getSimpleName(); private static final String TAG = SignalPlace.class.getSimpleName();

View File

@ -1,49 +0,0 @@
package org.thoughtcrime.securesms.contacts;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.IOException;
public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
private static final String TAG = ContactsSyncAdapter.class.getSimpleName();
public ContactsSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult)
{
Log.i(TAG, "onPerformSync(" + authority +")");
if (TextSecurePreferences.isPushRegistered(getContext())) {
try {
DirectoryHelper.refreshDirectory(getContext(), true);
} catch (IOException e) {
Log.w(TAG, e);
}
}
}
@Override
public void onSyncCanceled() {
Log.w(TAG, "onSyncCanceled()");
}
@Override
public void onSyncCanceled(Thread thread) {
Log.w(TAG, "onSyncCanceled(" + thread + ")");
}
}

View File

@ -13,11 +13,7 @@ import android.support.v7.app.AlertDialog;
import android.text.TextUtils; import android.text.TextUtils;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.components.emoji.EmojiStrings; import org.thoughtcrime.securesms.components.emoji.EmojiStrings;
import org.thoughtcrime.securesms.contactshare.Contact.Email; import org.thoughtcrime.securesms.contactshare.Contact.Email;
import org.thoughtcrime.securesms.contactshare.Contact.Phone; import org.thoughtcrime.securesms.contactshare.Contact.Phone;
@ -34,6 +30,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import network.loki.messenger.R;
public final class ContactUtil { public final class ContactUtil {
private static final String TAG = ContactUtil.class.getSimpleName(); private static final String TAG = ContactUtil.class.getSimpleName();
@ -102,13 +100,7 @@ public final class ContactUtil {
} }
private static @NonNull String getPrettyPhoneNumber(@NonNull String phoneNumber, @NonNull Locale fallbackLocale) { private static @NonNull String getPrettyPhoneNumber(@NonNull String phoneNumber, @NonNull Locale fallbackLocale) {
PhoneNumberUtil util = PhoneNumberUtil.getInstance(); return phoneNumber;
try {
PhoneNumber parsed = util.parse(phoneNumber, fallbackLocale.getISO3Country());
return util.format(parsed, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL);
} catch (NumberParseException e) {
return phoneNumber;
}
} }
public static @NonNull String getNormalizedPhoneNumber(@NonNull Context context, @NonNull String number) { public static @NonNull String getNormalizedPhoneNumber(@NonNull Context context, @NonNull String number) {

View File

@ -77,7 +77,6 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import com.google.android.gms.location.places.ui.PlacePicker;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
@ -133,7 +132,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase; import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft; import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts; import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
@ -179,7 +177,6 @@ import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.LocationSlide; import org.thoughtcrime.securesms.mms.LocationSlide;
import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.QuoteId; import org.thoughtcrime.securesms.mms.QuoteId;
@ -662,10 +659,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipient.addListener(this); recipient.addListener(this);
fragment.reloadList(); fragment.reloadList();
break; break;
/*
case PICK_LOCATION: case PICK_LOCATION:
SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this)); SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this));
attachmentManager.setLocation(place, getCurrentMediaConstraints()); attachmentManager.setLocation(place, getCurrentMediaConstraints());
break; break;
*/
case PICK_GIF: case PICK_GIF:
setMedia(data.getData(), setMedia(data.getData(),
MediaType.GIF, MediaType.GIF,
@ -1240,15 +1239,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private boolean handleDisplayQuickContact() { private boolean handleDisplayQuickContact() {
if (recipient.getAddress().isGroup()) return false; return !recipient.getAddress().isGroup();
// if (recipient.getContactUri() != null) { // if (recipient.getContactUri() != null) {
// ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null); // ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
// } else { // } else {
// handleAddToContacts(); // handleAddToContacts();
// } // }
return true;
} }
private void handleAddAttachment() { private void handleAddAttachment() {

View File

@ -10,12 +10,6 @@ import android.support.annotation.VisibleForTesting;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Pair; import android.util.Pair;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.google.i18n.phonenumbers.ShortNumberInfo;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.NumberUtil;
@ -111,7 +105,7 @@ public class Address implements Parcelable, Comparable<Address> {
public boolean isGroup() { return GroupUtil.isEncodedGroup(address); } public boolean isGroup() { return GroupUtil.isEncodedGroup(address); }
public boolean isSignalGroup() { return !isPublicChat() && !isRSSFeed(); } public boolean isSignalGroup() { return GroupUtil.isSignalGroup(address); }
public boolean isPublicChat() { return GroupUtil.isPublicChat(address); } public boolean isPublicChat() { return GroupUtil.isPublicChat(address); }
@ -200,19 +194,10 @@ public class Address implements Parcelable, Comparable<Address> {
private final Optional<PhoneNumber> localNumber; private final Optional<PhoneNumber> localNumber;
private final String localCountryCode; private final String localCountryCode;
private final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]"); private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]");
ExternalAddressFormatter(@NonNull String localNumberString) { ExternalAddressFormatter(@NonNull String localNumberString) {
try { throw new AssertionError("Not Implemented");
Phonenumber.PhoneNumber libNumber = phoneNumberUtil.parse(localNumberString, null);
int countryCode = libNumber.getCountryCode();
this.localNumber = Optional.of(new PhoneNumber(localNumberString, countryCode, parseAreaCode(localNumberString, countryCode)));
this.localCountryCode = phoneNumberUtil.getRegionCodeForNumber(libNumber);
} catch (NumberParseException e) {
throw new AssertionError(e);
}
} }
ExternalAddressFormatter(@NonNull String localCountryCode, boolean countryCode) { ExternalAddressFormatter(@NonNull String localCountryCode, boolean countryCode) {
@ -222,61 +207,7 @@ public class Address implements Parcelable, Comparable<Address> {
public String format(@Nullable String number) { public String format(@Nullable String number) {
if (number == null) return "Unknown"; if (number == null) return "Unknown";
if (GroupUtil.isEncodedGroup(number)) return number; return number;
if (ALPHA_PATTERN.matcher(number).find()) return number.trim();
String bareNumber = number.replaceAll("[^0-9+]", "");
if (bareNumber.length() == 0) {
if (number.trim().length() == 0) return "Unknown";
else return number.trim();
}
// libphonenumber doesn't seem to be correct for Germany and Finland
if (bareNumber.length() <= 6 && ("DE".equals(localCountryCode) || "FI".equals(localCountryCode) || "SK".equals(localCountryCode))) {
return bareNumber;
}
// libphonenumber seems incorrect for Russia and a few other countries with 4 digit short codes.
if (bareNumber.length() <= 4 && !SHORT_COUNTRIES.contains(localCountryCode)) {
return bareNumber;
}
if (isShortCode(bareNumber, localCountryCode)) {
return bareNumber;
}
String processedNumber = applyAreaCodeRules(localNumber, bareNumber);
try {
Phonenumber.PhoneNumber parsedNumber = phoneNumberUtil.parse(processedNumber, localCountryCode);
return phoneNumberUtil.format(parsedNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
} catch (NumberParseException e) {
Log.w(TAG, e);
if (bareNumber.charAt(0) == '+')
return bareNumber;
String localNumberImprecise = localNumber.isPresent() ? localNumber.get().getE164Number() : "";
if (localNumberImprecise.charAt(0) == '+')
localNumberImprecise = localNumberImprecise.substring(1);
if (localNumberImprecise.length() == bareNumber.length() || bareNumber.length() > localNumberImprecise.length())
return "+" + number;
int difference = localNumberImprecise.length() - bareNumber.length();
return "+" + localNumberImprecise.substring(0, difference) + bareNumber;
}
}
private boolean isShortCode(@NonNull String bareNumber, String localCountryCode) {
try {
Phonenumber.PhoneNumber parsedNumber = phoneNumberUtil.parse(bareNumber, localCountryCode);
return ShortNumberInfo.getInstance().isPossibleShortNumberForRegion(parsedNumber, localCountryCode);
} catch (NumberParseException e) {
return false;
}
} }
private @Nullable String parseAreaCode(@NonNull String e164Number, int countryCode) { private @Nullable String parseAreaCode(@NonNull String e164Number, int countryCode) {

View File

@ -1,24 +1,16 @@
package org.thoughtcrime.securesms.database.helpers; package org.thoughtcrime.securesms.database.helpers;
import android.Manifest;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import org.thoughtcrime.securesms.logging.Log;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.google.i18n.phonenumbers.ShortNumberInfo;
import org.thoughtcrime.securesms.DatabaseUpgradeActivity; import org.thoughtcrime.securesms.DatabaseUpgradeActivity;
import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.AttachmentSecret;
@ -26,7 +18,6 @@ import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase; import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
@ -37,8 +28,8 @@ import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.Hex;
@ -1385,71 +1376,18 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
add("AC"); add("AC");
}}; }};
private final Phonenumber.PhoneNumber localNumber;
private final String localNumberString; private final String localNumberString;
private final String localCountryCode;
private final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]"); private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]");
public NumberMigrator(String localNumber) { public NumberMigrator(String localNumber) {
try { this.localNumberString = localNumber;
this.localNumberString = localNumber;
this.localNumber = phoneNumberUtil.parse(localNumber, null);
this.localCountryCode = phoneNumberUtil.getRegionCodeForNumber(this.localNumber);
} catch (NumberParseException e) {
throw new AssertionError(e);
}
} }
public String migrate(@Nullable String number) { public String migrate(@Nullable String number) {
if (number == null) return "Unknown"; if (number == null) return "Unknown";
if (number.startsWith("__textsecure_group__!")) return number; return number;
if (ALPHA_PATTERN.matcher(number).find()) return number.trim();
String bareNumber = number.replaceAll("[^0-9+]", "");
if (bareNumber.length() == 0) {
if (TextUtils.isEmpty(number.trim())) return "Unknown";
else return number.trim();
}
// libphonenumber doesn't seem to be correct for Germany and Finland
if (bareNumber.length() <= 6 && ("DE".equals(localCountryCode) || "FI".equals(localCountryCode) || "SK".equals(localCountryCode))) {
return bareNumber;
}
// libphonenumber seems incorrect for Russia and a few other countries with 4 digit short codes.
if (bareNumber.length() <= 4 && !SHORT_COUNTRIES.contains(localCountryCode)) {
return bareNumber;
}
try {
Phonenumber.PhoneNumber parsedNumber = phoneNumberUtil.parse(bareNumber, localCountryCode);
if (ShortNumberInfo.getInstance().isPossibleShortNumberForRegion(parsedNumber, localCountryCode)) {
return bareNumber;
}
return phoneNumberUtil.format(parsedNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
} catch (NumberParseException e) {
Log.w(TAG, e);
if (bareNumber.charAt(0) == '+')
return bareNumber;
String localNumberImprecise = localNumberString;
if (localNumberImprecise.charAt(0) == '+')
localNumberImprecise = localNumberImprecise.substring(1);
if (localNumberImprecise.length() == bareNumber.length() || bareNumber.length() > localNumberImprecise.length())
return "+" + number;
int difference = localNumberImprecise.length() - bareNumber.length();
return "+" + localNumberImprecise.substring(0, difference) + bareNumber;
}
} }
} }

View File

@ -1,19 +1,12 @@
package org.thoughtcrime.securesms.database.loaders; package org.thoughtcrime.securesms.database.loaders;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import android.content.Context; import android.content.Context;
import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.AsyncTaskLoader;
import java.util.ArrayList;
import com.google.i18n.phonenumbers.PhoneNumberUtil; import java.util.Comparator;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.util.Map;
public class CountryListLoader extends AsyncTaskLoader<ArrayList<Map<String, String>>> { public class CountryListLoader extends AsyncTaskLoader<ArrayList<Map<String, String>>> {
@ -23,19 +16,7 @@ public class CountryListLoader extends AsyncTaskLoader<ArrayList<Map<String, Str
@Override @Override
public ArrayList<Map<String, String>> loadInBackground() { public ArrayList<Map<String, String>> loadInBackground() {
Set<String> regions = PhoneNumberUtil.getInstance().getSupportedRegions(); return new ArrayList<>();
ArrayList<Map<String, String>> results = new ArrayList<Map<String, String>>(regions.size());
for (String region : regions) {
Map<String, String> data = new HashMap<String, String>(2);
data.put("country_name", PhoneNumberFormatter.getRegionDisplayName(region));
data.put("country_code", "+" +PhoneNumberUtil.getInstance().getCountryCodeForRegion(region));
results.add(data);
}
Collections.sort(results, new RegionComparator());
return results;
} }
private static class RegionComparator implements Comparator<Map<String, String>> { private static class RegionComparator implements Comparator<Map<String, String>> {

View File

@ -9,13 +9,11 @@ import org.thoughtcrime.securesms.DeviceListFragment;
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent; import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.gcm.FcmService;
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob; import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
@ -48,6 +46,7 @@ import org.thoughtcrime.securesms.jobs.TypingSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation;
import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob;
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.push.MessageSenderEventListener; import org.thoughtcrime.securesms.push.MessageSenderEventListener;
@ -86,7 +85,6 @@ import network.loki.messenger.BuildConfig;
MultiDeviceBlockedUpdateJob.class, MultiDeviceBlockedUpdateJob.class,
DeviceListFragment.class, DeviceListFragment.class,
RefreshAttributesJob.class, RefreshAttributesJob.class,
FcmRefreshJob.class,
RequestGroupInfoJob.class, RequestGroupInfoJob.class,
PushGroupUpdateJob.class, PushGroupUpdateJob.class,
AvatarDownloadJob.class, AvatarDownloadJob.class,
@ -99,7 +97,6 @@ import network.loki.messenger.BuildConfig;
MultiDeviceProfileKeyUpdateJob.class, MultiDeviceProfileKeyUpdateJob.class,
SendReadReceiptJob.class, SendReadReceiptJob.class,
AppProtectionPreferenceFragment.class, AppProtectionPreferenceFragment.class,
FcmService.class,
RotateCertificateJob.class, RotateCertificateJob.class,
SendDeliveryReceiptJob.class, SendDeliveryReceiptJob.class,
RotateProfileKeyJob.class, RotateProfileKeyJob.class,
@ -115,7 +112,8 @@ import network.loki.messenger.BuildConfig;
MultiDeviceStickerPackOperationJob.class, MultiDeviceStickerPackOperationJob.class,
MultiDeviceStickerPackSyncJob.class, MultiDeviceStickerPackSyncJob.class,
LinkPreviewRepository.class, LinkPreviewRepository.class,
PushMessageSyncSendJob.class}) PushMessageSyncSendJob.class,
MultiDeviceOpenGroupUpdateJob.class})
public class SignalCommunicationModule { public class SignalCommunicationModule {

View File

@ -1,158 +0,0 @@
package org.thoughtcrime.securesms.gcm;
import android.content.Context;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import org.thoughtcrime.securesms.ApplicationContext;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.service.GenericForegroundService;
import org.thoughtcrime.securesms.util.PowerManagerCompat;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.WakeLockUtil;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.internal.util.Util;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
public class FcmService extends FirebaseMessagingService implements InjectableType {
private static final String TAG = FcmService.class.getSimpleName();
private static final Executor MESSAGE_EXECUTOR = SignalExecutors.newCachedSingleThreadExecutor("FcmMessageProcessing");
private static final String WAKE_LOCK_TAG = "FcmMessageProcessing";
@Inject SignalServiceMessageReceiver messageReceiver;
private static int activeCount;
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.i(TAG, "FCM message... Original Priority: " + remoteMessage.getOriginalPriority() + ", Actual Priority: " + remoteMessage.getPriority());
ApplicationContext.getInstance(getApplicationContext()).injectDependencies(this);
WakeLockUtil.runWithLock(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK, 60000, WAKE_LOCK_TAG, () -> {
handleReceivedNotification(getApplicationContext());
});
}
@Override
public void onNewToken(String token) {
Log.i(TAG, "onNewToken()");
if (!TextSecurePreferences.isPushRegistered(getApplicationContext())) {
Log.i(TAG, "Got a new FCM token, but the user isn't registered.");
return;
}
ApplicationContext.getInstance(getApplicationContext())
.getJobManager()
.add(new FcmRefreshJob());
}
private void handleReceivedNotification(Context context) {
if (!incrementActiveGcmCount()) {
Log.i(TAG, "Skipping FCM processing -- there's already one enqueued.");
return;
}
TextSecurePreferences.setNeedsMessagePull(context, true);
long startTime = System.currentTimeMillis();
PowerManager powerManager = ServiceUtil.getPowerManager(getApplicationContext());
boolean doze = PowerManagerCompat.isDeviceIdleMode(powerManager);
boolean network = new NetworkConstraint.Factory(ApplicationContext.getInstance(context)).create().isMet();
final Object foregroundLock = new Object();
final AtomicBoolean foregroundRunning = new AtomicBoolean(false);
final AtomicBoolean taskCompleted = new AtomicBoolean(false);
final CountDownLatch latch = new CountDownLatch(1);
if (doze || !network) {
Log.i(TAG, "Starting a foreground task because we may be operating in a constrained environment. Doze: " + doze + " Network: " + network);
showForegroundNotification(context);
foregroundRunning.set(true);
latch.countDown();
}
MESSAGE_EXECUTOR.execute(() -> {
try {
new PushNotificationReceiveJob(context).pullAndProcessMessages(messageReceiver, TAG, startTime);
} catch (IOException e) {
Log.i(TAG, "Failed to retrieve the envelope. Scheduling on JobManager.", e);
ApplicationContext.getInstance(context)
.getJobManager()
.add(new PushNotificationReceiveJob(context));
} finally {
synchronized (foregroundLock) {
if (foregroundRunning.getAndSet(false)) {
GenericForegroundService.stopForegroundTask(context);
} else {
latch.countDown();
}
taskCompleted.set(true);
}
decrementActiveGcmCount();
Log.i(TAG, "Processing complete.");
}
});
if (!foregroundRunning.get()) {
new Thread("FcmForegroundServiceTimer") {
@Override
public void run() {
Util.sleep(7000);
synchronized (foregroundLock) {
if (!taskCompleted.get() && !foregroundRunning.getAndSet(true)) {
Log.i(TAG, "Starting a foreground task because the job is running long.");
showForegroundNotification(context);
latch.countDown();
}
}
}
}.start();
}
try {
latch.await();
} catch (InterruptedException e) {
Log.w(TAG, "Latch was interrupted.", e);
}
}
private void showForegroundNotification(@NonNull Context context) {
GenericForegroundService.startForegroundTask(context,
context.getString(R.string.GcmBroadcastReceiver_retrieving_a_message),
NotificationChannels.OTHER,
R.drawable.ic_signal_downloading);
}
private static synchronized boolean incrementActiveGcmCount() {
if (activeCount < 2) {
activeCount++;
return true;
}
return false;
}
private static synchronized void decrementActiveGcmCount() {
activeCount--;
}
}

View File

@ -1,44 +0,0 @@
package org.thoughtcrime.securesms.gcm;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import com.google.firebase.iid.FirebaseInstanceId;
import org.thoughtcrime.securesms.logging.Log;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public final class FcmUtil {
private static final String TAG = FcmUtil.class.getSimpleName();
/**
* Retrieves the current FCM token. If one isn't available, it'll be generated.
*/
@WorkerThread
public static Optional<String> getToken() {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<String> token = new AtomicReference<>(null);
FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult() != null && !TextUtils.isEmpty(task.getResult().getToken())) {
token.set(task.getResult().getToken());
} else {
Log.w(TAG, "Failed to get the token.", task.getException());
}
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException e) {
Log.w(TAG, "Was interrupted while waiting for the token.");
}
return Optional.fromNullable(token.get());
}
}

View File

@ -9,7 +9,6 @@ import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob; import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.LocalBackupJob; import org.thoughtcrime.securesms.jobs.LocalBackupJob;
import org.thoughtcrime.securesms.jobs.MmsDownloadJob; import org.thoughtcrime.securesms.jobs.MmsDownloadJob;
import org.thoughtcrime.securesms.jobs.MmsReceiveJob; import org.thoughtcrime.securesms.jobs.MmsReceiveJob;
@ -59,7 +58,6 @@ public class WorkManagerFactoryMappings {
put(CleanPreKeysJob.class.getName(), CleanPreKeysJob.KEY); put(CleanPreKeysJob.class.getName(), CleanPreKeysJob.KEY);
put(CreateSignedPreKeyJob.class.getName(), CreateSignedPreKeyJob.KEY); put(CreateSignedPreKeyJob.class.getName(), CreateSignedPreKeyJob.KEY);
put(DirectoryRefreshJob.class.getName(), DirectoryRefreshJob.KEY); put(DirectoryRefreshJob.class.getName(), DirectoryRefreshJob.KEY);
put(FcmRefreshJob.class.getName(), FcmRefreshJob.KEY);
put(LocalBackupJob.class.getName(), LocalBackupJob.KEY); put(LocalBackupJob.class.getName(), LocalBackupJob.KEY);
put(MmsDownloadJob.class.getName(), MmsDownloadJob.KEY); put(MmsDownloadJob.class.getName(), MmsDownloadJob.KEY);
put(MmsReceiveJob.class.getName(), MmsReceiveJob.KEY); put(MmsReceiveJob.class.getName(), MmsReceiveJob.KEY);

View File

@ -1,151 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* 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 org.thoughtcrime.securesms.jobs;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import org.thoughtcrime.securesms.gcm.FcmUtil;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.PlayServicesProblemActivity;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
public class FcmRefreshJob extends BaseJob implements InjectableType {
public static final String KEY = "FcmRefreshJob";
private static final String TAG = FcmRefreshJob.class.getSimpleName();
@Inject SignalServiceAccountManager textSecureAccountManager;
public FcmRefreshJob() {
this(new Job.Parameters.Builder()
.setQueue("FcmRefreshJob")
.addConstraint(NetworkConstraint.KEY)
.setMaxAttempts(1)
.setLifespan(TimeUnit.MINUTES.toMillis(5))
.setMaxInstances(1)
.build());
}
private FcmRefreshJob(@NonNull Job.Parameters parameters) {
super(parameters);
}
@Override
public @NonNull Data serialize() {
return Data.EMPTY;
}
@Override
public @NonNull String getFactoryKey() {
return KEY;
}
@Override
public void onRun() throws Exception {
if (TextSecurePreferences.isFcmDisabled(context)) return;
Log.i(TAG, "Reregistering FCM...");
int result = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
if (result != ConnectionResult.SUCCESS) {
notifyFcmFailure();
} else {
Optional<String> token = FcmUtil.getToken();
if (token.isPresent()) {
String oldToken = TextSecurePreferences.getFcmToken(context);
if (!token.get().equals(oldToken)) {
int oldLength = oldToken != null ? oldToken.length() : -1;
Log.i(TAG, "Token changed. oldLength: " + oldLength + " newLength: " + token.get().length());
} else {
Log.i(TAG, "Token didn't change.");
}
textSecureAccountManager.setGcmId(token);
TextSecurePreferences.setFcmToken(context, token.get());
TextSecurePreferences.setFcmTokenLastSetTime(context, System.currentTimeMillis());
TextSecurePreferences.setWebsocketRegistered(context, true);
} else {
throw new RetryLaterException(new IOException("Failed to retrieve a token."));
}
}
}
@Override
public void onCanceled() {
Log.w(TAG, "GCM reregistration failed after retry attempt exhaustion!");
}
@Override
public boolean onShouldRetry(@NonNull Exception throwable) {
if (throwable instanceof NonSuccessfulResponseCodeException) return false;
return true;
}
private void notifyFcmFailure() {
Intent intent = new Intent(context, PlayServicesProblemActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 1122, intent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationChannels.FAILURES);
builder.setSmallIcon(R.drawable.ic_notification);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_action_warning_red));
builder.setContentTitle(context.getString(R.string.GcmRefreshJob_Permanent_Signal_communication_failure));
builder.setContentText(context.getString(R.string.GcmRefreshJob_Signal_was_unable_to_register_with_Google_Play_Services));
builder.setTicker(context.getString(R.string.GcmRefreshJob_Permanent_Signal_communication_failure));
builder.setVibrate(new long[] {0, 1000});
builder.setContentIntent(pendingIntent);
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(12, builder.build());
}
public static final class Factory implements Job.Factory<FcmRefreshJob> {
@Override
public @NonNull FcmRefreshJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new FcmRefreshJob(parameters);
}
}
}

View File

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob;
import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob; import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob;
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
@ -31,7 +32,6 @@ public final class JobManagerFactories {
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory()); put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory()); put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory(application)); put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory(application));
put(FcmRefreshJob.KEY, new FcmRefreshJob.Factory());
put(LocalBackupJob.KEY, new LocalBackupJob.Factory()); put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory()); put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());
put(MmsReceiveJob.KEY, new MmsReceiveJob.Factory()); put(MmsReceiveJob.KEY, new MmsReceiveJob.Factory());
@ -74,6 +74,7 @@ public final class JobManagerFactories {
put(UpdateApkJob.KEY, new UpdateApkJob.Factory()); put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
put(PushMessageSyncSendJob.KEY, new PushMessageSyncSendJob.Factory()); put(PushMessageSyncSendJob.KEY, new PushMessageSyncSendJob.Factory());
put(PushBackgroundMessageSendJob.KEY, new PushBackgroundMessageSendJob.Factory()); put(PushBackgroundMessageSendJob.KEY, new PushBackgroundMessageSendJob.Factory());
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
}}; }};
} }

View File

@ -245,7 +245,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
} }
private List<ContactData> getAllContacts() { private List<ContactData> getAllContacts() {
List<Address> contactAddresses = DatabaseFactory.getRecipientDatabase(context).getRegistered(); List<Address> contactAddresses = new ArrayList<>(DatabaseFactory.getRecipientDatabase(context).getAllAddresses());
List<ContactData> contacts = new ArrayList<>(contactAddresses.size()); List<ContactData> contacts = new ArrayList<>(contactAddresses.size());
for (Address address : contactAddresses) { for (Address address : contactAddresses) {
if (!address.isPhone()) { continue; } if (!address.isPhone()) { continue; }

View File

@ -59,6 +59,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord; import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupMessageProcessor; import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
@ -72,6 +73,7 @@ import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation;
import org.thoughtcrime.securesms.loki.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.loki.redesign.utilities.OpenGroupUtilities;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities; import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase; import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase;
@ -138,6 +140,8 @@ import org.whispersystems.signalservice.loki.api.DeviceLink;
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession; import org.whispersystems.signalservice.loki.api.DeviceLinkingSession;
import org.whispersystems.signalservice.loki.api.LokiAPI; import org.whispersystems.signalservice.loki.api.LokiAPI;
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities; import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.api.LokiPublicChat;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage; import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage;
@ -393,6 +397,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get()); else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
else if (syncMessage.getContacts().isPresent()) handleContactSyncMessage(syncMessage.getContacts().get()); else if (syncMessage.getContacts().isPresent()) handleContactSyncMessage(syncMessage.getContacts().get());
else if (syncMessage.getGroups().isPresent()) handleGroupSyncMessage(content, syncMessage.getGroups().get()); else if (syncMessage.getGroups().isPresent()) handleGroupSyncMessage(content, syncMessage.getGroups().get());
else if (syncMessage.getOpenGroups().isPresent()) handleOpenGroupSyncMessage(syncMessage.getOpenGroups().get());
else Log.w(TAG, "Contains no known sync types..."); else Log.w(TAG, "Contains no known sync types...");
} else if (content.getCallMessage().isPresent()) { } else if (content.getCallMessage().isPresent()) {
Log.i(TAG, "Got call message..."); Log.i(TAG, "Got call message...");
@ -704,6 +709,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID); LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) { if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
// TODO: We should ensure that our mapping has been uploaded to the server before sending out this message
MessageSender.sendBackgroundFriendRequest(context, hexEncodedPublicKey, "Please accept to enable messages to be synced across devices"); MessageSender.sendBackgroundFriendRequest(context, hexEncodedPublicKey, "Please accept to enable messages to be synced across devices");
Log.d("Loki", "Sent friend request to " + hexEncodedPublicKey); Log.d("Loki", "Sent friend request to " + hexEncodedPublicKey);
} else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) { } else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
@ -749,6 +755,24 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
} }
private void handleOpenGroupSyncMessage(@NonNull List<LokiPublicChat> openGroups) {
try {
for (LokiPublicChat openGroup : openGroups) {
long threadID = GroupManager.getPublicChatThreadId(openGroup.getId(), context);
if (threadID > -1) continue;
String url = openGroup.getServer();
long channel = openGroup.getChannel();
OpenGroupUtilities.addGroup(context, url, channel).fail(e -> {
Log.d("Loki", "Failed to sync open group: " + url + " due to error: " + e + ".");
return Unit.INSTANCE;
});
}
} catch (Exception e) {
Log.d("Loki", "Failed to sync open groups due to error: " + e + ".");
}
}
private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content, private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content,
@NonNull SentTranscriptMessage message) @NonNull SentTranscriptMessage message)
throws StorageFailedException throws StorageFailedException
@ -1206,6 +1230,17 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
TextSecurePreferences.setMultiDevice(context, true); TextSecurePreferences.setMultiDevice(context, true);
// Send a background message to the master device // Send a background message to the master device
MessageSender.sendBackgroundMessage(context, deviceLink.getMasterHexEncodedPublicKey()); MessageSender.sendBackgroundMessage(context, deviceLink.getMasterHexEncodedPublicKey());
/*
Update device link on the file server.
We put this here because after receiving the authorisation message, we will also receive all sync messages.
If these sync messages are contact syncs then we need to send them friend requests so that we can establish multi-device communication.
If our device mapping is not stored on the server before the other party receives our message, they will think that they got a friend request from a non-multi-device user.
*/
try {
PromiseUtil.timeout(LokiFileServerAPI.shared.addDeviceLink(deviceLink), 8000).get();
} catch (Exception e) {
Log.w("Loki", "Failed to upload device links to the file server! " + e);
}
// Update display name if needed // Update display name if needed
if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) { if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) {
TextSecurePreferences.setProfileName(context, content.senderDisplayName.get()); TextSecurePreferences.setProfileName(context, content.senderDisplayName.get());
@ -1218,7 +1253,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getContacts().isPresent()) { if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getContacts().isPresent()) {
handleContactSyncMessage(content.getSyncMessage().get().getContacts().get()); handleContactSyncMessage(content.getSyncMessage().get().getContacts().get());
} }
// The device link is propagated to the file server in LandingActivity.onDeviceLinkAuthorized because we can handle the error there
} }
private void setDisplayName(String hexEncodedPublicKey, String profileName) { private void setDisplayName(String hexEncodedPublicKey, String profileName) {

View File

@ -0,0 +1,80 @@
package org.thoughtcrime.securesms.loki
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.dependencies.InjectableType
import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.jobmanager.Data
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.jobs.BaseJob
import org.thoughtcrime.securesms.logging.Log
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.SignalServiceMessageSender
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage
import org.whispersystems.signalservice.loki.api.LokiPublicChat
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters), InjectableType {
companion object {
const val KEY = "MultiDeviceOpenGroupUpdateJob"
}
@Inject
lateinit var messageSender: SignalServiceMessageSender
constructor() : this(Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setQueue("MultiDeviceOpenGroupUpdateJob")
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(Parameters.UNLIMITED)
.build())
override fun getFactoryKey(): String { return KEY }
override fun serialize(): Data { return Data.EMPTY }
@Throws(Exception::class)
public override fun onRun() {
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.d("Loki", "Not multi device; aborting...")
return
}
val openGroups = mutableListOf<LokiPublicChat>()
DatabaseFactory.getGroupDatabase(context).groups.use { reader ->
while (true) {
val record = reader.next ?: return@use
if (!record.isPublicChat) { continue; }
val threadID = GroupManager.getThreadIdFromGroupId(record.encodedId, context)
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
if (openGroup != null) {
openGroups.add(openGroup)
}
}
}
if (openGroups.size > 0) {
messageSender.sendMessage(0, SignalServiceSyncMessage.forOpenGroups(openGroups),
UnidentifiedAccessUtil.getAccessForSync(context))
} else {
Log.d("Loki", "No open groups to sync.")
}
}
public override fun onShouldRetry(exception: Exception): Boolean {
return false
}
override fun onCanceled() { }
class Factory : Job.Factory<MultiDeviceOpenGroupUpdateJob> {
override fun create(parameters: Parameters, data: Data): MultiDeviceOpenGroupUpdateJob {
return MultiDeviceOpenGroupUpdateJob(parameters)
}
}
}

View File

@ -42,6 +42,12 @@ fun checkIsRevokedSlaveDevice(context: Context) {
} }
} }
fun updateDeviceLinksOnServer(context: Context) {
val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val deviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(hexEncodedPublicKey)
LokiFileServerAPI.shared.setDeviceLinks(deviceLinks)
}
fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> { fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(hexEncodedPublicKey).map { keys -> return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(hexEncodedPublicKey).map { keys ->

View File

@ -194,7 +194,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
private fun openConversation(thread: ThreadRecord) { private fun openConversation(thread: ThreadRecord) {
val intent = Intent(this, ConversationActivity::class.java) val intent = Intent(this, ConversationActivity::class.java)
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, thread.recipient.getAddress()) intent.putExtra(ConversationActivity.ADDRESS_EXTRA, thread.recipient.address)
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, thread.threadId) intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, thread.threadId)
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, thread.distributionType) intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, thread.distributionType)
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis()) intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis())
@ -255,9 +255,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val dialog = AlertDialog.Builder(activity) val dialog = AlertDialog.Builder(activity)
dialog.setMessage(dialogMessage) dialog.setMessage(dialogMessage)
dialog.setPositiveButton(R.string.yes) { _, _ -> dialog.setPositiveButton(R.string.yes) { _, _ ->
val isGroup = recipient.isGroupRecipient val isClosedGroup = recipient.address.isSignalGroup
// Send a leave group message if this is an active closed group // Send a leave group message if this is an active closed group
if (isGroup && DatabaseFactory.getGroupDatabase(activity).isActive(recipient.address.toGroupString())) { if (isClosedGroup && DatabaseFactory.getGroupDatabase(activity).isActive(recipient.address.toGroupString())) {
if (!GroupUtil.leaveGroup(activity, recipient)) { if (!GroupUtil.leaveGroup(activity, recipient)) {
Toast.makeText(activity, "Couldn't leave group", Toast.LENGTH_LONG).show() Toast.makeText(activity, "Couldn't leave group", Toast.LENGTH_LONG).show()
clearView(activity.recyclerView, viewHolder) clearView(activity.recyclerView, viewHolder)
@ -267,10 +267,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
// Archive the conversation and then delete it after 10 seconds (the case where the // Archive the conversation and then delete it after 10 seconds (the case where the
// app was closed before the conversation could be deleted is handled in onCreate) // app was closed before the conversation could be deleted is handled in onCreate)
threadDatabase.archiveConversation(threadID) threadDatabase.archiveConversation(threadID)
val delay = if (isClosedGroup) 10000L else 1000L
val handler = Handler() val handler = Handler()
handler.postDelayed(deleteThread, 10000) handler.postDelayed(deleteThread, delay)
// Notify the user // Notify the user
val toastMessage = if (isGroup) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message
Toast.makeText(activity, toastMessage, Toast.LENGTH_LONG).show() Toast.makeText(activity, toastMessage, Toast.LENGTH_LONG).show()
} }
dialog.setNegativeButton(R.string.no) { _, _ -> dialog.setNegativeButton(R.string.no) { _, _ ->

View File

@ -16,14 +16,12 @@ import kotlinx.android.synthetic.main.fragment_enter_chat_url.*
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.ui.failUi import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi import nl.komponents.kovenant.ui.successUi
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.loki.redesign.utilities.OpenGroupUtilities
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragment import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragment
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragmentDelegate import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragmentDelegate
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.sms.MessageSender
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
private val adapter = JoinPublicChatActivityAdapter(this) private val adapter = JoinPublicChatActivityAdapter(this)
@ -68,19 +66,11 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
return Toast.makeText(this, "Invalid URL", Toast.LENGTH_SHORT).show() return Toast.makeText(this, "Invalid URL", Toast.LENGTH_SHORT).show()
} }
showLoader() showLoader()
val application = ApplicationContext.getInstance(this)
val channel: Long = 1 val channel: Long = 1
val displayName = TextSecurePreferences.getProfileName(this) OpenGroupUtilities.addGroup(this, url, channel).success {
val lokiPublicChatAPI = application.lokiPublicChatAPI!! MessageSender.syncAllOpenGroups(this)
application.lokiPublicChatManager.addChat(url, channel).successUi { }.successUi {
DatabaseFactory.getLokiAPIDatabase(this).removeLastMessageServerID(channel, url)
DatabaseFactory.getLokiAPIDatabase(this).removeLastDeletionServerID(channel, url)
lokiPublicChatAPI.getMessages(channel, url)
lokiPublicChatAPI.setDisplayName(displayName, url)
lokiPublicChatAPI.join(channel, url)
val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(this)
val profileUrl: String? = TextSecurePreferences.getProfileAvatarUrl(this)
lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl)
finish() finish()
}.failUi { }.failUi {
hideLoader() hideLoader()

View File

@ -114,7 +114,6 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
} }
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
LokiFileServerAPI.shared.addDeviceLink(deviceLink)
TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterHexEncodedPublicKey) TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterHexEncodedPublicKey)
val intent = Intent(this, HomeActivity::class.java) val intent = Intent(this, HomeActivity::class.java)
show(intent) show(intent)

View File

@ -12,6 +12,7 @@ import android.view.View
import android.widget.Toast import android.widget.Toast
import kotlinx.android.synthetic.main.activity_linked_devices.* import kotlinx.android.synthetic.main.activity_linked_devices.*
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.ui.failUi import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi import nl.komponents.kovenant.ui.successUi
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
@ -23,6 +24,7 @@ import org.thoughtcrime.securesms.loki.signAndSendDeviceLinkMessage
import org.thoughtcrime.securesms.sms.MessageSender import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.DeviceLink import org.whispersystems.signalservice.loki.api.DeviceLink
import org.whispersystems.signalservice.loki.api.LokiAPI
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
import java.util.* import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
@ -143,23 +145,21 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
} }
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
LokiFileServerAPI.shared.addDeviceLink(deviceLink).success { LokiFileServerAPI.shared.addDeviceLink(deviceLink).bind(LokiAPI.sharedWorkContext) {
signAndSendDeviceLinkMessage(this, deviceLink).successUi { signAndSendDeviceLinkMessage(this, deviceLink)
LoaderManager.getInstance(this).restartLoader(0, null, this) }.successUi {
}.success { LoaderManager.getInstance(this).restartLoader(0, null, this)
TextSecurePreferences.setMultiDevice(this, true) }.success {
Timer().schedule(4000) { TextSecurePreferences.setMultiDevice(this, true)
MessageSender.syncAllGroups(this@LinkedDevicesActivity) Timer().schedule(4000) {
MessageSender.syncAllContacts(this@LinkedDevicesActivity, Address.fromSerialized(deviceLink.slaveHexEncodedPublicKey)) MessageSender.syncAllGroups(this@LinkedDevicesActivity)
} MessageSender.syncAllContacts(this@LinkedDevicesActivity, Address.fromSerialized(deviceLink.slaveHexEncodedPublicKey))
}.failUi { MessageSender.syncAllOpenGroups(this@LinkedDevicesActivity)
Toast.makeText(this, "Couldn't link device", Toast.LENGTH_LONG).show()
}.fail {
LokiFileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
} }
}.failUi { }.fail {
LokiFileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey) DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
}.failUi {
Toast.makeText(this, "Couldn't link device", Toast.LENGTH_LONG).show() Toast.makeText(this, "Couldn't link device", Toast.LENGTH_LONG).show()
} }
} }

View File

@ -0,0 +1,37 @@
package org.thoughtcrime.securesms.loki.redesign.utilities
import android.content.Context
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.then
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.LokiPublicChat
object OpenGroupUtilities {
@JvmStatic fun addGroup(context: Context, url: String, channel: Long): Promise<LokiPublicChat, Exception> {
// Check for an existing group
val groupID = LokiPublicChat.getId(channel, url)
val threadID = GroupManager.getPublicChatThreadId(groupID, context)
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
if (openGroup != null) { return Promise.of(openGroup) }
// Add the new group
val application = ApplicationContext.getInstance(context)
val displayName = TextSecurePreferences.getProfileName(context)
val lokiPublicChatAPI = application.lokiPublicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized.")
return application.lokiPublicChatManager.addChat(url, channel).then { group ->
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url)
DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url)
lokiPublicChatAPI.getMessages(channel, url)
lokiPublicChatAPI.setDisplayName(displayName, url)
lokiPublicChatAPI.join(channel, url)
val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context)
val profileUrl: String? = TextSecurePreferences.getProfileAvatarUrl(context)
lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl)
group
}
}
}

View File

@ -33,20 +33,12 @@ import android.provider.OpenableColumns;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import org.thoughtcrime.securesms.TransportOption;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
import org.thoughtcrime.securesms.logging.Log;
import android.util.Pair; import android.util.Pair;
import android.view.View; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.location.places.ui.PlacePicker;
import org.thoughtcrime.securesms.MediaPreviewActivity; import org.thoughtcrime.securesms.MediaPreviewActivity;
import network.loki.messenger.R; import org.thoughtcrime.securesms.TransportOption;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.components.AudioView; import org.thoughtcrime.securesms.components.AudioView;
import org.thoughtcrime.securesms.components.DocumentView; import org.thoughtcrime.securesms.components.DocumentView;
@ -55,6 +47,8 @@ import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.components.location.SignalMapView; import org.thoughtcrime.securesms.components.location.SignalMapView;
import org.thoughtcrime.securesms.components.location.SignalPlace; import org.thoughtcrime.securesms.components.location.SignalPlace;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity; import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider; import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider;
@ -77,6 +71,8 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import network.loki.messenger.R;
public class AttachmentManager { public class AttachmentManager {

View File

@ -14,17 +14,14 @@ import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.preference.CheckBoxPreference; import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import org.thoughtcrime.securesms.logging.Log;
import android.widget.Toast; import android.widget.Toast;
import com.google.firebase.iid.FirebaseInstanceId;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.LogSubmitActivity; import org.thoughtcrime.securesms.LogSubmitActivity;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.RegistrationActivity; import org.thoughtcrime.securesms.RegistrationActivity;
import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactIdentityManager; import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.push.AccountManagerFactory; import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
@ -34,6 +31,8 @@ import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedE
import java.io.IOException; import java.io.IOException;
import network.loki.messenger.R;
public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment { public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
private static final String TAG = AdvancedPreferenceFragment.class.getSimpleName(); private static final String TAG = AdvancedPreferenceFragment.class.getSimpleName();
@ -187,15 +186,11 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
try { try {
accountManager.setGcmId(Optional.<String>absent()); accountManager.setGcmId(Optional.absent());
} catch (AuthorizationFailedException e) { } catch (AuthorizationFailedException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
if (!TextSecurePreferences.isFcmDisabled(context)) {
FirebaseInstanceId.getInstance().deleteInstanceId();
}
return SUCCESS; return SUCCESS;
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w(TAG, ioe); Log.w(TAG, ioe);

View File

@ -1,15 +1,12 @@
package org.thoughtcrime.securesms.push; package org.thoughtcrime.securesms.push;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask;
import org.thoughtcrime.securesms.logging.Log;
import com.google.android.gms.security.ProviderInstaller;
import network.loki.messenger.BuildConfig;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import network.loki.messenger.BuildConfig;
public class AccountManagerFactory { public class AccountManagerFactory {
private static final String TAG = AccountManagerFactory.class.getSimpleName(); private static final String TAG = AccountManagerFactory.class.getSimpleName();
@ -22,20 +19,6 @@ public class AccountManagerFactory {
} }
public static SignalServiceAccountManager createManager(final Context context, String number, String password) { public static SignalServiceAccountManager createManager(final Context context, String number, String password) {
if (new SignalServiceNetworkAccess(context).isCensored(number)) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
ProviderInstaller.installIfNeeded(context);
} catch (Throwable t) {
Log.w(TAG, t);
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(number), return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(number),
number, password, BuildConfig.USER_AGENT); number, password, BuildConfig.USER_AGENT);
} }

View File

@ -1,26 +0,0 @@
package org.thoughtcrime.securesms.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.contacts.ContactsSyncAdapter;
public class ContactsSyncAdapterService extends Service {
private static ContactsSyncAdapter syncAdapter;
@Override
public synchronized void onCreate() {
if (syncAdapter == null) {
syncAdapter = new ContactsSyncAdapter(this, true);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return syncAdapter.getSyncAdapterBinder();
}
}

View File

@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.BackgroundMessage; import org.thoughtcrime.securesms.loki.BackgroundMessage;
import org.thoughtcrime.securesms.loki.FriendRequestHandler; import org.thoughtcrime.securesms.loki.FriendRequestHandler;
import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt; import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt;
import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob; import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob;
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
@ -86,6 +87,10 @@ public class MessageSender {
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceGroupUpdateJob()); ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceGroupUpdateJob());
} }
public static void syncAllOpenGroups(Context context) {
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceOpenGroupUpdateJob());
}
/** /**
* Send a contact sync message to all our devices telling them that we want to sync `contact` * Send a contact sync message to all our devices telling them that we want to sync `contact`
*/ */

View File

@ -6,7 +6,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread; import android.support.annotation.WorkerThread;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import com.google.android.gms.common.util.Hex;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -15,6 +14,7 @@ import org.thoughtcrime.securesms.database.model.StickerPackRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord; import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
@ -81,8 +81,8 @@ public final class StickerPackPreviewRepository implements InjectableType {
@WorkerThread @WorkerThread
private Optional<StickerManifestResult> getManifestRemote(@NonNull String packId, @NonNull String packKey) { private Optional<StickerManifestResult> getManifestRemote(@NonNull String packId, @NonNull String packKey) {
try { try {
byte[] packIdBytes = Hex.stringToBytes(packId); byte[] packIdBytes = Hex.fromStringCondensed(packId);
byte[] packKeyBytes = Hex.stringToBytes(packKey); byte[] packKeyBytes = Hex.fromStringCondensed(packKey);
SignalServiceStickerManifest remoteManifest = receiver.retrieveStickerManifest(packIdBytes, packKeyBytes); SignalServiceStickerManifest remoteManifest = receiver.retrieveStickerManifest(packIdBytes, packKeyBytes);
StickerManifest localManifest = new StickerManifest(packId, StickerManifest localManifest = new StickerManifest(packId,
packKey, packKey,

View File

@ -5,9 +5,9 @@ import android.support.annotation.NonNull;
import com.bumptech.glide.Priority; import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.load.data.DataFetcher;
import com.google.android.gms.common.util.Hex;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.Hex;
import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
@ -32,8 +32,8 @@ public final class StickerRemoteUriFetcher implements DataFetcher<InputStream> {
@Override @Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
try { try {
byte[] packIdBytes = Hex.stringToBytes(stickerUri.getPackId()); byte[] packIdBytes = Hex.fromStringCondensed(stickerUri.getPackId());
byte[] packKeyBytes = Hex.stringToBytes(stickerUri.getPackKey()); byte[] packKeyBytes = Hex.fromStringCondensed(stickerUri.getPackKey());
InputStream stream = receiver.retrieveSticker(packIdBytes, packKeyBytes, stickerUri.getStickerId()); InputStream stream = receiver.retrieveSticker(packIdBytes, packKeyBytes, stickerUri.getStickerId());
callback.onDataReady(stream); callback.onDataReady(stream);

View File

@ -4,15 +4,13 @@ import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread; import android.support.annotation.WorkerThread;
import android.widget.Toast;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.*; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -26,6 +24,8 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import network.loki.messenger.R;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
public class GroupUtil { public class GroupUtil {
@ -87,6 +87,10 @@ public class GroupUtil {
return groupId.startsWith(ENCODED_RSS_FEED_GROUP_PREFIX); return groupId.startsWith(ENCODED_RSS_FEED_GROUP_PREFIX);
} }
public static boolean isSignalGroup(@NonNull String groupId) {
return groupId.startsWith(ENCODED_SIGNAL_GROUP_PREFIX);
}
@WorkerThread @WorkerThread
public static Optional<OutgoingGroupMediaMessage> createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) { public static Optional<OutgoingGroupMediaMessage> createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) {
String encodedGroupId = groupRecipient.getAddress().toGroupString(); String encodedGroupId = groupRecipient.getAddress().toGroupString();
@ -114,6 +118,8 @@ public class GroupUtil {
} }
public static boolean leaveGroup(@NonNull Context context, Recipient groupRecipient) { public static boolean leaveGroup(@NonNull Context context, Recipient groupRecipient) {
if (!groupRecipient.getAddress().isSignalGroup()) { return true; }
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient); Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);

View File

@ -1,61 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import org.thoughtcrime.securesms.logging.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
public class PlayServicesUtil {
private static final String TAG = PlayServicesUtil.class.getSimpleName();
public enum PlayServicesStatus {
SUCCESS,
MISSING,
NEEDS_UPDATE,
TRANSIENT_ERROR
}
public static PlayServicesStatus getPlayServicesStatus(Context context) {
int gcmStatus = 0;
try {
gcmStatus = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
} catch (Throwable t) {
Log.w(TAG, t);
return PlayServicesStatus.MISSING;
}
Log.i(TAG, "Play Services: " + gcmStatus);
switch (gcmStatus) {
case ConnectionResult.SUCCESS:
return PlayServicesStatus.SUCCESS;
case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
try {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo("com.google.android.gms", 0);
if (applicationInfo != null && !applicationInfo.enabled) {
return PlayServicesStatus.MISSING;
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, e);
}
return PlayServicesStatus.NEEDS_UPDATE;
case ConnectionResult.SERVICE_DISABLED:
case ConnectionResult.SERVICE_MISSING:
case ConnectionResult.SERVICE_INVALID:
case ConnectionResult.API_UNAVAILABLE:
case ConnectionResult.SERVICE_MISSING_PERMISSION:
return PlayServicesStatus.MISSING;
default:
return PlayServicesStatus.TRANSIENT_ERROR;
}
}
}

View File

@ -2,13 +2,6 @@ package org.thoughtcrime.securesms.util;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.google.i18n.phonenumbers.ShortNumberInfo;
import org.thoughtcrime.securesms.logging.Log;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -24,23 +17,7 @@ public class ShortCodeUtil {
}}; }};
public static boolean isShortCode(@NonNull String localNumber, @NonNull String number) { public static boolean isShortCode(@NonNull String localNumber, @NonNull String number) {
try { return false;
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
Phonenumber.PhoneNumber localNumberObject = util.parse(localNumber, null);
String localCountryCode = util.getRegionCodeForNumber(localNumberObject);
String bareNumber = number.replaceAll("[^0-9+]", "");
// libphonenumber seems incorrect for Russia and a few other countries with 4 digit short codes.
if (bareNumber.length() <= 4 && !SHORT_COUNTRIES.contains(localCountryCode)) {
return true;
}
Phonenumber.PhoneNumber shortCode = util.parse(number, localCountryCode);
return ShortNumberInfo.getInstance().isPossibleShortNumberForRegion(shortCode, localCountryCode);
} catch (NumberParseException e) {
Log.w(TAG, e);
return false;
}
} }
} }

View File

@ -32,20 +32,14 @@ import android.os.Looper;
import android.provider.Telephony; import android.provider.Telephony;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.RequiresPermission;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.EncodedStringValue; import com.google.android.mms.pdu_alt.EncodedStringValue;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import network.loki.messenger.BuildConfig;
import org.thoughtcrime.securesms.components.ComposeText; import org.thoughtcrime.securesms.components.ComposeText;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
@ -58,7 +52,7 @@ import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -73,6 +67,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import network.loki.messenger.BuildConfig;
public class Util { public class Util {
private static final String TAG = Util.class.getSimpleName(); private static final String TAG = Util.class.getSimpleName();
@ -170,27 +166,15 @@ public class Util {
} }
public static @NonNull String toIsoString(byte[] bytes) { public static @NonNull String toIsoString(byte[] bytes) {
try { return new String(bytes, StandardCharsets.ISO_8859_1);
return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
} catch (UnsupportedEncodingException e) {
throw new AssertionError("ISO_8859_1 must be supported!");
}
} }
public static byte[] toIsoBytes(String isoString) { public static byte[] toIsoBytes(String isoString) {
try { return isoString.getBytes(StandardCharsets.ISO_8859_1);
return isoString.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
} catch (UnsupportedEncodingException e) {
throw new AssertionError("ISO_8859_1 must be supported!");
}
} }
public static byte[] toUtf8Bytes(String utf8String) { public static byte[] toUtf8Bytes(String utf8String) {
try { return utf8String.getBytes(StandardCharsets.UTF_8);
return utf8String.getBytes(CharacterSets.MIMENAME_UTF_8);
} catch (UnsupportedEncodingException e) {
throw new AssertionError("UTF_8 must be supported!");
}
} }
public static void wait(Object lock, long timeout) { public static void wait(Object lock, long timeout) {
@ -279,27 +263,6 @@ public class Util {
return total; return total;
} }
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_SMS,
android.Manifest.permission.READ_PHONE_NUMBERS
})
@SuppressLint("MissingPermission")
public static Optional<Phonenumber.PhoneNumber> getDeviceNumber(Context context) {
try {
final String localNumber = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number();
final Optional<String> countryIso = getSimCountryIso(context);
if (TextUtils.isEmpty(localNumber)) return Optional.absent();
if (!countryIso.isPresent()) return Optional.absent();
return Optional.fromNullable(PhoneNumberUtil.getInstance().parse(localNumber, countryIso.get()));
} catch (NumberParseException e) {
Log.w(TAG, e);
return Optional.absent();
}
}
public static Optional<String> getSimCountryIso(Context context) { public static Optional<String> getSimCountryIso(Context context) {
String simCountryIso = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getSimCountryIso(); String simCountryIso = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getSimCountryIso();
return Optional.fromNullable(simCountryIso != null ? simCountryIso.toUpperCase() : null); return Optional.fromNullable(simCountryIso != null ? simCountryIso.toUpperCase() : null);