Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b8da4bc9c7 | |||
| b83b2e9e29 | |||
| 7f97bd0ea9 | |||
| f0abb05e0a | |||
| 7db7c5beeb | |||
| 50e22ddcc9 | |||
| 85e2203244 | |||
| 7b2ef54922 | |||
| 4043338f90 | |||
| 269cb006c4 | |||
| 3daaa51d13 | |||
| 099f3ad109 | |||
| 91ec15330b | |||
| 7a5f382315 | |||
| 0cfc61fde9 | |||
| 0a060fbb74 | |||
| 8a8034c33e | |||
| 313e67fa57 | |||
| 0613b879fe | |||
| 155e748bd1 | |||
| 40e57b3286 | |||
| 90bf08845b | |||
| 328132e041 | |||
| baec8d229d | |||
| 740c7bd210 | |||
| 032c38b3d7 | |||
| 2fbcf3f7f9 | |||
| 4a811d4f37 | |||
| 1f9ae91488 | |||
| 3e76970257 | |||
| f022088ed9 | |||
| e60c143a42 | |||
| 5861ca14d6 | |||
| 99f5948fc7 | |||
| c2e9ba3763 | |||
| 9743011f25 | |||
| 7fb244b0be | |||
| a79a33751b | |||
| 1f4ccc2944 | |||
| 7776939716 | |||
| d9810443fc | |||
| 17d01b27f2 | |||
| fd2e034f13 | |||
| 361e153605 | |||
| 564915ab9f | |||
| 69a887fe61 | |||
| e6020f9707 | |||
| b2ee6320f4 | |||
| 98af653204 | |||
| 56527d3f3f | |||
| 1355f59a94 | |||
| c02df7a370 | |||
| 89e8e80665 | |||
| 3ef1650b51 | |||
| 4739b8c165 | |||
| ebdb5f382d | |||
| fa8e9cefd3 | |||
| b459e9bd0e | |||
| a336ce3e2c | |||
| 986d86f82e | |||
| f202ca1eb2 | |||
| f1b2dd581a | |||
| 5ed18c106c | |||
| e051eecf48 | |||
| 5fcf183215 | |||
| df494a49a8 | |||
| 564b837d93 | |||
| 8c9cd3dd3d | |||
| c290194c9a | |||
| cf2bdefcee | |||
| 4f9c692dbd | |||
| dd67248066 | |||
| d4f1af7521 | |||
| df62bfe8de | |||
| b8929c181a | |||
| 9fa44b3e68 | |||
| 755d081c64 | |||
| b635384dd5 | |||
| 1fb07c9f80 | |||
| 5a9220356c | |||
| c8accec761 | |||
| cad5c5c185 | |||
| acaf836af9 | |||
| 5221b4c8fd | |||
| 5ee4320a6c | |||
| 92a0a83187 | |||
| 76a8a724ff | |||
| d8ccf5ed6d | |||
| a2d6a00cd1 | |||
| ffe69f7f4b | |||
| 887cc3a1bb | |||
| c192b719f5 | |||
| ed0de74406 | |||
| ff1878511d | |||
| a77cbee5f2 | |||
| e92fda0ca3 | |||
| a4c9e1699b | |||
| df87b64f66 | |||
| b79e8e812d | |||
| ec2deebda4 | |||
| befbf84c59 | |||
| 7b205db200 | |||
| e43c85b12b | |||
| 758c175b3d | |||
| fdceb0e080 | |||
| 3863ef91e1 | |||
| 6e07258fc7 | |||
| c07a0dedd6 | |||
| e5b906184d | |||
| 2dbd12403f | |||
| 9906608956 | |||
| 5870f0cd02 | |||
| 3dc6ea1c85 | |||
| e66ae137fe | |||
| 4d32f5746c | |||
| 6e5c6e3d4d | |||
| c459aa4476 | |||
| bf4bcba78c | |||
| ccb5e3285f | |||
| 5424d56801 | |||
| 5c15784f66 | |||
| 0abecd74c9 | |||
| aa3df42f71 | |||
| cfe6e023cb | |||
| 6140b2aab4 | |||
| cc95e1fa1f | |||
| 643e8d80f7 | |||
| 84cc1c124a | |||
| 22d8f3177f | |||
| 471f4480ac | |||
| bc387096f5 | |||
| 93b5a345ea | |||
| 50b18b7d65 | |||
| 004319db64 | |||
| 90b52cebf1 | |||
| f4a01a2bae | |||
| 77ed15125d | |||
| 15ef0271fc | |||
| 1dd2ad04ba | |||
| 0a259d2277 | |||
| 9f6a7cbf00 | |||
| 5c6e84b21b | |||
| 00f0f143af | |||
| 7bcfec2367 | |||
| 17e5fe5a89 | |||
| 4b8fef6918 | |||
| 44afb50c81 | |||
| 39f14a4a9a |
@@ -1,48 +1,24 @@
|
||||
<!--
|
||||
Please read the guidelines linked above, especially when you are a first time contributor. Remove this text and provide some basic informations below, if applicable. Remove template content which is not applicable.
|
||||
|
||||
This is a bug report forum, if you like to discuss a new feature or submit suggestions, please use the community forum at https://support.delta.chat
|
||||
Please fill in as much as you can below (leaving out if not applicable is ok). This is a bug report and feature-tracking forum, if you like to discuss a new feature, please use the community forum at https://support.delta.chat.
|
||||
-->
|
||||
|
||||
**Delta Chat version**
|
||||
|
||||
<!-- See settings dialog … -->
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
<!-- What is the expected output? -->
|
||||
|
||||
**Actual behavior**
|
||||
|
||||
<!-- What do you see instead? -->
|
||||
|
||||
**Steps to reproduce the problem**
|
||||
|
||||
1. …
|
||||
2. …
|
||||
|
||||
**Screen snapshots**
|
||||
|
||||
<!-- If applicable … -->
|
||||
|
||||
**Debug logs**
|
||||
<details>
|
||||
<summary>Logs</summary>
|
||||
|
||||
```
|
||||
- Platform (android/blackberry/anbox):
|
||||
- Device:
|
||||
- Delta Chat Version:
|
||||
- Expected behavior:
|
||||
- Actual behavior:
|
||||
- Steps to reproduce the problem:
|
||||
- Screenshots:
|
||||
- Logs:
|
||||
|
||||
<!--
|
||||
If applicable, debug logs can be copied from within the Delta Chat app:
|
||||
|
||||
Open the _Settings_ menu -> _About_ -> _version number|Info_.
|
||||
Debug logs can be copied from within the Delta Chat app with
|
||||
Settings menu -> Advanced -> Debug log
|
||||
|
||||
Alternatively from the Android system log:
|
||||
`adb logcat -v time -s DeltaChat`
|
||||
|
||||
` adb logcat -v time -s DeltaChat `
|
||||
|
||||
This log contains private data (e.g. mail address, provider information) which shall be removed or anonymised prior to posting.
|
||||
Logs may contain private data
|
||||
which shall be removed or anonymised prior to posting.
|
||||
-->
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -27,3 +27,6 @@ jni/libspeex/.deps/
|
||||
|
||||
# ignore private scripts and directories, eg. local2github.prv.sh
|
||||
*.prv*
|
||||
|
||||
# contains files for ndk-build when done from gradle.
|
||||
.externalNativeBuild
|
||||
|
||||
@@ -15,67 +15,22 @@
|
||||
<uses-feature android:name="android.hardware.portrait" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
|
||||
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
||||
<uses-permission android:name="org.thoughtcrime.securesms.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"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_CALL_LOG" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<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.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
|
||||
<uses-permission android:name="android.permission.READ_CALL_STATE"/>
|
||||
|
||||
<!-- 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.VIBRATE"/>
|
||||
<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.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.WRITE_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
|
||||
|
||||
<!-- For conversation 'shortcuts' on the desktop -->
|
||||
<uses-permission android:name="android.permission.INSTALL_SHORTCUT"/>
|
||||
|
||||
<!-- For fixing MMS -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<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.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
||||
<uses-permission android:name="android.permission.RAISED_THREAD_PRIORITY" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||
|
||||
<application android:name=".ApplicationContext"
|
||||
android:icon="@mipmap/ic_launcher_dev"
|
||||
@@ -84,7 +39,8 @@
|
||||
tools:replace="android:allowBackup"
|
||||
android:allowBackup="false"
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:largeHeap="true">
|
||||
android:largeHeap="true"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.geo.API_KEY"
|
||||
@@ -96,12 +52,6 @@
|
||||
<meta-data android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/automotive_app_desc" />
|
||||
|
||||
<activity android:name="org.thoughtcrime.securesms.WebRtcCallActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|fontScale"
|
||||
android:launchMode="singleTask"/>
|
||||
|
||||
<activity android:name=".InviteActivity"
|
||||
android:theme="@style/TextSecure.HighlightTheme"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
@@ -192,7 +142,6 @@
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
|
||||
|
||||
<activity android:name=".GroupCreateActivity"
|
||||
android:windowSoftInputMode="stateVisible"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".DatabaseUpgradeActivity"
|
||||
@@ -231,9 +180,6 @@
|
||||
android:label="@string/AndroidManifest__change_passphrase"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".VerifyIdentityActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".ApplicationPreferencesActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
@@ -301,12 +247,12 @@
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".QrScanActivity"
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".QrShowActivity"
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
<activity android:name=".QrShowActivity"
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".ClearProfileAvatarActivity"
|
||||
android:theme="@style/Theme.AppCompat.Dialog.Alert"
|
||||
@@ -336,24 +282,9 @@
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/>
|
||||
<service android:enabled="true" android:exported="false" android:name=".service.KeyCachingService"/>
|
||||
<service android:enabled="true" android:name=".service.MessageRetrievalService"/>
|
||||
|
||||
<service android:name=".service.AccountAuthenticatorService" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" />
|
||||
</service>
|
||||
|
||||
<service android:name=".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 android:name=".connect.KeepAliveService" android:enabled="true"/>
|
||||
|
||||
<service android:name=".service.DirectShareService"
|
||||
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
|
||||
@@ -364,12 +295,7 @@
|
||||
|
||||
<service android:name=".service.GenericForegroundService"/>
|
||||
|
||||
<receiver android:name=".gcm.GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
<category android:name="org.thoughtcrime.securesms" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".connect.TimerReceiver" android:enabled="true"/>
|
||||
|
||||
<receiver android:name=".notifications.MarkReadReceiver"
|
||||
android:enabled="true"
|
||||
@@ -403,18 +329,11 @@
|
||||
|
||||
<receiver android:name=".service.ExpirationListener" />
|
||||
|
||||
<receiver android:name=".jobmanager.requirements.BackoffReceiver" />
|
||||
|
||||
<provider android:name=".providers.PartProvider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false"
|
||||
android:authorities="${applicationId}.provider.securesms" />
|
||||
|
||||
<provider android:name=".providers.MmsBodyProvider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false"
|
||||
android:authorities="${applicationId}.provider.securesms.mms" />
|
||||
|
||||
<provider android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
@@ -438,24 +357,6 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".service.DirectoryRefreshListener">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".service.RotateSignedPreKeyListener">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".service.PersistentConnectionBootListener">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".notifications.MessageNotifier$ReminderReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="org.thoughtcrime.securesms.MessageNotifier.REMINDER_ACTION"/>
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
TextSecure provides encrypted text messages for Android.
|
||||
Copyright 2011 Whisper Systems
|
||||
|
||||
This software has the follow third party dependencies:
|
||||
|
||||
Bouncy Castle 1.42
|
||||
http://www.bouncycastle.org/
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
ZXing
|
||||
http://code.google.com/p/zxing/
|
||||
Apache License 2.0
|
||||
|
||||
Copyright 2009 ZXing authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -14,7 +14,7 @@ For ready-to-use binaries, please go to https://delta.chat .
|
||||
When checking out _deltachat-android-ii_, make sure also to check out the
|
||||
subproject _deltachat-core_:
|
||||
|
||||
- When using Git, you can can do this initially by
|
||||
- When using Git, you can do this initially by
|
||||
`$ git clone --recursive https://github.com/deltachat/deltachat-android-ii`
|
||||
or later by `git submodule update --init --recursive`. If you do this in your
|
||||
home directory, this results in the folder `~/deltachat-android-ii` which is just fine.
|
||||
|
||||
@@ -94,7 +94,6 @@ dependencies {
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.5.0'
|
||||
compile 'com.makeramen:roundedimageview:2.1.0'
|
||||
compile 'com.pnikosis:materialish-progress:1.5'
|
||||
compile 'org.greenrobot:eventbus:3.0.0'
|
||||
compile 'pl.tajchert:waitingdots:0.1.0'
|
||||
compile 'com.soundcloud.android:android-crop:0.9.10@aar'
|
||||
compile 'com.melnykov:floatingactionbutton:1.3.0'
|
||||
@@ -184,7 +183,6 @@ dependencyVerification {
|
||||
'com.github.chrisbanes:PhotoView:ed06775308da260e1fd86d1d3288988fcd3d80db24ce0d7c9fcfedc39e622292',
|
||||
'com.github.bumptech.glide:glide:997de7ac95be6c944d3b8cbe13de11307736ea45451c1b09a6cec7c328ead59f',
|
||||
'com.makeramen:roundedimageview:1f5a1865796b308c6cdd114acc6e78408b110f0a62fc63553278fbeacd489cd1',
|
||||
'org.greenrobot:eventbus:180d4212467df06f2fbc9c8d8a2984533ac79c87769ad883bc421612f0b4e17c',
|
||||
'com.soundcloud.android:android-crop:ffd4b973cf6e97f7d64118a0dc088df50e9066fd5634fe6911dd0c0c5d346177',
|
||||
'com.squareup.dagger:dagger:789aca24537022e49f91fc6444078d9de8f1dd99e1bfb090f18491b186967883',
|
||||
'com.amulyakhare:com.amulyakhare.textdrawable:54c92b5fba38cfd316a07e5a30528068f45ce8515a6890f1297df4c401af5dcb',
|
||||
@@ -255,8 +253,8 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode 394
|
||||
versionName "0.0.4"
|
||||
versionCode 399
|
||||
versionName "0.0.9"
|
||||
applicationId "chat.delta.androidii"
|
||||
|
||||
minSdkVersion 14
|
||||
|
||||
@@ -238,27 +238,27 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_isConfigured(JNIEnv *env, jobje
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_performJobs(JNIEnv *env, jobject obj)
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_performImapJobs(JNIEnv *env, jobject obj)
|
||||
{
|
||||
dc_perform_imap_jobs(get_dc_context(env, obj));
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_idle(JNIEnv *env, jobject obj)
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_performImapIdle(JNIEnv *env, jobject obj)
|
||||
{
|
||||
dc_perform_imap_idle(get_dc_context(env, obj));
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_interruptIdle(JNIEnv *env, jobject obj)
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_performImapFetch(JNIEnv *env, jobject obj)
|
||||
{
|
||||
dc_interrupt_imap_idle(get_dc_context(env, obj));
|
||||
dc_perform_imap_fetch(get_dc_context(env, obj));
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_fetch(JNIEnv *env, jobject obj)
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_interruptImapIdle(JNIEnv *env, jobject obj)
|
||||
{
|
||||
dc_perform_imap_fetch(get_dc_context(env, obj));
|
||||
dc_interrupt_imap_idle(get_dc_context(env, obj));
|
||||
}
|
||||
|
||||
|
||||
@@ -274,9 +274,9 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_performSmtpIdle(JNIEnv *env, jo
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_interruptSmtpIdle(JNIEnv *env, jobject obj)
|
||||
JNIEXPORT void Java_com_b44t_messenger_DcContext_maybeNetwork(JNIEnv *env, jobject obj)
|
||||
{
|
||||
dc_interrupt_smtp_idle(get_dc_context(env, obj));
|
||||
dc_maybe_network(get_dc_context(env, obj));
|
||||
}
|
||||
|
||||
|
||||
@@ -726,6 +726,12 @@ JNIEXPORT jlong Java_com_b44t_messenger_DcChatlist_getChatCPtr(JNIEnv *env, jobj
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint Java_com_b44t_messenger_DcChatlist_getMsgId(JNIEnv *env, jobject obj, jint index)
|
||||
{
|
||||
return dc_chatlist_get_msg_id(get_dc_chatlist(env, obj), index);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jlong Java_com_b44t_messenger_DcChatlist_getMsgCPtr(JNIEnv *env, jobject obj, jint index)
|
||||
{
|
||||
dc_chatlist_t* chatlist = get_dc_chatlist(env, obj);
|
||||
@@ -1175,6 +1181,12 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getNameNAddr(JNIEnv *env, jo
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getProfileImage(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return JSTRING_NEW("");
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcContact_isBlocked(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return (jboolean)(dc_contact_is_blocked(get_dc_contact(env, obj))!=0);
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 129 B After Width: | Height: | Size: 344 B |
|
After Width: | Height: | Size: 727 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 750 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 92 B After Width: | Height: | Size: 351 B |
|
After Width: | Height: | Size: 508 B |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 97 B After Width: | Height: | Size: 368 B |
|
After Width: | Height: | Size: 822 B |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 97 B After Width: | Height: | Size: 387 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@drawable/ic_help_white_24dp"
|
||||
android:tint="?attr/pref_icon_tint"/>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@drawable/ic_mood_white_24dp"
|
||||
android:tint="?attr/pref_icon_tint"/>
|
||||
@@ -1,11 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent" android:layout_width="match_parent">
|
||||
<ImageView
|
||||
android:id="@+id/background_imageview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"/>
|
||||
<org.thoughtcrime.securesms.components.InputAwareLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
@@ -69,13 +64,6 @@
|
||||
android:text="@string/conversation_activity__enable_signal_messages"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<Button android:id="@+id/unblock_button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:text="@string/ConversationActivity_unblock"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<Button android:id="@+id/make_default_sms_button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
android:paddingBottom="6dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:background="@color/white">
|
||||
android:background="?attr/colorBackgroundFloating">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
app:backgroundTint="@color/signal_primary"
|
||||
android:src="@drawable/ic_create_white_24dp"
|
||||
android:src="@drawable/ic_add_white_24dp"
|
||||
android:focusable="true"
|
||||
android:contentDescription="@string/conversation_list_fragment__fab_content_description"/>
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
android:focusable="true"
|
||||
android:nextFocusRight="@+id/fab"
|
||||
android:nextFocusLeft="@+id/container"
|
||||
android:layout_height="72dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
android:layout_height="72dp">
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||
android:id="@+id/contact_photo_image"
|
||||
@@ -23,153 +21,124 @@
|
||||
android:cropToPadding="true"
|
||||
tools:src="@drawable/ic_contact_picture"
|
||||
android:contentDescription="@string/conversation_list_item_view__contact_photo_image"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp" />
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp" />
|
||||
|
||||
<RelativeLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@id/contact_photo_image"
|
||||
android:layout_toEndOf="@id/contact_photo_image"
|
||||
android:weightSum="1"
|
||||
android:orientation="horizontal">
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_toRightOf="@id/contact_photo_image"
|
||||
android:layout_toEndOf="@id/contact_photo_image">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/from"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_toLeftOf="@+id/thumbnail"
|
||||
android:layout_toStartOf="@+id/thumbnail">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<org.thoughtcrime.securesms.components.FromTextView
|
||||
android:id="@+id/from_text"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
<LinearLayout
|
||||
android:id="@+id/from"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:drawablePadding="5dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:maxLines="1"
|
||||
android:textColor="?attr/conversation_list_item_contact_color"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/verified_indicator"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintHorizontal_weight="0.9"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Jules Bonnot" />
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verified_indicator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingStart="5dp"
|
||||
android:src="@drawable/ic_verified"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintHorizontal_weight="0.1"
|
||||
app:layout_constraintStart_toEndOf="@id/from_text"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<ImageView
|
||||
android:id="@+id/group_indicator"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_group_white_24dp"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginEnd="5dp" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
<org.thoughtcrime.securesms.components.FromTextView
|
||||
android:id="@+id/from_text"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="5dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:maxLines="1"
|
||||
android:textColor="?attr/conversation_list_item_contact_color"
|
||||
tools:text="Jules Bonnot" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/subject"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/from"
|
||||
android:layout_toLeftOf="@+id/status"
|
||||
android:layout_toStartOf="@+id/status"
|
||||
android:paddingRight="1dp"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:textColor="?attr/conversation_list_item_subject_color"
|
||||
android:maxLines="1"
|
||||
tools:text="Wheels arrive at 3pm flat. This is a somewhat longer message."
|
||||
android:ellipsize="end" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.ThumbnailView
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_toLeftOf="@+id/date"
|
||||
android:layout_toStartOf="@+id/date"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginTop="5dp"
|
||||
android:contentDescription="@string/conversation_activity__attachment_thumbnail"
|
||||
android:visibility="gone"
|
||||
tools:src="@drawable/ic_video_light"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView android:id="@id/date"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginStart="3dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignBaseline="@+id/from"
|
||||
android:layout_above="@id/subject"
|
||||
style="@style/Signal.Text.Caption"
|
||||
android:textColor="?attr/conversation_list_item_date_color"
|
||||
android:textAllCaps="true"
|
||||
tools:text="30 mins"
|
||||
android:singleLine="true"/>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:gravity="end"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView android:id="@+id/archived"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/date"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignBaseline="@id/subject"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingBottom="2dp"
|
||||
style="@style/Signal.Text.Caption"
|
||||
android:text="@string/conversation_list_item_view__archived"
|
||||
android:textColor="@color/core_white"
|
||||
android:textAllCaps="true"
|
||||
android:background="@drawable/archived_indicator_background" />
|
||||
<TextView android:id="@id/date"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
style="@style/Signal.Text.Caption"
|
||||
android:textColor="?attr/conversation_list_item_date_color"
|
||||
android:textAllCaps="true"
|
||||
tools:text="30 mins"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:minHeight="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_below="@id/date"
|
||||
android:layout_toLeftOf="@id/archived"
|
||||
android:layout_toStartOf="@id/archived"
|
||||
android:layout_alignWithParentIfMissing="true">
|
||||
<TextView android:id="@+id/archived"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingBottom="2dp"
|
||||
style="@style/Signal.Text.Caption"
|
||||
android:text="@string/conversation_list_item_view__archived"
|
||||
android:textColor="@color/core_white"
|
||||
android:textAllCaps="true"
|
||||
android:background="@drawable/archived_indicator_background" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.DeliveryStatusView
|
||||
android:id="@+id/delivery_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:iconColor="?attr/conversation_list_item_delivery_icon_color"/>
|
||||
<org.thoughtcrime.securesms.components.DeliveryStatusView
|
||||
android:id="@+id/delivery_status"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconColor="?attr/conversation_list_item_delivery_icon_color"/>
|
||||
|
||||
<ImageView android:id="@+id/unread_indicator"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"/>
|
||||
<ImageView android:id="@+id/unread_indicator"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<View android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@drawable/attachment_selector_shadow"/>
|
||||
|
||||
</org.thoughtcrime.securesms.ConversationListItem>
|
||||
|
||||
@@ -39,9 +39,10 @@
|
||||
tools:src="@drawable/ic_contact_picture"
|
||||
android:contentDescription="@string/conversation_list_item_view__contact_photo_image"/>
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/contact_photo_image"
|
||||
@@ -50,34 +51,15 @@
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextSecure.TitleTextStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="5dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="18dp"
|
||||
android:transitionName="recipient_name"
|
||||
app:layout_constraintEnd_toStartOf="@id/verified_indicator"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintHorizontal_weight="0.9"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Jules Bonnot" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verified_indicator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_verified"
|
||||
android:visibility="gone"
|
||||
android:layout_marginLeft="5dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintHorizontal_weight="0.1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/title"
|
||||
app:layout_constraintStart_toEndOf="@id/title"
|
||||
app:layout_constraintTop_toTopOf="@id/title"
|
||||
android:layout_marginStart="5dp" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/subtitle"
|
||||
style="@style/TextSecure.SubtitleTextStyle"
|
||||
@@ -87,9 +69,8 @@
|
||||
android:maxLines="1"
|
||||
android:textDirection="ltr"
|
||||
android:textSize="13dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
tools:text="(123) 123-1234" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</org.thoughtcrime.securesms.ConversationTitleView>
|
||||
@@ -13,6 +13,7 @@
|
||||
android:horizontalSpacing="0dp"
|
||||
android:verticalSpacing="0dp"
|
||||
android:numColumns="auto_fit"
|
||||
android:gravity="center"/>
|
||||
android:gravity="center"
|
||||
android:background="?emoji_tab_strip_background"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/conversation__menu_view_profile"
|
||||
android:id="@+id/menu_conversation_settings"/>
|
||||
|
||||
<item android:title="@string/conversation__menu_add_attachment"
|
||||
android:id="@+id/menu_add_attachment" />
|
||||
|
||||
<item android:title="@string/conversation__menu_view_all_media"
|
||||
android:id="@+id/menu_view_media" />
|
||||
|
||||
<item android:title="@string/conversation__menu_conversation_settings"
|
||||
android:id="@+id/menu_conversation_settings"/>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:id="@+id/menu_add_to_contacts"
|
||||
android:title="@string/conversation_add_to_contacts__menu_add_to_contacts"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/conversation__menu_archive_chat"
|
||||
android:id="@+id/menu_archive_chat"/>
|
||||
|
||||
</menu>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/conversation_insecure__invite"
|
||||
android:id="@+id/menu_invite"/>
|
||||
<item android:title="@string/conversation__menu_delete_chat"
|
||||
android:id="@+id/menu_delete_chat"/>
|
||||
|
||||
</menu>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:title="@string/conversation_expiring_off__disappearing_messages"
|
||||
android:id="@+id/menu_expiring_messages_off"
|
||||
android:icon="@drawable/ic_timer_off_white_24dp"/>
|
||||
|
||||
</menu>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:id="@+id/menu_expiring_messages"
|
||||
app:actionLayout="@layout/expiration_timer_menu"
|
||||
app:showAsAction="always"
|
||||
android:title="@string/menu_conversation_expiring_on__messages_expiring"/>
|
||||
|
||||
</menu>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:title="@string/convesation_group_options__recipients_list"
|
||||
android:id="@+id/menu_group_recipients"
|
||||
android:icon="?menu_group_icon"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:title="@string/conversation_group_options__delivery"
|
||||
android:id="@+id/menu_group_delivery"
|
||||
android:icon="?menu_split_icon"
|
||||
app:showAsAction="ifRoom" >
|
||||
|
||||
<menu>
|
||||
<group android:id="@+id/distribution_group"
|
||||
android:checkableBehavior="single">
|
||||
<item android:id="@+id/menu_distribution_conversation" android:title="@string/conversation_group_options__conversation" android:checked="true" />
|
||||
<item android:id="@+id/menu_distribution_broadcast" android:title="@string/conversation_group_options__broadcast" />
|
||||
</group>
|
||||
</menu>
|
||||
</item>
|
||||
</menu>
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
<item android:id="@+id/menu_edit_group"
|
||||
android:title="@string/conversation__menu_edit_group"
|
||||
app:showAsAction="collapseActionView" />
|
||||
android:icon="?menu_group_icon"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item android:id="@+id/menu_leave"
|
||||
android:title="@string/conversation__menu_leave_group"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:title="@string/conversation_secure_verified__menu_reset_secure_session"
|
||||
android:id="@+id/menu_reset_secure_session"/>
|
||||
</menu>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/conversation__menu_unarchive_chat"
|
||||
android:id="@+id/menu_archive_chat"/>
|
||||
|
||||
</menu>
|
||||
@@ -13,16 +13,13 @@
|
||||
<item android:title="@string/text_secure_normal__menu_new_chat"
|
||||
android:id="@+id/menu_new_chat" />
|
||||
|
||||
<item android:title="@string/menu_deaddrop"
|
||||
android:id="@+id/menu_deaddrop" />
|
||||
|
||||
<item android:title="@string/text_secure_normal__menu_clear_passphrase"
|
||||
android:id="@+id/menu_clear_passphrase" />
|
||||
|
||||
<item android:title="@string/text_secure_normal__invite_friends"
|
||||
android:id="@+id/menu_invite" />
|
||||
|
||||
<item android:title="@string/text_secure_normal__menu_settings"
|
||||
android:id="@+id/menu_settings" />
|
||||
|
||||
<item android:title="@string/text_secure_normal__help"
|
||||
android:id="@+id/menu_help"/>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<attr name="attachment_document_icon_large" format="reference" />
|
||||
|
||||
<attr name="conversation_list_item_background" format="reference"/>
|
||||
<attr name="conversation_list_deaddrop_bg_color" format="reference|color"/>
|
||||
<attr name="conversation_list_item_contact_color" format="reference|color"/>
|
||||
<attr name="conversation_list_item_subject_color" format="reference|color"/>
|
||||
<attr name="conversation_list_item_delivery_icon_color" format="reference|color"/>
|
||||
|
||||
@@ -41,8 +41,14 @@
|
||||
<string name="ApplicationPreferencesActivity_sms_mms_summary">SMS %1$s, MMS %2$s</string>
|
||||
<string name="ApplicationPreferencesActivity_privacy_summary">Screen lock %1$s, Read receipts %2$s</string>
|
||||
<string name="ApplicationPreferencesActivity_appearance_summary">Theme %1$s, Language %2$s</string>
|
||||
<string name="ApplicationPreferencesActivity_appearance_summary_new">Theme %1$s, Language %2$s, Background %3$s</string>
|
||||
<string name="ApplicationPreferencesActivity_appearance_background_default">default</string>
|
||||
<string name="ApplicationPreferencesActivity_appearance_background_custom">custom</string>
|
||||
|
||||
<!-- AppearancePreferenceFragment -->
|
||||
<string name="AppearancePreferencesFragment_background_save_error">Background image could not be saved</string>
|
||||
<string name="AppearancePreferencesFragment_background_custom_summary">Custom</string>
|
||||
<string name="AppearancePreferencesFragment_background_default_summary">Default</string>
|
||||
|
||||
<!-- AppProtectionPreferenceFragment -->
|
||||
<plurals name="AppProtectionPreferenceFragment_minutes">
|
||||
@@ -166,6 +172,7 @@
|
||||
<string name="ConversationActivity_quoted_contact_message">%1$s %2$s</string>
|
||||
<string name="ConversationActivity_NoMessagesHint">Send message to %1$s:\n\n• It is okay if %2$s does not use Delta Chat.\n\n• Delivering the first message may take a while.</string>
|
||||
<string name="ConversationActivity_MsgNewGroupDraftHint">Compose the first message, allowing others to reply within this group.\n\n• It is okay if not all members use Delta Chat.\n\n• Delivering the first message may take a while.</string>
|
||||
<string name="ConversationActivity_ask_delete_chat">Delete this chat? It will no longer be shown in the chat list, its messages will remain on the server.</string>
|
||||
|
||||
<!-- ConversationAdapter -->
|
||||
<plurals name="ConversationAdapter_n_unread_messages">
|
||||
@@ -226,9 +233,9 @@
|
||||
<item quantity="one">Delete selected chats?</item>
|
||||
<item quantity="other">Delete selected chats?</item>
|
||||
</plurals>
|
||||
<plurals name="ConversationListFragment_this_will_permanently_delete_all_n_selected_conversations">
|
||||
<item quantity="one">This will permanently delete the selected chats.</item>
|
||||
<item quantity="other">This will permanently delete all %1$d selected chats.</item>
|
||||
<plurals name="ConversationListFragment_ask_selected_chats">
|
||||
<item quantity="one">Delete the selected chat? It will no longer be shown in the chat list, its messages will remain on the server.</item>
|
||||
<item quantity="other">Delete %1%d selected chats? They will no longer be shown in the chat list, their messages will remain on the server.</item>
|
||||
</plurals>
|
||||
<string name="ConversationListFragment_deleting">Deleting</string>
|
||||
<string name="ConversationListFragment_deleting_selected_conversations">Deleting selected chats...</string>
|
||||
@@ -298,6 +305,7 @@
|
||||
<string name="ShareActivity_share_with">Share with</string>
|
||||
<string name="ShareActivity_unable_to_open_media">Can\'t find an app able to open this media.</string>
|
||||
<string name="ShareActivity_file_not_found">Could not find %1$s.</string>
|
||||
<string name="ShareActivity_forward_message_to_user_info">Forward selected messages to %1$s?</string>
|
||||
|
||||
<!-- ExperienceUpgradeActivity -->
|
||||
<string name="ExperienceUpgradeActivity_welcome_to_signal_dgaf">Welcome to Delta Chat.</string>
|
||||
@@ -1518,5 +1526,15 @@
|
||||
<string name="copy_to_clipboard">Copy to clipboard</string>
|
||||
<string name="open">Open</string>
|
||||
<string name="done">Done</string>
|
||||
<string name="menu_deaddrop">Contact requests</string>
|
||||
<string name="not_now">Not now</string>
|
||||
<string name="never">Never</string>
|
||||
<string name="conversation__menu_view_profile">View profile</string>
|
||||
<string name="conversation__menu_archive_chat">Archive chat</string>
|
||||
<string name="conversation__menu_unarchive_chat">Unarchive chat</string>
|
||||
<string name="conversation__menu_delete_chat">Delete chat</string>
|
||||
<string name="conversation__no_messages">No messages.</string>
|
||||
<string name="permanent_notification_subtitle">Tap to open.</string>
|
||||
<string name="error_no_network">Network unavailable.</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
<item name="attachment_document_icon_large">@drawable/ic_document_large_light</item>
|
||||
|
||||
<item name="conversation_list_item_background">@drawable/conversation_list_item_background</item>
|
||||
<item name="conversation_list_deaddrop_bg_color">#ffcccccc</item>
|
||||
<item name="conversation_list_item_contact_color">@color/core_light_90</item>
|
||||
<item name="conversation_list_item_subject_color">@color/core_light_60</item>
|
||||
<item name="conversation_list_item_delivery_icon_color">@color/core_light_35</item>
|
||||
@@ -275,6 +276,7 @@
|
||||
<item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item>
|
||||
|
||||
<item name="conversation_list_item_background">@drawable/conversation_list_item_background_dark</item>
|
||||
<item name="conversation_list_deaddrop_bg_color">#ff333333</item>
|
||||
<item name="conversation_list_item_contact_color">#ffdddddd</item>
|
||||
<item name="conversation_list_item_subject_color">#ffdddddd</item>
|
||||
<item name="conversation_list_item_delivery_icon_color">@color/core_dark_55</item>
|
||||
|
||||
@@ -25,4 +25,12 @@
|
||||
android:title="@string/preferences__advanced"
|
||||
android:icon="@drawable/ic_advanced_24dp"/>
|
||||
|
||||
<Preference android:key="preference_category_invite"
|
||||
android:title="@string/text_secure_normal__invite_friends"
|
||||
android:icon="@drawable/ic_invite_24dp"/>
|
||||
|
||||
<Preference android:key="preference_category_help"
|
||||
android:title="@string/text_secure_normal__help"
|
||||
android:icon="@drawable/ic_help_24dp"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
android:entryValues="@array/language_values"
|
||||
android:defaultValue="zz"/>
|
||||
|
||||
<Preference
|
||||
<org.thoughtcrime.securesms.preferences.widgets.SignalPreference
|
||||
android:key="pref_chat_background"
|
||||
android:title="@string/preferences__chat_background"
|
||||
/>
|
||||
|
||||
@@ -1,28 +1,8 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Delta Chat Java Adapter
|
||||
* (C) 2017 Björn Petersen
|
||||
* Contact: r10s@b44t.com, http://b44t.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see http://www.gnu.org/licenses/ .
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
package com.b44t.messenger;
|
||||
|
||||
public class DcChat {
|
||||
|
||||
public static final int DC_CHAT_NO_CHAT = 0;
|
||||
public final static int DC_CHAT_ID_DEADDROP = 1;
|
||||
public final static int DC_CHAT_ID_STARRED = 5;
|
||||
public final static int DC_CHAT_ID_ARCHIVED_LINK = 6;
|
||||
|
||||
@@ -1,28 +1,5 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Delta Chat Java Adapter
|
||||
* (C) 2017 Björn Petersen
|
||||
* Contact: r10s@b44t.com, http://b44t.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see http://www.gnu.org/licenses/ .
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
package com.b44t.messenger;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
public class DcChatlist {
|
||||
@@ -40,6 +17,7 @@ public class DcChatlist {
|
||||
public native int getCnt ();
|
||||
public native int getChatId (int index);
|
||||
public @NonNull DcChat getChat (int index) { return new DcChat(getChatCPtr(index)); }
|
||||
public native int getMsgId (int index);
|
||||
public @NonNull DcMsg getMsg (int index) { return new DcMsg(getMsgCPtr(index)); }
|
||||
public @NonNull DcLot getSummary(int index, DcChat chat) { return new DcLot(getSummaryCPtr(index, chat==null? null : chat.getChatCPtr())); }
|
||||
|
||||
|
||||
@@ -1,28 +1,5 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Delta Chat Java Adapter
|
||||
* (C) 2017 Björn Petersen
|
||||
* Contact: r10s@b44t.com, http://b44t.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see http://www.gnu.org/licenses/ .
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
package com.b44t.messenger;
|
||||
|
||||
|
||||
public class DcContact {
|
||||
|
||||
public final static int DC_CONTACT_ID_SELF = 1;
|
||||
@@ -59,14 +36,15 @@ public class DcContact {
|
||||
return getAddr();
|
||||
}
|
||||
|
||||
public native int getId ();
|
||||
public native String getName ();
|
||||
public native String getDisplayName();
|
||||
public native String getFirstName ();
|
||||
public native String getAddr ();
|
||||
public native String getNameNAddr ();
|
||||
public native boolean isBlocked ();
|
||||
public native boolean isVerified ();
|
||||
public native int getId ();
|
||||
public native String getName ();
|
||||
public native String getDisplayName ();
|
||||
public native String getFirstName ();
|
||||
public native String getAddr ();
|
||||
public native String getNameNAddr ();
|
||||
public native String getProfileImage();
|
||||
public native boolean isBlocked ();
|
||||
public native boolean isVerified ();
|
||||
|
||||
// working with raw c-data
|
||||
private long contactCPtr; // CAVE: the name is referenced in the JNI
|
||||
|
||||
@@ -1,28 +1,5 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Delta Chat Java Adapter
|
||||
* (C) 2017 Björn Petersen
|
||||
* Contact: r10s@b44t.com, http://b44t.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see http://www.gnu.org/licenses/ .
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
package com.b44t.messenger;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
public class DcContext {
|
||||
@@ -35,6 +12,7 @@ public class DcContext {
|
||||
public final static int DC_EVENT_INFO = 100;
|
||||
public final static int DC_EVENT_WARNING = 300;
|
||||
public final static int DC_EVENT_ERROR = 400;
|
||||
public final static int DC_EVENT_ERROR_NETWORK = 401;
|
||||
public final static int DC_EVENT_MSGS_CHANGED = 2000;
|
||||
public final static int DC_EVENT_INCOMING_MSG = 2005;
|
||||
public final static int DC_EVENT_MSG_DELIVERED = 2010;
|
||||
@@ -86,13 +64,13 @@ public class DcContext {
|
||||
public native void configure ();
|
||||
public native void stopOngoingProcess ();
|
||||
public native int isConfigured ();
|
||||
public native void performJobs ();
|
||||
public native void fetch ();
|
||||
public native void idle ();
|
||||
public native void interruptIdle ();
|
||||
public native void performImapJobs ();
|
||||
public native void performImapFetch ();
|
||||
public native void performImapIdle ();
|
||||
public native void interruptImapIdle ();
|
||||
public native void performSmtpJobs ();
|
||||
public native void performSmtpIdle ();
|
||||
public native void interruptSmtpIdle ();
|
||||
public native void maybeNetwork ();
|
||||
public native void setConfig (String key, String value);
|
||||
public void setConfigInt (String key, int value) { setConfig(key, Integer.toString(value)); }
|
||||
public native String getConfig (String key, String def);
|
||||
|
||||
@@ -1,43 +1,35 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Delta Chat Java Adapter
|
||||
* (C) 2017 Björn Petersen
|
||||
* Contact: r10s@b44t.com, http://b44t.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see http://www.gnu.org/licenses/ .
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
package com.b44t.messenger;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class DcEventCenter {
|
||||
private Hashtable<Integer, ArrayList<Object>> allObservers = new Hashtable<>();
|
||||
private Hashtable<Integer, ArrayList<DcEventDelegate>> allObservers = new Hashtable<>();
|
||||
private final Object LOCK = new Object();
|
||||
|
||||
public interface DcEventDelegate {
|
||||
void handleEvent(int eventId, Object data1, Object data2);
|
||||
default boolean runOnMain() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void addObserver(Object observer, int eventId) {
|
||||
/**
|
||||
* @deprecated use addObserver(int, DcEventDelegate) instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void addObserver(DcEventDelegate observer, int eventId) {
|
||||
addObserver(eventId, observer);
|
||||
}
|
||||
|
||||
public void addObserver(int eventId, DcEventDelegate observer) {
|
||||
synchronized (LOCK) {
|
||||
ArrayList<Object> idObservers = allObservers.get(eventId);
|
||||
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
|
||||
if (idObservers == null) {
|
||||
allObservers.put(eventId, (idObservers = new ArrayList<>()));
|
||||
}
|
||||
@@ -45,21 +37,19 @@ public class DcEventCenter {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeObserver(Object observer, int eventId) {
|
||||
public void removeObserver(int eventId, DcEventDelegate observer) {
|
||||
synchronized (LOCK) {
|
||||
ArrayList<Object> idObservers = allObservers.get(eventId);
|
||||
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
|
||||
if (idObservers != null) {
|
||||
idObservers.remove(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeObservers(Object observer) {
|
||||
public void removeObservers(DcEventDelegate observer) {
|
||||
synchronized (LOCK) {
|
||||
Enumeration<Integer> enumKey = allObservers.keys();
|
||||
while(enumKey.hasMoreElements()) {
|
||||
Integer eventId = enumKey.nextElement();
|
||||
ArrayList<Object> idObservers = allObservers.get(eventId);
|
||||
for(Integer eventId : allObservers.keySet()) {
|
||||
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
|
||||
if (idObservers != null) {
|
||||
idObservers.remove(observer);
|
||||
}
|
||||
@@ -69,11 +59,14 @@ public class DcEventCenter {
|
||||
|
||||
public void sendToObservers(int eventId, Object data1, Object data2) {
|
||||
synchronized (LOCK) {
|
||||
ArrayList<Object> idObservers = allObservers.get(eventId);
|
||||
ArrayList<DcEventDelegate> idObservers = allObservers.get(eventId);
|
||||
if (idObservers != null) {
|
||||
for (int i = 0; i < idObservers.size(); i++) {
|
||||
Object observer = idObservers.get(i);
|
||||
((DcEventDelegate) observer).handleEvent(eventId, data1, data2);
|
||||
for (DcEventDelegate observer : idObservers) {
|
||||
if(observer.runOnMain()) {
|
||||
Util.runOnMain(() -> observer.handleEvent(eventId, data1, data2));
|
||||
} else {
|
||||
Util.runOnBackground(() -> observer.handleEvent(eventId, data1, data2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,5 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Delta Chat Java Adapter
|
||||
* (C) 2017 Björn Petersen
|
||||
* Contact: r10s@b44t.com, http://b44t.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see http://www.gnu.org/licenses/ .
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
package com.b44t.messenger;
|
||||
|
||||
|
||||
public class DcLot {
|
||||
|
||||
public final static int DC_TEXT1_DRAFT = 1;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.b44t.messenger;
|
||||
|
||||
/**
|
||||
* Contains a list of media entries, their respective positions and ability to move through it.
|
||||
*/
|
||||
public class DcMediaGalleryElement {
|
||||
|
||||
final int[] mediaMsgs;
|
||||
int position;
|
||||
final DcContext context;
|
||||
public DcMediaGalleryElement(int[] mediaMsgs, int position, DcContext context, boolean leftIsRecent) {
|
||||
this.mediaMsgs = mediaMsgs;
|
||||
this.position = position;
|
||||
this.context = context;
|
||||
|
||||
// normal state is left is recent. If the ui needs right to be recent we reverse the order here.
|
||||
if (!leftIsRecent) {
|
||||
for (int ii = 0; ii < mediaMsgs.length / 2; ii++) {
|
||||
int tmp = mediaMsgs[ii];
|
||||
int opposite = mediaMsgs.length - 1 - ii;
|
||||
mediaMsgs[ii] = mediaMsgs[opposite];
|
||||
mediaMsgs[opposite] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mediaMsgs.length;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void moveToPosition(int newPosition) {
|
||||
if(newPosition < 0 || newPosition >= mediaMsgs.length)
|
||||
throw new IllegalArgumentException("can't move outside of known area.");
|
||||
position = newPosition;
|
||||
}
|
||||
|
||||
public DcMsg getMessage() {
|
||||
return context.getMsg(mediaMsgs[position]);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,11 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Delta Chat Java Adapter
|
||||
* (C) 2017 Björn Petersen
|
||||
* Contact: r10s@b44t.com, http://b44t.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see http://www.gnu.org/licenses/ .
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
package com.b44t.messenger;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.model.Quote;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
|
||||
public class DcMsg {
|
||||
@@ -54,6 +27,7 @@ public class DcMsg {
|
||||
public final static int DC_STATE_OUT_DELIVERED = 26;
|
||||
public final static int DC_STATE_OUT_MDN_RCVD = 28;
|
||||
|
||||
public static final int DC_MSG_NO_ID = 0;
|
||||
public final static int DC_MSG_ID_MARKER1 = 1;
|
||||
public final static int DC_MSG_ID_DAYMARKER = 9;
|
||||
|
||||
@@ -111,6 +85,12 @@ public class DcMsg {
|
||||
public native void setDimension (int width, int height);
|
||||
public native void setDuration (int duration);
|
||||
|
||||
public File getFileAsFile() {
|
||||
if(getFile()==null)
|
||||
throw new AssertionError("expected a file to be present.");
|
||||
return new File(getFile());
|
||||
}
|
||||
|
||||
// aliases and higher-level tools
|
||||
public static int[] msgSetToIds(final Set<DcMsg> dcMsgs) {
|
||||
int cnt = dcMsgs==null? 0 : dcMsgs.size();
|
||||
@@ -187,12 +167,6 @@ public class DcMsg {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Recipient getIndividualRecipient() {
|
||||
// TODO: remove the dependency to org.thoughtcrime.securesms.recipients.Recipient
|
||||
RecipientProvider.RecipientDetails recipientDetails = new RecipientProvider.RecipientDetails("individualRecipient", null, false, null, null);
|
||||
return new Recipient(Address.fromContact(getFromId()), recipientDetails);
|
||||
}
|
||||
|
||||
// working with raw c-data
|
||||
private long msgCPtr; // CAVE: the name is referenced in the JNI
|
||||
private native void unrefMsgCPtr ();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.arch.lifecycle.DefaultLifecycleObserver;
|
||||
import android.arch.lifecycle.LifecycleOwner;
|
||||
import android.arch.lifecycle.ProcessLifecycleOwner;
|
||||
@@ -25,23 +26,18 @@ import android.support.annotation.NonNull;
|
||||
import android.support.multidex.MultiDexApplication;
|
||||
import android.util.Log;
|
||||
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEventCenter;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
|
||||
import org.thoughtcrime.securesms.crypto.PRNGFixes;
|
||||
import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobmanager.dependencies.DependencyInjector;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.JavaJobSerializer;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirementProvider;
|
||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirementProvider;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.SqlCipherMigrationRequirementProvider;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.util.ScreenLockUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.webrtc.PeerConnectionFactory;
|
||||
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||
@@ -52,8 +48,6 @@ import org.whispersystems.libsignal.util.AndroidSignalProtocolLogger;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import dagger.ObjectGraph;
|
||||
|
||||
/**
|
||||
* Will be called once when the TextSecure process is created.
|
||||
*
|
||||
@@ -68,7 +62,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
|
||||
private ExpiringMessageManager expiringMessageManager;
|
||||
private JobManager jobManager;
|
||||
private ObjectGraph objectGraph;
|
||||
|
||||
private volatile boolean isAppVisible;
|
||||
|
||||
@@ -87,34 +80,28 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
|
||||
initializeRandomNumberFix();
|
||||
initializeLogging();
|
||||
initializeDependencyInjection();
|
||||
initializeJobManager();
|
||||
initializeExpiringMessageManager();
|
||||
//initializeSignedPreKeyCheck(); -- keys are generated in the core, however, not sure if this is needed for the lock screen
|
||||
initializePeriodicTasks();
|
||||
initializeWebRtc();
|
||||
initializeIncomingMessageNotifier();
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(@NonNull LifecycleOwner owner) {
|
||||
isAppVisible = true;
|
||||
Log.i(TAG, "App is now visible.");
|
||||
executePendingContactSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(@NonNull LifecycleOwner owner) {
|
||||
isAppVisible = false;
|
||||
Log.i(TAG, "App is no longer visible.");
|
||||
ScreenLockUtil.shouldLockApp = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectDependencies(Object object) {
|
||||
if (object instanceof InjectableType) {
|
||||
objectGraph.inject(object);
|
||||
}
|
||||
}
|
||||
|
||||
public JobManager getJobManager() {
|
||||
@@ -137,35 +124,43 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
SignalProtocolLoggerProvider.setProvider(new AndroidSignalProtocolLogger());
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void initializeIncomingMessageNotifier() {
|
||||
|
||||
DcEventCenter dcEventCenter = dcContext.eventCenter;
|
||||
dcEventCenter.addObserver(DcContext.DC_EVENT_INCOMING_MSG, new DcEventCenter.DcEventDelegate() {
|
||||
@Override
|
||||
public void handleEvent(int eventId, Object data1, Object data2) {
|
||||
MessageNotifier.updateNotification(dcContext.context, ((Long) data1).intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean runOnMain() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// in five seconds, the system should be up and ready so we can start issuing notifications.
|
||||
|
||||
Util.runOnBackgroundDelayed(() -> {
|
||||
MessageNotifier.updateNotification(dcContext.context);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("TextSecureJobs")
|
||||
.withDependencyInjector(this)
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withRequirementProviders(new MasterSecretRequirementProvider(this),
|
||||
new NetworkRequirementProvider(this),
|
||||
new SqlCipherMigrationRequirementProvider())
|
||||
.withConsumerThreads(5)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void initializeDependencyInjection() {
|
||||
this.objectGraph = ObjectGraph.create(new SignalCommunicationModule(this, new SignalServiceNetworkAccess(this)),
|
||||
new AxolotlStorageModule(this));
|
||||
}
|
||||
|
||||
private void initializeSignedPreKeyCheck() {
|
||||
if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) {
|
||||
jobManager.add(new CreateSignedPreKeyJob(this));
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeExpiringMessageManager() {
|
||||
this.expiringMessageManager = new ExpiringMessageManager(this);
|
||||
}
|
||||
|
||||
private void initializePeriodicTasks() {
|
||||
//RotateSignedPreKeyListener.schedule(this); -- no key rotation in the ui part
|
||||
//DirectoryRefreshListener.schedule(this); -- directory in this sense seems to be the address book
|
||||
//LocalBackupListener.schedule(this); -- disabled for now, there is no automatic backup; maybe the implicit IMAP backup is sufficient
|
||||
|
||||
@@ -208,10 +203,4 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void executePendingContactSync() {
|
||||
if (TextSecurePreferences.needsFullContactSync(this)) {
|
||||
ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Bundle;
|
||||
@@ -33,6 +35,7 @@ import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||
@@ -65,6 +68,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance";
|
||||
private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats";
|
||||
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
|
||||
private static final String PREFERENCE_CATEGORY_INVITE = "preference_category_invite";
|
||||
private static final String PREFERENCE_CATEGORY_HELP = "preference_category_help";
|
||||
|
||||
public static final int REQUEST_CODE_SET_BACKGROUND = 11;
|
||||
|
||||
@@ -147,6 +152,10 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_CHATS));
|
||||
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
|
||||
this.findPreference(PREFERENCE_CATEGORY_INVITE)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_INVITE));
|
||||
this.findPreference(PREFERENCE_CATEGORY_HELP)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
|
||||
|
||||
if (VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
tintIcons(getActivity());
|
||||
@@ -179,37 +188,36 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
.setSummary(ChatsPreferenceFragment.getSummary(getActivity()));
|
||||
}
|
||||
|
||||
private void setCategoryVisibility() {
|
||||
}
|
||||
|
||||
@TargetApi(11)
|
||||
private void tintIcons(Context context) {
|
||||
Drawable sms = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_textsms_white_24dp));
|
||||
Drawable notifications = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_notifications_white_24dp));
|
||||
Drawable privacy = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_security_white_24dp));
|
||||
Drawable appearance = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_brightness_6_white_24dp));
|
||||
Drawable chats = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_forum_white_24dp));
|
||||
Drawable devices = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_laptop_white_24dp));
|
||||
Drawable advanced = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_advanced_white_24dp));
|
||||
Drawable invite = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_mood_white_24dp));
|
||||
Drawable help = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_help_white_24dp));
|
||||
|
||||
int[] tintAttr = new int[]{R.attr.pref_icon_tint};
|
||||
TypedArray typedArray = context.obtainStyledAttributes(tintAttr);
|
||||
int color = typedArray.getColor(0, 0x0);
|
||||
typedArray.recycle();
|
||||
|
||||
DrawableCompat.setTint(sms, color);
|
||||
DrawableCompat.setTint(notifications, color);
|
||||
DrawableCompat.setTint(privacy, color);
|
||||
DrawableCompat.setTint(appearance, color);
|
||||
DrawableCompat.setTint(chats, color);
|
||||
DrawableCompat.setTint(devices, color);
|
||||
DrawableCompat.setTint(advanced, color);
|
||||
DrawableCompat.setTint(invite, color);
|
||||
DrawableCompat.setTint(help, color);
|
||||
|
||||
this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS).setIcon(notifications);
|
||||
this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION).setIcon(privacy);
|
||||
this.findPreference(PREFERENCE_CATEGORY_APPEARANCE).setIcon(appearance);
|
||||
this.findPreference(PREFERENCE_CATEGORY_CHATS).setIcon(chats);
|
||||
this.findPreference(PREFERENCE_CATEGORY_ADVANCED).setIcon(advanced);
|
||||
this.findPreference(PREFERENCE_CATEGORY_INVITE).setIcon(invite);
|
||||
this.findPreference(PREFERENCE_CATEGORY_HELP).setIcon(help);
|
||||
}
|
||||
|
||||
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
|
||||
@@ -239,6 +247,16 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
case PREFERENCE_CATEGORY_ADVANCED:
|
||||
fragment = new AdvancedPreferenceFragment();
|
||||
break;
|
||||
case PREFERENCE_CATEGORY_INVITE:
|
||||
startActivity(new Intent(getActivity(), InviteActivity.class));
|
||||
break;
|
||||
case PREFERENCE_CATEGORY_HELP:
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help_url))));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getActivity(), R.string.ConversationListActivity_there_is_no_browser_installed_on_your_device, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == ScreenLockUtil.REQUEST_CODE_CONFIRM_CREDENTIALS) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == ScreenLockUtil.REQUEST_CODE_CONFIRM_CREDENTIALS) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
shouldLockApp = false;
|
||||
} else {
|
||||
|
||||
@@ -8,11 +8,8 @@ import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.support.annotation.NonNull;
|
||||
|
||||
import com.b44t.messenger.DcLot;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
|
||||
@@ -14,6 +13,7 @@ import java.util.Set;
|
||||
public interface BindableConversationListItem extends Unbindable {
|
||||
|
||||
public void bind(@NonNull ThreadRecord thread,
|
||||
int msgId,
|
||||
@NonNull DcLot dcSummary,
|
||||
@NonNull GlideRequests glideRequests, @NonNull Locale locale,
|
||||
@NonNull Set<Long> selectedThreads, boolean batchMode);
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
@@ -128,12 +127,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
@Override
|
||||
protected Void doInBackground(Context... params) {
|
||||
|
||||
try {
|
||||
DirectoryHelper.refreshDirectory(params[0], true);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
public static final String REFRESHABLE = "refreshable";
|
||||
public static final String RECENTS = "recents";
|
||||
public static final String SELECT_VERIFIED_EXTRA = "select_verified";
|
||||
public static final String FROM_SHARE_ACTIVITY_EXTRA = "from_share_activity";
|
||||
|
||||
private ApplicationDcContext dcContext;
|
||||
|
||||
@@ -233,6 +234,10 @@ public class ContactSelectionListFragment extends Fragment
|
||||
return getActivity().getIntent().getBooleanExtra(SELECT_VERIFIED_EXTRA, false);
|
||||
}
|
||||
|
||||
private boolean isFromShareActivity() {
|
||||
return getActivity().getIntent().getBooleanExtra(FROM_SHARE_ACTIVITY_EXTRA, false);
|
||||
}
|
||||
|
||||
private void initializeCursor() {
|
||||
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(),
|
||||
GlideApp.with(this),
|
||||
@@ -267,11 +272,12 @@ public class ContactSelectionListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
public Loader<DcContactsLoader.Ret> onCreateLoader(int id, Bundle args) {
|
||||
boolean addCreateGroupLinks = !isMulti();
|
||||
boolean addCreateGroupLinks = isFromShareActivity() ? false : !isMulti();
|
||||
int listflags = DcContext.DC_GCL_ADD_SELF;
|
||||
if(isSelectVerfied()) {
|
||||
listflags = DcContext.DC_GCL_VERIFIED_ONLY;
|
||||
}
|
||||
|
||||
return new DcContactsLoader(getActivity(), listflags, cursorFilter, addCreateGroupLinks);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,8 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Browser;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.view.WindowCompat;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
@@ -56,22 +54,20 @@ import android.view.View.OnFocusChangeListener;
|
||||
import android.view.View.OnKeyListener;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEventCenter;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.google.android.gms.location.places.ui.PlacePicker;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.audio.AudioRecorder;
|
||||
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
|
||||
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
||||
@@ -103,14 +99,9 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
||||
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
||||
import org.thoughtcrime.securesms.mms.AttachmentManager;
|
||||
import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType;
|
||||
import org.thoughtcrime.securesms.mms.AudioSlide;
|
||||
@@ -118,32 +109,22 @@ import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.LocationSlide;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.QuoteId;
|
||||
import org.thoughtcrime.securesms.mms.QuoteModel;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.scribbles.ScribbleActivity;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
@@ -156,15 +137,20 @@ import org.thoughtcrime.securesms.util.views.Stub;
|
||||
import org.whispersystems.libsignal.InvalidMessageException;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.thoughtcrime.securesms.TransportOption.Type;
|
||||
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
|
||||
/**
|
||||
* Activity for displaying a message thread, as well as
|
||||
@@ -177,7 +163,6 @@ import static org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
implements ConversationFragment.ConversationFragmentListener,
|
||||
AttachmentManager.AttachmentListener,
|
||||
RecipientModifiedListener,
|
||||
DcEventCenter.DcEventDelegate,
|
||||
OnKeyboardShownListener,
|
||||
AttachmentDrawerListener,
|
||||
@@ -186,12 +171,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
{
|
||||
private static final String TAG = ConversationActivity.class.getSimpleName();
|
||||
|
||||
public static final String ADDRESS_EXTRA = "address";
|
||||
public static final String THREAD_ID_EXTRA = "thread_id";
|
||||
public static final String IS_ARCHIVED_EXTRA = "is_archived";
|
||||
public static final String TEXT_EXTRA = "draft_text";
|
||||
public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type";
|
||||
public static final String TIMING_EXTRA = "timing";
|
||||
public static final String LAST_SEEN_EXTRA = "last_seen";
|
||||
public static final String STARTING_POSITION_EXTRA = "starting_position";
|
||||
|
||||
@@ -215,7 +197,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
protected ConversationTitleView titleView;
|
||||
private TextView charactersLeft;
|
||||
private ConversationFragment fragment;
|
||||
private Button unblockButton;
|
||||
private InputAwareLayout container;
|
||||
private View composePanel;
|
||||
protected Stub<ReminderView> reminderView;
|
||||
@@ -229,17 +210,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
protected HidingLinearLayout quickAttachmentToggle;
|
||||
private QuickAttachmentDrawer quickAttachmentDrawer;
|
||||
private InputPanel inputPanel;
|
||||
private ImageView imageView;
|
||||
|
||||
private Recipient recipient;
|
||||
private ApplicationDcContext dcContext;
|
||||
private DcChat dcChat = new DcChat(0);
|
||||
private long threadId;
|
||||
private int distributionType;
|
||||
private int threadId;
|
||||
private boolean archived;
|
||||
private final boolean isSecureText = true;
|
||||
private boolean isDefaultSms = true;
|
||||
private boolean isMmsEnabled = true;
|
||||
private boolean isSecurityInitialized = false;
|
||||
|
||||
|
||||
@@ -281,7 +259,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
if (result != null && result) {
|
||||
Log.i(TAG, "Finished loading draft");
|
||||
Util.runOnMain(() -> {
|
||||
if (fragment != null && fragment.isResumed()) {
|
||||
fragment.moveToLastSeen();
|
||||
@@ -333,7 +310,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -348,14 +324,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
titleView.setTitle(glideRequests, dcChat);
|
||||
titleView.setVerified(dcChat.isVerified());
|
||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||
setGroupShareProfileReminder(recipient);
|
||||
calculateCharactersRemaining();
|
||||
|
||||
MessageNotifier.setVisibleThread(threadId);
|
||||
markThreadAsRead();
|
||||
|
||||
Log.w(TAG, "onResume() Finished: " + (System.currentTimeMillis() - getIntent().getLongExtra(TIMING_EXTRA, 0)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -369,13 +342,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
fragment.setLastSeen(System.currentTimeMillis());
|
||||
markLastSeen();
|
||||
AudioSlidePlayer.stopAll();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -393,7 +364,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
saveDraft();
|
||||
if (recipient != null) recipient.removeListener(this);
|
||||
if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver);
|
||||
dcContext.eventCenter.removeObservers(this);
|
||||
super.onDestroy();
|
||||
@@ -441,9 +411,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
break;
|
||||
case GROUP_EDIT:
|
||||
recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
|
||||
recipient.addListener(this);
|
||||
dcChat = dcContext.getChat((int)threadId);
|
||||
titleView.setTitle(glideRequests, dcChat);
|
||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||
supportInvalidateOptionsMenu();
|
||||
break;
|
||||
case TAKE_PHOTO:
|
||||
@@ -453,7 +422,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
break;
|
||||
case ADD_CONTACT:
|
||||
recipient = Recipient.from(this, recipient.getAddress(), true);
|
||||
recipient.addListener(this);
|
||||
fragment.reloadList();
|
||||
break;
|
||||
case PICK_LOCATION:
|
||||
@@ -494,50 +462,33 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
MenuInflater inflater = this.getMenuInflater();
|
||||
menu.clear();
|
||||
|
||||
if (isSecureText) {
|
||||
if (recipient.getExpireMessages() > 0) {
|
||||
inflater.inflate(R.menu.conversation_expiring_on, menu);
|
||||
|
||||
final MenuItem item = menu.findItem(R.id.menu_expiring_messages);
|
||||
final View actionView = MenuItemCompat.getActionView(item);
|
||||
final TextView badgeView = actionView.findViewById(R.id.expiration_badge);
|
||||
|
||||
badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.getExpireMessages()));
|
||||
actionView.setOnClickListener(v -> onOptionsItemSelected(item));
|
||||
} else {
|
||||
inflater.inflate(R.menu.conversation_expiring_off, menu);
|
||||
}
|
||||
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isGroupConversation()) {
|
||||
inflater.inflate(R.menu.conversation_group_options, menu);
|
||||
|
||||
if (!isPushGroupConversation()) {
|
||||
inflater.inflate(R.menu.conversation_mms_group_options, menu);
|
||||
if (distributionType == ThreadDatabase.DistributionTypes.BROADCAST) {
|
||||
menu.findItem(R.id.menu_distribution_broadcast).setChecked(true);
|
||||
} else {
|
||||
menu.findItem(R.id.menu_distribution_conversation).setChecked(true);
|
||||
}
|
||||
} else if (isActiveGroup()) {
|
||||
inflater.inflate(R.menu.conversation_push_group_options, menu);
|
||||
}
|
||||
if (recipient != null && recipient.isMuted()) {
|
||||
inflater.inflate(R.menu.conversation_muted, menu);
|
||||
}
|
||||
else {
|
||||
inflater.inflate(R.menu.conversation_unmuted, menu);
|
||||
}
|
||||
|
||||
inflater.inflate(R.menu.conversation, menu);
|
||||
|
||||
if (isSingleConversation() && isSecureText) {
|
||||
inflater.inflate(R.menu.conversation_secure, menu);
|
||||
} else if (isSingleConversation()) {
|
||||
inflater.inflate(R.menu.conversation_insecure, menu);
|
||||
if (isGroupConversation()) {
|
||||
if (isActiveGroup()) {
|
||||
inflater.inflate(R.menu.conversation_push_group_options, menu);
|
||||
}
|
||||
}
|
||||
|
||||
if (recipient != null && recipient.isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
|
||||
else inflater.inflate(R.menu.conversation_unmuted, menu);
|
||||
|
||||
if (isSingleConversation() && getRecipient().getContactUri() == null) {
|
||||
inflater.inflate(R.menu.conversation_add_to_contacts, menu);
|
||||
if( dcChat.getArchived()==0 ) {
|
||||
inflater.inflate(R.menu.conversation_archive, menu);
|
||||
}
|
||||
else {
|
||||
inflater.inflate(R.menu.conversation_unarchive, menu);
|
||||
}
|
||||
|
||||
inflater.inflate(R.menu.conversation_delete, menu);
|
||||
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
return true;
|
||||
@@ -547,21 +498,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_add_attachment: handleAddAttachment(); return true;
|
||||
case R.id.menu_view_media: handleViewMedia(); return true;
|
||||
case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
|
||||
case R.id.menu_reset_secure_session: handleResetSecureSession(); return true;
|
||||
case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
|
||||
case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true;
|
||||
case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true;
|
||||
case R.id.menu_edit_group: handleEditPushGroup(); return true;
|
||||
case R.id.menu_leave: handleLeavePushGroup(); return true;
|
||||
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
|
||||
case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
|
||||
case R.id.menu_conversation_settings: handleConversationSettings(); return true;
|
||||
case R.id.menu_expiring_messages_off:
|
||||
case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true;
|
||||
case android.R.id.home: handleReturnToConversationList(); return true;
|
||||
case R.id.menu_add_attachment: handleAddAttachment(); return true;
|
||||
case R.id.menu_view_media: handleViewMedia(); return true;
|
||||
case R.id.menu_edit_group: handleEditPushGroup(); return true;
|
||||
case R.id.menu_leave: handleLeaveGroup(); return true;
|
||||
case R.id.menu_archive_chat: handleArchiveChat(); return true;
|
||||
case R.id.menu_delete_chat: handleDeleteChat(); return true;
|
||||
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
|
||||
case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
|
||||
case R.id.menu_conversation_settings: handleConversationSettings(); return true;
|
||||
case android.R.id.home: handleReturnToConversationList(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -579,11 +525,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
inputPanel.onKeyboardShown();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(ReminderUpdateEvent event) {
|
||||
updateReminders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
@@ -598,32 +539,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleSelectMessageExpiration() {
|
||||
if (isPushGroupConversation() && !isActiveGroup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection CodeBlock2Expr
|
||||
ExpirationDialog.show(this, recipient.getExpireMessages(), expirationTime -> {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime);
|
||||
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000L);
|
||||
MessageSender.send(ConversationActivity.this, outgoingMessage, threadId, false, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
invalidateOptionsMenu();
|
||||
if (fragment != null) fragment.setLastSeen(0);
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
});
|
||||
}
|
||||
|
||||
private void handleMuteNotifications() {
|
||||
MuteDialog.show(this, until -> {
|
||||
recipient.setMuted(until);
|
||||
@@ -663,175 +578,53 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void handleUnblock() {
|
||||
//noinspection CodeBlock2Expr
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.ConversationActivity_unblock_this_contact_question)
|
||||
.setMessage(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
||||
.setBlocked(recipient, false);
|
||||
|
||||
ApplicationContext.getInstance(ConversationActivity.this)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceBlockedUpdateJob(ConversationActivity.this));
|
||||
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleResetSecureSession() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.ConversationActivity_reset_secure_session_question);
|
||||
builder.setIconAttribute(R.attr.dialog_alert_icon);
|
||||
builder.setCancelable(true);
|
||||
builder.setMessage(R.string.ConversationActivity_this_may_help_if_youre_having_encryption_problems);
|
||||
builder.setPositiveButton(R.string.ConversationActivity_reset, (dialog, which) -> {
|
||||
if (isSingleConversation()) {
|
||||
final Context context = getApplicationContext();
|
||||
|
||||
OutgoingEndSessionMessage endSessionMessage =
|
||||
new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipient(), "TERMINATE", 0, -1));
|
||||
|
||||
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
|
||||
@Override
|
||||
protected Long doInBackground(OutgoingEndSessionMessage... messages) {
|
||||
return MessageSender.send(context, messages[0], threadId, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Long result) {
|
||||
sendComplete(result);
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, endSessionMessage);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void handleViewMedia() {
|
||||
Intent intent = new Intent(this, MediaOverviewActivity.class);
|
||||
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void handleLeavePushGroup() {
|
||||
if (getRecipient() == null) {
|
||||
Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient),
|
||||
Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
private void handleLeaveGroup() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group))
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
dcContext.removeContactFromChat((int)threadId, DcContact.DC_CONTACT_ID_SELF);
|
||||
Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void handleArchiveChat() {
|
||||
int doArchive = dcContext.getChat((int)threadId).getArchived()==0? 1: 0;
|
||||
dcContext.archiveChat((int)threadId, doArchive);
|
||||
Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show();
|
||||
if( doArchive == 1 ) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(getString(R.string.ConversationActivity_leave_group));
|
||||
builder.setIconAttribute(R.attr.dialog_info_icon);
|
||||
builder.setCancelable(true);
|
||||
builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group));
|
||||
builder.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
Context self = ConversationActivity.this;
|
||||
|
||||
try {
|
||||
String groupId = getRecipient().getAddress().toGroupString();
|
||||
DatabaseFactory.getGroupDatabase(self).setActive(groupId, false);
|
||||
|
||||
GroupContext context = GroupContext.newBuilder()
|
||||
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
|
||||
.setType(GroupContext.Type.QUIT)
|
||||
.build();
|
||||
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipient(), context, null, System.currentTimeMillis(), 0, null, Collections.emptyList());
|
||||
MessageSender.send(self, outgoingMessage, threadId, false, null);
|
||||
DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self)));
|
||||
initializeEnabledCheck();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
Toast.makeText(self, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNegativeButton(R.string.no, null);
|
||||
builder.show();
|
||||
private void handleDeleteChat() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.ConversationActivity_ask_delete_chat))
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
dcContext.deleteChat((int)threadId);
|
||||
Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
})
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void handleEditPushGroup() {
|
||||
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class);
|
||||
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipient.getAddress());
|
||||
if (dcChat.isVerified()) {
|
||||
intent.putExtra(GroupCreateActivity.GROUP_CREATE_VERIFIED_EXTRA, true);
|
||||
}
|
||||
startActivityForResult(intent, GROUP_EDIT);
|
||||
}
|
||||
|
||||
private void handleDistributionBroadcastEnabled(MenuItem item) {
|
||||
distributionType = ThreadDatabase.DistributionTypes.BROADCAST;
|
||||
item.setChecked(true);
|
||||
|
||||
if (threadId != -1) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
|
||||
.setDistributionType(threadId, ThreadDatabase.DistributionTypes.BROADCAST);
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDistributionConversationEnabled(MenuItem item) {
|
||||
distributionType = ThreadDatabase.DistributionTypes.CONVERSATION;
|
||||
item.setChecked(true);
|
||||
|
||||
if (threadId != -1) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
|
||||
.setDistributionType(threadId, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDisplayGroupRecipients() {
|
||||
//TODO: i think this button is not needed and the group options just go to the profile
|
||||
//new GroupMembersDialog(this, getRecipient()).display();
|
||||
}
|
||||
|
||||
private void handleAddToContacts() {
|
||||
if (recipient.getAddress().isGroup()) return;
|
||||
|
||||
try {
|
||||
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
|
||||
if (recipient.getAddress().isEmail()) {
|
||||
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString());
|
||||
} else {
|
||||
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString());
|
||||
}
|
||||
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
|
||||
startActivityForResult(intent, ADD_CONTACT);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleDisplayQuickContact() {
|
||||
if (recipient.getAddress().isGroup()) return false;
|
||||
|
||||
if (recipient.getContactUri() != null) {
|
||||
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
|
||||
} else {
|
||||
handleAddToContacts();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleAddAttachment() {
|
||||
if (attachmentTypeSelector == null) {
|
||||
attachmentTypeSelector = new AttachmentTypeSelector(this, getSupportLoaderManager(), new AttachmentTypeListener());
|
||||
@@ -853,7 +646,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
calculateCharactersRemaining();
|
||||
supportInvalidateOptionsMenu();
|
||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||
}
|
||||
|
||||
///// Initializers
|
||||
@@ -992,7 +784,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
|
||||
charactersLeft = ViewUtil.findById(this, R.id.space_left);
|
||||
emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub);
|
||||
unblockButton = ViewUtil.findById(this, R.id.unblock_button);
|
||||
composePanel = ViewUtil.findById(this, R.id.bottom_panel);
|
||||
container = ViewUtil.findById(this, R.id.layout_container);
|
||||
reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
|
||||
@@ -1000,7 +791,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
quickAttachmentDrawer = ViewUtil.findById(this, R.id.quick_attachment_drawer);
|
||||
quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
|
||||
inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
|
||||
imageView = ViewUtil.findById(this, R.id.background_imageview);
|
||||
|
||||
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
|
||||
|
||||
@@ -1029,9 +819,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
});
|
||||
|
||||
titleView.setOnClickListener(v -> handleConversationSettings());
|
||||
titleView.setOnLongClickListener(v -> handleDisplayQuickContact());
|
||||
titleView.setOnBackClickedListener(view -> super.onBackPressed());
|
||||
unblockButton.setOnClickListener(v -> handleUnblock());
|
||||
|
||||
composeText.setOnKeyListener(composeKeyPressedListener);
|
||||
composeText.addTextChangedListener(composeKeyPressedListener);
|
||||
@@ -1048,8 +836,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
String backgroundImagePath = TextSecurePreferences.getBackgroundImagePath(this);
|
||||
Drawable image = Drawable.createFromPath(backgroundImagePath);
|
||||
imageView.setImageDrawable(image);
|
||||
if(!backgroundImagePath.isEmpty()) {
|
||||
Drawable image = Drawable.createFromPath(backgroundImagePath);
|
||||
getWindow().setBackgroundDrawable(image);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initializeActionBar() {
|
||||
@@ -1063,13 +853,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
if (recipient != null) recipient.removeListener(this);
|
||||
|
||||
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
|
||||
threadId = getIntent().getIntExtra(THREAD_ID_EXTRA, -1);
|
||||
if(threadId == DcChat.DC_CHAT_NO_CHAT)
|
||||
throw new IllegalStateException("can't display a conversation for no chat.");
|
||||
dcChat = dcContext.getChat((int)threadId);
|
||||
recipient = dcContext.getRecipient(dcChat);
|
||||
archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false);
|
||||
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
glideRequests = GlideApp.with(this);
|
||||
|
||||
|
||||
@@ -1079,20 +868,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
conversationContainer.setClipToPadding(true);
|
||||
}
|
||||
|
||||
recipient.addListener(this);
|
||||
}
|
||||
|
||||
public void onModified(final Recipient recipient) {
|
||||
Log.w(TAG, "onModified(" + recipient.getAddress().serialize() + ")");
|
||||
Util.runOnMain(() -> {
|
||||
Log.w(TAG, "onModifiedRun(): " + recipient.getRegistered());
|
||||
titleView.setTitle(glideRequests, dcChat);
|
||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||
setGroupShareProfileReminder(recipient);
|
||||
updateReminders();
|
||||
initializeSecurity(isSecureText, isDefaultSms);
|
||||
invalidateOptionsMenu();
|
||||
});
|
||||
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
composePanel.setVisibility(View.GONE);
|
||||
titleView.hideAvatar();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeReceivers() {
|
||||
@@ -1209,8 +988,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
return drafts;
|
||||
}
|
||||
|
||||
protected ListenableFuture<Long> saveDraft() {
|
||||
final SettableFuture<Long> future = new SettableFuture<>();
|
||||
protected ListenableFuture<Integer> saveDraft() {
|
||||
final SettableFuture<Integer> future = new SettableFuture<>();
|
||||
|
||||
if (this.recipient == null) {
|
||||
future.set(threadId);
|
||||
@@ -1218,32 +997,25 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
final Drafts drafts = getDraftsForCurrentState();
|
||||
final long thisThreadId = this.threadId;
|
||||
final int thisDistributionType = this.distributionType;
|
||||
final int thisThreadId = this.threadId;
|
||||
|
||||
new AsyncTask<Long, Void, Long>() {
|
||||
new AsyncTask<Integer, Void, Integer>() {
|
||||
@Override
|
||||
protected Long doInBackground(Long... params) {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(ConversationActivity.this);
|
||||
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
|
||||
long threadId = params[0];
|
||||
protected Integer doInBackground(Integer... params) {
|
||||
int threadId = params[0];
|
||||
|
||||
if (drafts.size() > 0) {
|
||||
if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipient(), thisDistributionType);
|
||||
|
||||
draftDatabase.insertDrafts(threadId, drafts);
|
||||
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
|
||||
drafts.getUriSnippet(),
|
||||
System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true);
|
||||
if (drafts.size() > 0 && threadId>0) {
|
||||
// todo: the following line only works for text drafts
|
||||
dcContext.setDraft(dcChat.getId(), drafts.getSnippet(dcContext.context));
|
||||
} else if (threadId > 0) {
|
||||
threadDatabase.update(threadId, false);
|
||||
dcContext.setDraft(dcChat.getId(), null);
|
||||
}
|
||||
|
||||
return threadId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Long result) {
|
||||
protected void onPostExecute(Integer result) {
|
||||
future.set(result);
|
||||
}
|
||||
|
||||
@@ -1252,16 +1024,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
return future;
|
||||
}
|
||||
|
||||
private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
|
||||
if (false) { // TODO: do we need a unblock button here? typically, the chatlist is closed when blocked
|
||||
unblockButton.setVisibility(View.VISIBLE);
|
||||
composePanel.setVisibility(View.GONE);
|
||||
} else {
|
||||
composePanel.setVisibility(View.VISIBLE);
|
||||
unblockButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setGroupShareProfileReminder(@NonNull Recipient recipient) {
|
||||
if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing()) {
|
||||
groupShareProfileView.get().setRecipient(recipient);
|
||||
@@ -1336,14 +1098,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
private void markThreadAsRead() {
|
||||
new AsyncTask<Long, Void, Void>() {
|
||||
new AsyncTask<Integer, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Long... params) {
|
||||
protected Void doInBackground(Integer... params) {
|
||||
Context context = ConversationActivity.this;
|
||||
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(params[0], false);
|
||||
ApplicationDcContext dcContext = DcHelper.getContext(context);
|
||||
int[] messageIds = dcContext.getChatMsgs(((ConversationActivity) context).threadId, 0, 0);
|
||||
dcContext.markseenMsgs(messageIds);
|
||||
|
||||
MessageNotifier.updateNotification(context);
|
||||
MarkReadReceiver.process(context, messageIds);
|
||||
MessageNotifier.updateNotification(context, threadId, false);
|
||||
// MarkReadReceiver.process(context, messageIds);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1351,16 +1115,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
private void markLastSeen() {
|
||||
new AsyncTask<Long, Void, Void>() {
|
||||
new AsyncTask<Integer, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Long... params) {
|
||||
DatabaseFactory.getThreadDatabase(ConversationActivity.this).setLastSeen(params[0]);
|
||||
protected Void doInBackground(Integer... params) {
|
||||
dcContext.marknoticedChat(params[0]);
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
|
||||
}
|
||||
|
||||
protected void sendComplete(long threadId) {
|
||||
protected void sendComplete(int threadId) {
|
||||
boolean refreshFragment = (threadId != this.threadId);
|
||||
this.threadId = threadId;
|
||||
|
||||
@@ -1419,42 +1183,88 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), Collections.emptyList(), expiresIn, subscriptionId, initiating);
|
||||
}
|
||||
|
||||
private String getRealPathFromAttachment(Attachment attachment) {
|
||||
try {
|
||||
// get file in the blobdir as `<blobdir>/<name>[-<uniqueNumber>].<ext>`
|
||||
String filename = attachment.getFileName();
|
||||
String ext = "";
|
||||
if(filename==null) {
|
||||
filename = new SimpleDateFormat("yyyy-MM-dd-HH-mm").format(new Date());
|
||||
ext = "."+MimeTypeMap.getSingleton().getExtensionFromMimeType(attachment.getContentType());
|
||||
}
|
||||
else {
|
||||
int i = filename.lastIndexOf(".");
|
||||
if(i>=0) {
|
||||
ext = filename.substring(i);
|
||||
filename = filename.substring(0, i);
|
||||
}
|
||||
}
|
||||
String path = null;
|
||||
for (int i=0; i<1000; i++) {
|
||||
path = dcContext.getBlobdir()+"/"+filename+(i==0? "" : i<100? "-"+i : "-"+(new Date().getTime()+i))+ext;
|
||||
if (!new File(path).exists()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// copy content to this file
|
||||
if(path!=null) {
|
||||
InputStream inputStream = PartAuthority.getAttachmentStream(this, attachment.getDataUri());
|
||||
OutputStream outputStream = new FileOutputStream(path);
|
||||
Util.copy(inputStream, outputStream);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ListenableFuture<Void> sendMediaMessage(final boolean forceSms, String body, SlideDeck slideDeck, List<Contact> contacts, final long expiresIn, final int subscriptionId, final boolean initiating) {
|
||||
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, distributionType, inputPanel.getQuote().orNull(), contacts);
|
||||
|
||||
final SettableFuture<Void> future = new SettableFuture<>();
|
||||
final Context context = getApplicationContext();
|
||||
|
||||
final OutgoingMediaMessage outgoingMessage;
|
||||
|
||||
if (isSecureText && !forceSms) {
|
||||
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessageCandidate);
|
||||
} else {
|
||||
outgoingMessage = outgoingMessageCandidate;
|
||||
}
|
||||
|
||||
inputPanel.clearQuote();
|
||||
attachmentManager.clear(glideRequests, false);
|
||||
composeText.setText("");
|
||||
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
||||
|
||||
new AsyncTask<Void, Void, Long>() {
|
||||
@Override
|
||||
protected Long doInBackground(Void... param) {
|
||||
if (initiating) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
||||
try {
|
||||
DcMsg msg = null;
|
||||
|
||||
List<Attachment> attachments = slideDeck.asAttachments();
|
||||
for (Attachment attachment : attachments) {
|
||||
String contentType = attachment.getContentType();
|
||||
|
||||
if (MediaUtil.isImageType(contentType)) {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_IMAGE);
|
||||
msg.setDimension(attachment.getWidth(), attachment.getHeight());
|
||||
}
|
||||
else if (MediaUtil.isAudioType(contentType)) {
|
||||
msg = new DcMsg(dcContext,
|
||||
attachment.isVoiceNote()? DcMsg.DC_MSG_VOICE : DcMsg.DC_MSG_AUDIO);
|
||||
}
|
||||
else if (MediaUtil.isVideoType(contentType)) {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_VIDEO);
|
||||
}
|
||||
else {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_FILE);
|
||||
}
|
||||
|
||||
return MessageSender.send(context, outgoingMessage, threadId, false, () -> fragment.releaseOutgoingMessage(id));
|
||||
String path = getRealPathFromAttachment(attachment);
|
||||
msg.setFile(path, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Long result) {
|
||||
sendComplete(result);
|
||||
future.set(null);
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
msg.setText(body);
|
||||
dcContext.sendMsg(dcChat.getId(), msg);
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
sendComplete(dcChat.getId());
|
||||
future.set(null);
|
||||
return future;
|
||||
}
|
||||
|
||||
@@ -1751,7 +1561,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThreadId(long threadId) {
|
||||
public void setThreadId(int threadId) {
|
||||
this.threadId = threadId;
|
||||
}
|
||||
|
||||
@@ -1831,7 +1641,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
public void handleEvent(int eventId, Object data1, Object data2) {
|
||||
if (eventId==DcContext.DC_EVENT_CHAT_MODIFIED || eventId==DcContext.DC_EVENT_CONTACTS_CHANGED) {
|
||||
onModified(recipient);
|
||||
titleView.setTitle(glideRequests, dcChat);
|
||||
setGroupShareProfileReminder(recipient);
|
||||
updateReminders();
|
||||
initializeSecurity(isSecureText, isDefaultSms);
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,13 +408,5 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
recordCache.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addFastRecord(@NonNull MessageRecord record) {
|
||||
// TODO: i think this is not need, we simply should reload the view
|
||||
}
|
||||
|
||||
public void releaseFastRecord(long id) {
|
||||
// TODO: i think this is not need, we simply should reload the view
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -55,6 +54,7 @@ import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEventCenter;
|
||||
@@ -68,16 +68,13 @@ import org.thoughtcrime.securesms.connect.DcMsgListLoader;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
||||
import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -87,6 +84,8 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.thoughtcrime.securesms.ShareActivity.EXTRA_MSG_IDS;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public class ConversationFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<int[]>,
|
||||
@@ -166,8 +165,16 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
private void setNoMessageText() {
|
||||
if(getListAdapter().isGroupChat()){
|
||||
noMessageTextView.setText(R.string.ConversationActivity_MsgNewGroupDraftHint);
|
||||
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
noMessageTextView.setText(R.string.conversation__no_messages);
|
||||
}
|
||||
else if(getListAdapter().isGroupChat()){
|
||||
if( dcContext.getChat((int)threadId).isUnpromoted() ) {
|
||||
noMessageTextView.setText(R.string.ConversationActivity_MsgNewGroupDraftHint);
|
||||
}
|
||||
else {
|
||||
noMessageTextView.setText(R.string.conversation__no_messages);
|
||||
}
|
||||
}else{
|
||||
String name = getListAdapter().getChatName();
|
||||
String message = getString(R.string.ConversationActivity_NoMessagesHint, name, name);
|
||||
@@ -211,7 +218,6 @@ public class ConversationFragment extends Fragment
|
||||
|
||||
public void moveToLastSeen() {
|
||||
if (lastSeen <= 0) {
|
||||
Log.i(TAG, "No need to move to last seen.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -225,8 +231,8 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.recipient = Recipient.from(getActivity(), getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true);
|
||||
this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
|
||||
this.threadId = this.getActivity().getIntent().getIntExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
|
||||
this.recipient = Recipient.from(getActivity(), Address.fromChat((int)this.threadId), true);
|
||||
this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
|
||||
this.startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1);
|
||||
this.firstLoad = true;
|
||||
@@ -273,7 +279,7 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
if (messageRecords.size() > 1) {
|
||||
menu.findItem(R.id.menu_context_forward).setVisible(false);
|
||||
// menu.findItem(R.id.menu_context_forward).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_details).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_save_attachment).setVisible(false);
|
||||
@@ -414,9 +420,11 @@ public class ConversationFragment extends Fragment
|
||||
.show();
|
||||
}
|
||||
|
||||
private void handleForwardMessage(DcMsg message) {
|
||||
private void handleForwardMessage(final Set<DcMsg> messageRecords) {
|
||||
Intent composeIntent = new Intent(getActivity(), ShareActivity.class);
|
||||
composeIntent.putExtra(Intent.EXTRA_TEXT, message.getDisplayBody().toString());
|
||||
int[] msgIds = DcMsg.msgSetToIds(messageRecords);
|
||||
composeIntent.putExtra(EXTRA_MSG_IDS, msgIds);
|
||||
// composeIntent.putExtra(Intent.EXTRA_TEXT, messageRecords.getDisplayBody().toString());
|
||||
/* TODO: handle fowarding media
|
||||
if (message.isMms()) {
|
||||
MmsMessageRecord mediaMessage = (MmsMessageRecord) message;
|
||||
@@ -557,36 +565,6 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
}
|
||||
|
||||
public long stageOutgoingMessage(OutgoingMediaMessage message) {
|
||||
MessageRecord messageRecord = DatabaseFactory.getMmsDatabase(getContext()).readerFor(message, threadId).getCurrent();
|
||||
|
||||
if (getListAdapter() != null) {
|
||||
//getListAdapter().setHeaderView(null);
|
||||
setLastSeen(0);
|
||||
getListAdapter().addFastRecord(messageRecord);
|
||||
}
|
||||
|
||||
return messageRecord.getId();
|
||||
}
|
||||
|
||||
public long stageOutgoingMessage(OutgoingTextMessage message) {
|
||||
MessageRecord messageRecord = DatabaseFactory.getSmsDatabase(getContext()).readerFor(message, threadId).getCurrent();
|
||||
|
||||
if (getListAdapter() != null) {
|
||||
//getListAdapter().setHeaderView(null);
|
||||
setLastSeen(0);
|
||||
getListAdapter().addFastRecord(messageRecord);
|
||||
}
|
||||
|
||||
return messageRecord.getId();
|
||||
}
|
||||
|
||||
public void releaseOutgoingMessage(long id) {
|
||||
if (getListAdapter() != null) {
|
||||
getListAdapter().releaseFastRecord(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollToStartingPosition(final int startingPosition) {
|
||||
list.post(() -> {
|
||||
list.getLayoutManager().scrollToPosition(startingPosition);
|
||||
@@ -601,7 +579,7 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
public interface ConversationFragmentListener {
|
||||
void setThreadId(long threadId);
|
||||
void setThreadId(int threadId);
|
||||
void handleReplyMessage(MessageRecord messageRecord);
|
||||
}
|
||||
|
||||
@@ -877,9 +855,9 @@ public class ConversationFragment extends Fragment
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == CODE_ADD_EDIT_CONTACT && getContext() != null) {
|
||||
ApplicationContext.getInstance(getContext().getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(getContext().getApplicationContext(), false));
|
||||
// ApplicationContext.getInstance(getContext().getApplicationContext())
|
||||
// .getJobManager()
|
||||
// .add(new DirectoryRefreshJob(getContext().getApplicationContext(), false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,7 +915,8 @@ public class ConversationFragment extends Fragment
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_forward:
|
||||
handleForwardMessage(getSelectedMessageRecord());
|
||||
// handleForwardMessage(getSelectedMessageRecord());
|
||||
handleForwardMessage(getListAdapter().getSelectedItems());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_resend:
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.graphics.PorterDuff;
|
||||
import android.support.annotation.DimenRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
@@ -391,7 +392,14 @@ public class ConversationItem extends LinearLayout
|
||||
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
audioViewStub.get().setAudio(new AudioSlide(context, messageRecord), showControls);
|
||||
if(dcChat.getId() == DcChat.DC_CHAT_ID_DEADDROP) { // no audio on dead drops
|
||||
// TODO: replace the currently defunct display of a play button with some notification text
|
||||
// to inform the user that here would be audio, if this were a proper chat, then ask the user
|
||||
// if he wants to start the chat on click.
|
||||
audioViewStub.get().setEnabled(false);
|
||||
audioViewStub.get().setOnClickListener(passthroughClickListener);
|
||||
} else
|
||||
audioViewStub.get().setAudio(new AudioSlide(context, messageRecord), showControls);
|
||||
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
@@ -676,18 +684,33 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeadDropClick() {
|
||||
new AlertDialog.Builder(context)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
int chatId = dcContext.createChatByMsgId(messageRecord.getId());
|
||||
if( chatId != 0 ) {
|
||||
Intent intent = new Intent(context, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, chatId);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setMessage(context.getString(R.string.new_conversation_activity__ask_start_chat_with, dcContext.getContact(messageRecord.getFromId()).getDisplayName()))
|
||||
.show();
|
||||
}
|
||||
|
||||
private class ThumbnailClickListener implements SlideClickListener {
|
||||
public void onClick(final View v, final Slide slide) {
|
||||
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
|
||||
if (dcChat.getId() == DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
handleDeadDropClick();
|
||||
} else if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
|
||||
performClick();
|
||||
} else if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) {
|
||||
Intent intent = new Intent(context, MediaPreviewActivity.class);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.setDataAndType(slide.getUri(), slide.getContentType());
|
||||
intent.putExtra(MediaPreviewActivity.DC_MSG_ID, slide.getDcMsgId());
|
||||
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
|
||||
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, messageRecord.isOutgoing());
|
||||
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
|
||||
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
|
||||
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false);
|
||||
|
||||
context.startActivity(intent);
|
||||
@@ -722,7 +745,9 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
if (!shouldInterceptClicks(messageRecord) && parent != null) {
|
||||
if (dcChat.getId() == DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
handleDeadDropClick();
|
||||
} else if (!shouldInterceptClicks(messageRecord) && parent != null) {
|
||||
parent.onClick(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.support.v7.widget.TooltipCompat;
|
||||
@@ -28,16 +26,14 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
import com.google.zxing.integration.android.IntentResult;
|
||||
|
||||
import org.thoughtcrime.securesms.components.SearchToolbar;
|
||||
import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.qr.QrScanHandler;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.search.SearchFragment;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
@@ -79,8 +75,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
|
||||
initializeSearchListener();
|
||||
|
||||
RegistrationLockDialog.showReminderIfNecessary(this);
|
||||
|
||||
TooltipCompat.setTooltipText(searchAction, getText(R.string.SearchToolbar_search_for_conversations_contacts_and_messages));
|
||||
}
|
||||
|
||||
@@ -158,13 +152,12 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_new_chat: createChat(); return true;
|
||||
case R.id.menu_settings: handleDisplaySettings(); return true;
|
||||
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
|
||||
case R.id.menu_invite: handleInvite(); return true;
|
||||
case R.id.menu_help: handleHelp(); return true;
|
||||
case R.id.menu_qr_scan: handleQrScan(); return true;
|
||||
case R.id.menu_qr_show: handleQrShow(); return true;
|
||||
case R.id.menu_new_chat: createChat(); return true;
|
||||
case R.id.menu_settings: handleDisplaySettings(); return true;
|
||||
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
|
||||
case R.id.menu_qr_scan: handleQrScan(); return true;
|
||||
case R.id.menu_qr_show: handleQrShow(); return true;
|
||||
case R.id.menu_deaddrop: handleDeaddrop(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -180,18 +173,15 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) {
|
||||
openConversation(threadId, recipient, distributionType, lastSeen, -1);
|
||||
public void onCreateConversation(int threadId, long lastSeen) {
|
||||
openConversation(threadId, lastSeen, -1);
|
||||
}
|
||||
|
||||
public void openConversation(long threadId, Recipient recipient, int distributionType, long lastSeen, int startingPosition) {
|
||||
public void openConversation(int threadId, long lastSeen, int startingPosition) {
|
||||
searchToolbar.clearFocus();
|
||||
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
|
||||
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis());
|
||||
intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, lastSeen);
|
||||
intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, startingPosition);
|
||||
|
||||
@@ -216,6 +206,12 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void handleDeaddrop() {
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, DcChat.DC_CHAT_ID_DEADDROP);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void handleDisplaySettings() {
|
||||
Intent preferencesIntent = new Intent(this, ApplicationPreferencesActivity.class);
|
||||
startActivity(preferencesIntent);
|
||||
@@ -227,20 +223,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
private void handleInvite() {
|
||||
startActivity(new Intent(this, InviteActivity.class));
|
||||
}
|
||||
|
||||
private void handleHelp() {
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help_url))));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.ConversationListActivity_there_is_no_browser_installed_on_your_device, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == IntentIntegrator.REQUEST_CODE) {
|
||||
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
|
||||
QrScanHandler qrScanHandler = new QrScanHandler(this);
|
||||
|
||||
@@ -48,6 +48,7 @@ class ConversationListAdapter extends RecyclerView.Adapter {
|
||||
private static final int MESSAGE_TYPE_SWITCH_ARCHIVE = 1;
|
||||
private static final int MESSAGE_TYPE_THREAD = 2;
|
||||
private static final int MESSAGE_TYPE_INBOX_ZERO = 3;
|
||||
private static final int MESSAGE_TYPE_DEADDROP = 4; // DEADDROP and THREAD share the same class, however, for DEADDROP it is modified on construction so it cannot be reused
|
||||
|
||||
private final @NonNull ApplicationDcContext dcContext;
|
||||
private @NonNull DcChatlist dcChatlist;
|
||||
@@ -131,14 +132,17 @@ class ConversationListAdapter extends RecyclerView.Adapter {
|
||||
ViewHolder holder = (ViewHolder)viewHolder;
|
||||
DcChat chat = dcContext.getChat(dcChatlist.getChatId(i));
|
||||
DcLot summary = dcChatlist.getSummary(i, chat);
|
||||
holder.getItem().bind(dcContext.getThreadRecord(summary, chat), summary, glideRequests, locale, batchSet, batchMode);
|
||||
holder.getItem().bind(dcContext.getThreadRecord(summary, chat), dcChatlist.getMsgId(i), summary, glideRequests, locale, batchSet, batchMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int i) {
|
||||
int chatId = dcChatlist.getChatId(i);
|
||||
|
||||
if (chatId == DcChat.DC_CHAT_ID_ARCHIVED_LINK) {
|
||||
if (chatId==DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
return MESSAGE_TYPE_DEADDROP;
|
||||
}
|
||||
else if (chatId == DcChat.DC_CHAT_ID_ARCHIVED_LINK) {
|
||||
return MESSAGE_TYPE_SWITCH_ARCHIVE;
|
||||
} else if(chatId == DcChat.DC_CHAT_ID_ALLDONE_HINT) {
|
||||
return MESSAGE_TYPE_INBOX_ZERO;
|
||||
|
||||
@@ -51,12 +51,10 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeenTime) {
|
||||
public void onCreateConversation(int threadId, long lastSeenTime) {
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
intent.putExtra(ConversationActivity.IS_ARCHIVED_EXTRA, true);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
|
||||
intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, lastSeenTime);
|
||||
|
||||
startActivity(intent);
|
||||
|
||||
@@ -50,13 +50,12 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcChatlist;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEventCenter;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.ConversationListAdapter.ItemClickListener;
|
||||
import org.thoughtcrime.securesms.components.recyclerview.DeleteItemAnimator;
|
||||
import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton;
|
||||
@@ -69,9 +68,7 @@ import org.thoughtcrime.securesms.components.reminder.ShareReminder;
|
||||
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
|
||||
import org.thoughtcrime.securesms.connect.DcChatlistLoader;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
@@ -159,7 +156,6 @@ public class ConversationListFragment extends Fragment
|
||||
|
||||
updateReminders(true);
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,7 +163,6 @@ public class ConversationListFragment extends Fragment
|
||||
super.onPause();
|
||||
|
||||
fab.stopPulse();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
public ConversationListAdapter getListAdapter() {
|
||||
@@ -270,10 +265,7 @@ public class ConversationListFragment extends Fragment
|
||||
final DcContext dcContext = DcHelper.getContext(getActivity());
|
||||
int conversationsCount = getListAdapter().getBatchSelections().size();
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
|
||||
alert.setIconAttribute(R.attr.dialog_alert_icon);
|
||||
alert.setTitle(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_delete_selected_conversations,
|
||||
conversationsCount, conversationsCount));
|
||||
alert.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_this_will_permanently_delete_all_n_selected_conversations,
|
||||
alert.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationListFragment_ask_selected_chats,
|
||||
conversationsCount, conversationsCount));
|
||||
alert.setCancelable(true);
|
||||
|
||||
@@ -322,8 +314,8 @@ public class ConversationListFragment extends Fragment
|
||||
actionMode.setTitle(String.valueOf(getListAdapter().getBatchSelections().size()));
|
||||
}
|
||||
|
||||
private void handleCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen) {
|
||||
((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, recipient, distributionType, lastSeen);
|
||||
private void handleCreateConversation(int threadId, long lastSeen) {
|
||||
((ConversationSelectedListener)getActivity()).onCreateConversation(threadId, lastSeen);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -368,8 +360,31 @@ public class ConversationListFragment extends Fragment
|
||||
@Override
|
||||
public void onItemClick(ConversationListItem item) {
|
||||
if (actionMode == null) {
|
||||
handleCreateConversation(item.getThreadId(), item.getRecipient(),
|
||||
item.getDistributionType(), item.getLastSeen());
|
||||
// the next line will fail for signal calls, DC uses int, though.
|
||||
int threadId = (int)item.getThreadId();
|
||||
|
||||
if (threadId==DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
DcContext dcContext = DcHelper.getContext(getActivity());
|
||||
int msgId = item.getMsgId();
|
||||
int contactId = item.getContactId();
|
||||
DcContact contact = dcContext.getContact(contactId);
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setMessage(getActivity().getString(R.string.new_conversation_activity__ask_start_chat_with, contact.getNameNAddr()))
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
int belongingChatId = dcContext.createChatByMsgId(msgId);
|
||||
if( belongingChatId != 0 ) {
|
||||
handleCreateConversation(belongingChatId, 0);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.not_now, null)
|
||||
.setNeutralButton(R.string.never, (dialog, which) -> {
|
||||
dcContext.blockContact(contactId, 1);
|
||||
})
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
|
||||
handleCreateConversation(threadId, item.getLastSeen());
|
||||
} else {
|
||||
ConversationListAdapter adapter = (ConversationListAdapter)list.getAdapter();
|
||||
adapter.toggleThreadInBatchSet(item.getThreadId());
|
||||
@@ -399,7 +414,7 @@ public class ConversationListFragment extends Fragment
|
||||
}
|
||||
|
||||
public interface ConversationSelectedListener {
|
||||
void onCreateConversation(long threadId, Recipient recipient, int distributionType, long lastSeen);
|
||||
void onCreateConversation(int threadId, long lastSeen);
|
||||
void onSwitchToArchive();
|
||||
}
|
||||
|
||||
@@ -450,11 +465,6 @@ public class ConversationListFragment extends Fragment
|
||||
actionMode = null;
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(ReminderUpdateEvent event) {
|
||||
updateReminders(false);
|
||||
}
|
||||
|
||||
private class ArchiveListenerCallback extends ItemTouchHelper.SimpleCallback {
|
||||
|
||||
ArchiveListenerCallback() {
|
||||
@@ -506,6 +516,12 @@ public class ConversationListFragment extends Fragment
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
|
||||
} else {
|
||||
if (threadId==DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
int contactId = ((ConversationListItem)viewHolder.itemView).getContactId();
|
||||
dcContext.marknoticedContact(contactId);
|
||||
return;
|
||||
}
|
||||
|
||||
new SnackbarAsyncTask<Long>(getView(),
|
||||
getResources().getQuantityString(R.plurals.ConversationListFragment_conversations_archived, 1, 1),
|
||||
getString(R.string.ConversationListFragment_undo),
|
||||
|
||||
@@ -17,12 +17,9 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Spannable;
|
||||
@@ -38,21 +35,24 @@ import android.widget.TextView;
|
||||
|
||||
import com.amulyakhare.textdrawable.TextDrawable;
|
||||
import com.annimon.stream.Stream;
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcLot;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.DeliveryStatusView;
|
||||
import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.search.model.MessageResult;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.ResUtil;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -61,8 +61,7 @@ import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class ConversationListItem extends RelativeLayout
|
||||
implements RecipientModifiedListener,
|
||||
BindableConversationListItem, Unbindable
|
||||
implements BindableConversationListItem, Unbindable
|
||||
{
|
||||
@SuppressWarnings("unused")
|
||||
private final static String TAG = ConversationListItem.class.getSimpleName();
|
||||
@@ -74,6 +73,7 @@ public class ConversationListItem extends RelativeLayout
|
||||
private Set<Long> selectedThreads;
|
||||
private Recipient recipient;
|
||||
private long threadId;
|
||||
private int msgId;
|
||||
private GlideRequests glideRequests;
|
||||
private TextView subjectView;
|
||||
private FromTextView fromView;
|
||||
@@ -81,14 +81,11 @@ public class ConversationListItem extends RelativeLayout
|
||||
private TextView archivedView;
|
||||
private DeliveryStatusView deliveryStatusIndicator;
|
||||
private ImageView unreadIndicator;
|
||||
private ImageView verifiedIndicator;
|
||||
private ImageView groupIndicator;
|
||||
private long lastSeen;
|
||||
|
||||
private int unreadCount;
|
||||
private AvatarImageView contactPhotoImage;
|
||||
private ThumbnailView thumbnailView;
|
||||
|
||||
private int distributionType;
|
||||
|
||||
public ConversationListItem(Context context) {
|
||||
this(context, null);
|
||||
@@ -106,11 +103,9 @@ public class ConversationListItem extends RelativeLayout
|
||||
this.dateView = findViewById(R.id.date);
|
||||
this.deliveryStatusIndicator = findViewById(R.id.delivery_status);
|
||||
this.contactPhotoImage = findViewById(R.id.contact_photo_image);
|
||||
this.thumbnailView = findViewById(R.id.thumbnail);
|
||||
this.archivedView = findViewById(R.id.archived);
|
||||
this.unreadIndicator = findViewById(R.id.unread_indicator);
|
||||
this.verifiedIndicator = findViewById(R.id.verified_indicator);
|
||||
thumbnailView.setClickable(false);
|
||||
this.groupIndicator = findViewById(R.id.group_indicator);
|
||||
|
||||
ViewUtil.setTextViewGravityStart(this.fromView, getContext());
|
||||
ViewUtil.setTextViewGravityStart(this.subjectView, getContext());
|
||||
@@ -118,16 +113,18 @@ public class ConversationListItem extends RelativeLayout
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull ThreadRecord thread,
|
||||
int msgId,
|
||||
@NonNull DcLot dcSummary,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<Long> selectedThreads,
|
||||
boolean batchMode)
|
||||
{
|
||||
bind(thread, dcSummary, glideRequests, locale, selectedThreads, batchMode, null);
|
||||
bind(thread, msgId, dcSummary, glideRequests, locale, selectedThreads, batchMode, null);
|
||||
}
|
||||
|
||||
public void bind(@NonNull ThreadRecord thread,
|
||||
int msgId,
|
||||
@NonNull DcLot dcSummary,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@@ -139,12 +136,11 @@ public class ConversationListItem extends RelativeLayout
|
||||
this.selectedThreads = selectedThreads;
|
||||
this.recipient = thread.getRecipient();
|
||||
this.threadId = thread.getThreadId();
|
||||
this.msgId = msgId;
|
||||
this.glideRequests = glideRequests;
|
||||
this.unreadCount = thread.getUnreadCount();
|
||||
this.distributionType = thread.getDistributionType();
|
||||
this.lastSeen = thread.getLastSeen();
|
||||
|
||||
this.recipient.addListener(this);
|
||||
if (highlightSubstring != null) {
|
||||
this.fromView.setText(getHighlightedSpan(locale, recipient.getName(), highlightSubstring));
|
||||
} else {
|
||||
@@ -159,24 +155,24 @@ public class ConversationListItem extends RelativeLayout
|
||||
if (thread.getDate() > 0) {
|
||||
CharSequence date = DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, thread.getDate());
|
||||
dateView.setText(date);
|
||||
dateView.setTypeface(unreadCount == 0 ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
|
||||
dateView.setTextColor(unreadCount == 0 ? ThemeUtil.getThemedColor(getContext(), R.attr.conversation_list_item_date_color)
|
||||
: ThemeUtil.getThemedColor(getContext(), R.attr.conversation_list_item_unread_color));
|
||||
}
|
||||
|
||||
if (thread.isArchived()) {
|
||||
this.archivedView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
this.archivedView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
setStatusIcons(thread);
|
||||
setThumbnailSnippet(thread);
|
||||
setBatchState(batchMode);
|
||||
setRippleColor(recipient);
|
||||
setUnreadIndicator(thread);
|
||||
this.contactPhotoImage.setAvatar(glideRequests, recipient, true);
|
||||
verifiedIndicator.setVisibility(thread.isVerified() ? VISIBLE : GONE);
|
||||
setBgColor();
|
||||
|
||||
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
ApplicationDcContext dcContext = DcHelper.getContext(getContext());
|
||||
DcContact dcContact = dcContext.getContact(dcContext.getMsg(msgId).getFromId());
|
||||
this.contactPhotoImage.setAvatar(glideRequests, dcContext.getRecipient(dcContact), true);
|
||||
}
|
||||
else {
|
||||
this.contactPhotoImage.setAvatar(glideRequests, recipient, true);
|
||||
}
|
||||
fromView.setCompoundDrawablesWithIntrinsicBounds(0, 0, thread.isVerified() ? R.drawable.ic_verified : 0, 0);
|
||||
groupIndicator.setVisibility(recipient.isGroupRecipient() ? VISIBLE : GONE);
|
||||
int color = ResUtil.getColor(getContext(), R.attr.conversation_list_item_contact_color);
|
||||
groupIndicator.setColorFilter(color);
|
||||
}
|
||||
|
||||
public void bind(@NonNull Recipient contact,
|
||||
@@ -188,18 +184,15 @@ public class ConversationListItem extends RelativeLayout
|
||||
this.recipient = contact;
|
||||
this.glideRequests = glideRequests;
|
||||
|
||||
this.recipient.addListener(this);
|
||||
|
||||
fromView.setText(getHighlightedSpan(locale, recipient.getName(), highlightSubstring));
|
||||
subjectView.setText(getHighlightedSpan(locale, contact.getAddress().toPhoneString(), highlightSubstring));
|
||||
dateView.setText("");
|
||||
archivedView.setVisibility(GONE);
|
||||
unreadIndicator.setVisibility(GONE);
|
||||
deliveryStatusIndicator.setNone();
|
||||
thumbnailView.setVisibility(GONE);
|
||||
|
||||
setBatchState(false);
|
||||
setRippleColor(contact);
|
||||
setBgColor();
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient, true);
|
||||
}
|
||||
|
||||
@@ -212,24 +205,20 @@ public class ConversationListItem extends RelativeLayout
|
||||
this.recipient = messageResult.recipient;
|
||||
this.glideRequests = glideRequests;
|
||||
|
||||
this.recipient.addListener(this);
|
||||
|
||||
fromView.setText(recipient, true);
|
||||
subjectView.setText(getHighlightedSpan(locale, messageResult.bodySnippet, highlightSubstring));
|
||||
dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.receivedTimestampMs));
|
||||
archivedView.setVisibility(GONE);
|
||||
unreadIndicator.setVisibility(GONE);
|
||||
deliveryStatusIndicator.setNone();
|
||||
thumbnailView.setVisibility(GONE);
|
||||
|
||||
setBatchState(false);
|
||||
setRippleColor(recipient);
|
||||
setBgColor();
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbind() {
|
||||
if (this.recipient != null) this.recipient.removeListener(this);
|
||||
}
|
||||
|
||||
private void setBatchState(boolean batch) {
|
||||
@@ -244,87 +233,76 @@ public class ConversationListItem extends RelativeLayout
|
||||
return threadId;
|
||||
}
|
||||
|
||||
public int getUnreadCount() {
|
||||
return unreadCount;
|
||||
public int getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
public int getDistributionType() {
|
||||
return distributionType;
|
||||
public int getContactId() {
|
||||
DcContext dcContext = DcHelper.getContext(getContext());
|
||||
return dcContext.getMsg(msgId).getFromId();
|
||||
}
|
||||
|
||||
public long getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
private void setThumbnailSnippet(ThreadRecord thread) {
|
||||
if (thread.getSnippetUri() != null) {
|
||||
this.thumbnailView.setVisibility(View.VISIBLE);
|
||||
this.thumbnailView.setImageResource(glideRequests, thread.getSnippetUri());
|
||||
|
||||
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
|
||||
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.thumbnail);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
subjectParams.addRule(RelativeLayout.START_OF, R.id.thumbnail);
|
||||
}
|
||||
this.subjectView.setLayoutParams(subjectParams);
|
||||
this.post(new ThumbnailPositioner(thumbnailView, archivedView, deliveryStatusIndicator, dateView));
|
||||
} else {
|
||||
this.thumbnailView.setVisibility(View.GONE);
|
||||
|
||||
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
|
||||
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.status);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
subjectParams.addRule(RelativeLayout.START_OF, R.id.status);
|
||||
}
|
||||
this.subjectView.setLayoutParams(subjectParams);
|
||||
}
|
||||
}
|
||||
|
||||
private void setStatusIcons(ThreadRecord thread) {
|
||||
int state = dcSummary.getState();
|
||||
|
||||
if (state==DcMsg.DC_STATE_IN_FRESH || state==DcMsg.DC_STATE_IN_NOTICED) {
|
||||
if (thread.isArchived())
|
||||
{
|
||||
// archived
|
||||
this.archivedView.setVisibility(View.VISIBLE);
|
||||
deliveryStatusIndicator.setNone();
|
||||
} else if (state==DcMsg.DC_STATE_OUT_ERROR) {
|
||||
deliveryStatusIndicator.setFailed();
|
||||
} else {
|
||||
if(state==DcMsg.DC_STATE_OUT_MDN_RCVD) {
|
||||
deliveryStatusIndicator.setRead();
|
||||
}
|
||||
else if(state==DcMsg.DC_STATE_OUT_DELIVERED) {
|
||||
deliveryStatusIndicator.setSent();
|
||||
}
|
||||
else if (state==DcMsg.DC_STATE_OUT_PENDING){
|
||||
deliveryStatusIndicator.setPending();
|
||||
}
|
||||
else {
|
||||
deliveryStatusIndicator.setNone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setRippleColor(Recipient recipient) {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
((RippleDrawable)(getBackground()).mutate())
|
||||
.setColor(ColorStateList.valueOf(recipient.getColor().toConversationColor(getContext())));
|
||||
}
|
||||
}
|
||||
|
||||
private void setUnreadIndicator(ThreadRecord thread) {
|
||||
if (thread.getUnreadCount() == 0) {
|
||||
unreadIndicator.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.archivedView.setVisibility(View.GONE);
|
||||
int state = dcSummary.getState();
|
||||
if (state==DcMsg.DC_STATE_IN_FRESH || state==DcMsg.DC_STATE_IN_NOTICED)
|
||||
{
|
||||
// incoming
|
||||
deliveryStatusIndicator.setNone();
|
||||
int unreadCount = thread.getUnreadCount();
|
||||
if(unreadCount==0) {
|
||||
unreadIndicator.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
unreadIndicator.setImageDrawable(TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.width(ViewUtil.dpToPx(getContext(), 24))
|
||||
.height(ViewUtil.dpToPx(getContext(), 24))
|
||||
.textColor(Color.WHITE)
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(String.valueOf(unreadCount), getResources().getColor(R.color.green_A700)));
|
||||
unreadIndicator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// outgoing
|
||||
unreadIndicator.setVisibility(View.GONE);
|
||||
if (state == DcMsg.DC_STATE_OUT_ERROR) {
|
||||
deliveryStatusIndicator.setFailed();
|
||||
} else if (state == DcMsg.DC_STATE_OUT_MDN_RCVD) {
|
||||
deliveryStatusIndicator.setRead();
|
||||
} else if (state == DcMsg.DC_STATE_OUT_DELIVERED) {
|
||||
deliveryStatusIndicator.setSent();
|
||||
} else if (state == DcMsg.DC_STATE_OUT_PENDING) {
|
||||
deliveryStatusIndicator.setPending();
|
||||
} else {
|
||||
deliveryStatusIndicator.setNone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unreadIndicator.setImageDrawable(TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.width(ViewUtil.dpToPx(getContext(), 24))
|
||||
.height(ViewUtil.dpToPx(getContext(), 24))
|
||||
.textColor(Color.WHITE)
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(String.valueOf(thread.getUnreadCount()), getResources().getColor(R.color.green_A700)));
|
||||
unreadIndicator.setVisibility(View.VISIBLE);
|
||||
private void setBgColor() {
|
||||
if(threadId==DcChat.DC_CHAT_ID_DEADDROP) {
|
||||
TypedArray ta = getContext().obtainStyledAttributes(new int[] { R.attr.conversation_list_deaddrop_bg_color});
|
||||
setBackgroundColor(ta.getColor(0, 0xffffffff));
|
||||
ta.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
private Spanned getHighlightedSpan(@NonNull Locale locale,
|
||||
@@ -364,50 +342,4 @@ public class ConversationListItem extends RelativeLayout
|
||||
|
||||
return spanned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient recipient) {
|
||||
Util.runOnMain(() -> {
|
||||
fromView.setText(recipient, unreadCount == 0);
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient, true);
|
||||
setRippleColor(recipient);
|
||||
});
|
||||
}
|
||||
|
||||
private static class ThumbnailPositioner implements Runnable {
|
||||
|
||||
private final View thumbnailView;
|
||||
private final View archivedView;
|
||||
private final View deliveryStatusView;
|
||||
private final View dateView;
|
||||
|
||||
ThumbnailPositioner(View thumbnailView, View archivedView, View deliveryStatusView, View dateView) {
|
||||
this.thumbnailView = thumbnailView;
|
||||
this.archivedView = archivedView;
|
||||
this.deliveryStatusView = deliveryStatusView;
|
||||
this.dateView = dateView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
LayoutParams thumbnailParams = (RelativeLayout.LayoutParams)thumbnailView.getLayoutParams();
|
||||
|
||||
if (archivedView.getVisibility() == View.VISIBLE &&
|
||||
(archivedView.getWidth() + deliveryStatusView.getWidth()) > dateView.getWidth())
|
||||
{
|
||||
thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.status);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
thumbnailParams.addRule(RelativeLayout.START_OF, R.id.status);
|
||||
}
|
||||
} else {
|
||||
thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.date);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
thumbnailParams.addRule(RelativeLayout.START_OF, R.id.date);
|
||||
}
|
||||
}
|
||||
|
||||
thumbnailView.setLayoutParams(thumbnailParams);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class ConversationListItemAction extends LinearLayout implements Bindable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull ThreadRecord thread, @NonNull DcLot dcSummary, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
|
||||
public void bind(@NonNull ThreadRecord thread, int msgId, @NonNull DcLot dcSummary, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
|
||||
this.description.setText(thread.getRecipient().getName());
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ConversationListItemInboxZero extends LinearLayout implements Binda
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull ThreadRecord thread, @NonNull DcLot dcSummary, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
|
||||
public void bind(@NonNull ThreadRecord thread, int msgId, @NonNull DcLot dcSummary, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,12 +77,11 @@ public class ConversationPopupActivity extends ConversationActivity {
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_expand:
|
||||
saveDraft().addListener(new ListenableFuture.Listener<Long>() {
|
||||
saveDraft().addListener(new ListenableFuture.Listener<Integer>() {
|
||||
@Override
|
||||
public void onSuccess(Long result) {
|
||||
public void onSuccess(Integer result) {
|
||||
ActivityOptionsCompat transition = ActivityOptionsCompat.makeScaleUpAnimation(getWindow().getDecorView(), 0, 0, getWindow().getAttributes().width, getWindow().getAttributes().height);
|
||||
Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, getRecipient().getAddress());
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, result);
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||
@@ -113,7 +112,7 @@ public class ConversationPopupActivity extends ConversationActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendComplete(long threadId) {
|
||||
protected void sendComplete(int threadId) {
|
||||
super.sendComplete(threadId);
|
||||
finish();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ public class ConversationTitleView extends RelativeLayout {
|
||||
private AvatarImageView avatar;
|
||||
private TextView title;
|
||||
private TextView subtitle;
|
||||
private ImageView verified;
|
||||
|
||||
public ConversationTitleView(Context context) {
|
||||
this(context, null);
|
||||
@@ -45,7 +44,6 @@ public class ConversationTitleView extends RelativeLayout {
|
||||
this.content = ViewUtil.findById(this, R.id.content);
|
||||
this.title = ViewUtil.findById(this, R.id.title);
|
||||
this.subtitle = ViewUtil.findById(this, R.id.subtitle);
|
||||
this.verified = ViewUtil.findById(this, R.id.verified_indicator);
|
||||
this.avatar = ViewUtil.findById(this, R.id.contact_photo_image);
|
||||
|
||||
ViewUtil.setTextViewGravityStart(this.title, getContext());
|
||||
@@ -61,7 +59,7 @@ public class ConversationTitleView extends RelativeLayout {
|
||||
} else if (recipient != null && recipient.isMuted()) {
|
||||
title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_volume_off_white_18dp, 0, 0, 0);
|
||||
} else*/ {
|
||||
title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
//title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (dcChat != null) {
|
||||
@@ -70,7 +68,11 @@ public class ConversationTitleView extends RelativeLayout {
|
||||
}
|
||||
|
||||
public void setVerified(boolean verified) {
|
||||
this.verified.setVisibility(verified ? View.VISIBLE : View.GONE);
|
||||
title.setCompoundDrawablesWithIntrinsicBounds(0, 0, verified ? R.drawable.ic_verified : 0, 0);
|
||||
}
|
||||
|
||||
public void hideAvatar() {
|
||||
avatar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
@@ -17,24 +14,16 @@ import android.widget.TextView;
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class ConversationUpdateItem extends LinearLayout
|
||||
implements RecipientModifiedListener, BindableConversationItem
|
||||
@@ -67,8 +56,6 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
this.title = findViewById(R.id.conversation_update_title);
|
||||
this.body = findViewById(R.id.conversation_update_body);
|
||||
this.date = findViewById(R.id.conversation_update_date);
|
||||
|
||||
this.setOnClickListener(new InternalClickListener(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,7 +84,7 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
|
||||
private void bind(@NonNull DcMsg messageRecord, @NonNull Locale locale) {
|
||||
this.messageRecord = messageRecord;
|
||||
this.sender = messageRecord.getIndividualRecipient();
|
||||
this.sender = Recipient.fromMsg(DcHelper.getContext(getContext()), messageRecord.getId());
|
||||
this.locale = locale;
|
||||
|
||||
this.sender.addListener(this);
|
||||
@@ -202,58 +189,10 @@ public class ConversationUpdateItem extends LinearLayout
|
||||
Util.runOnMain(() -> bind(messageRecord, locale));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnClickListener(View.OnClickListener l) {
|
||||
super.setOnClickListener(new InternalClickListener(l));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbind() {
|
||||
if (sender != null) {
|
||||
sender.removeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalClickListener implements View.OnClickListener {
|
||||
|
||||
@Nullable private final View.OnClickListener parent;
|
||||
|
||||
InternalClickListener(@Nullable View.OnClickListener parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if ((!messageRecord.isIdentityUpdate() &&
|
||||
!messageRecord.isIdentityDefault() &&
|
||||
!messageRecord.isIdentityVerified()) ||
|
||||
!batchSelected.isEmpty())
|
||||
{
|
||||
if (parent != null) parent.onClick(v);
|
||||
return;
|
||||
}
|
||||
|
||||
final Recipient sender = ConversationUpdateItem.this.sender;
|
||||
|
||||
IdentityUtil.getRemoteIdentityKey(getContext(), sender).addListener(new ListenableFuture.Listener<Optional<IdentityRecord>>() {
|
||||
@Override
|
||||
public void onSuccess(Optional<IdentityRecord> result) {
|
||||
if (result.isPresent()) {
|
||||
Intent intent = new Intent(getContext(), VerifyIdentityActivity.class);
|
||||
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, sender.getAddress());
|
||||
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(result.get().getIdentityKey()));
|
||||
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, result.get().getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);
|
||||
|
||||
getContext().startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ExecutionException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,12 +38,9 @@ import com.soundcloud.android.crop.Crop;
|
||||
|
||||
import org.thoughtcrime.securesms.components.InputAwareLayout;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
@@ -77,7 +74,7 @@ import javax.inject.Inject;
|
||||
import static android.provider.MediaStore.EXTRA_OUTPUT;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public class CreateProfileActivity extends BaseActionBarActivity implements InjectableType {
|
||||
public class CreateProfileActivity extends BaseActionBarActivity {
|
||||
|
||||
private static final String TAG = CreateProfileActivity.class.getSimpleName();
|
||||
|
||||
@@ -309,7 +306,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
|
||||
private void initializeProfileAvatar(boolean excludeSystem) {
|
||||
String address = DcHelper.get(this, DcHelper.CONFIG_ADDRESS);
|
||||
|
||||
if (AvatarHelper.getAvatarFile(this, address).exists() && AvatarHelper.getAvatarFile(this, address).length() > 0) {
|
||||
if (AvatarHelper.getSelfAvatarFile(this, address).exists() && AvatarHelper.getSelfAvatarFile(this, address).length() > 0) {
|
||||
new AsyncTask<Void, Void, byte[]>() {
|
||||
@Override
|
||||
protected byte[] doInBackground(Void... params) {
|
||||
@@ -452,14 +449,14 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
|
||||
TextSecurePreferences.setProfileName(context, name);
|
||||
|
||||
try {
|
||||
AvatarHelper.setAvatar(CreateProfileActivity.this, DcHelper.get(context, DcHelper.CONFIG_ADDRESS), avatarBytes);
|
||||
AvatarHelper.setSelfAvatar(CreateProfileActivity.this, DcHelper.get(context, DcHelper.CONFIG_ADDRESS), avatarBytes);
|
||||
TextSecurePreferences.setProfileAvatarId(CreateProfileActivity.this, new SecureRandom().nextInt());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceProfileKeyUpdateJob(context));
|
||||
// ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceProfileKeyUpdateJob(context));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -21,43 +21,20 @@ import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase.Reader;
|
||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.FileUtils;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.VersionTracker;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
private static final String TAG = DatabaseUpgradeActivity.class.getSimpleName();
|
||||
|
||||
public static final int NO_MORE_KEY_EXCHANGE_PREFIX_VERSION = 46;
|
||||
public static final int MMS_BODY_VERSION = 46;
|
||||
@@ -118,11 +95,6 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
Log.w("DatabaseUpgradeActivity", "Upgrading...");
|
||||
setContentView(R.layout.database_upgrade_activity);
|
||||
|
||||
ProgressBar indeterminateProgress = findViewById(R.id.indeterminate_progress);
|
||||
ProgressBar determinateProgress = findViewById(R.id.determinate_progress);
|
||||
|
||||
new DatabaseUpgradeTask(indeterminateProgress, determinateProgress)
|
||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, VersionTracker.getLastSeenVersion(this));
|
||||
} else {
|
||||
VersionTracker.updateLastSeenVersion(this);
|
||||
updateNotifications(this);
|
||||
@@ -175,204 +147,4 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
public void setProgress(int progress, int total);
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private class DatabaseUpgradeTask extends AsyncTask<Integer, Double, Void>
|
||||
implements DatabaseUpgradeListener
|
||||
{
|
||||
|
||||
private final ProgressBar indeterminateProgress;
|
||||
private final ProgressBar determinateProgress;
|
||||
|
||||
DatabaseUpgradeTask(ProgressBar indeterminateProgress, ProgressBar determinateProgress) {
|
||||
this.indeterminateProgress = indeterminateProgress;
|
||||
this.determinateProgress = determinateProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Integer... params) {
|
||||
Context context = DatabaseUpgradeActivity.this.getApplicationContext();
|
||||
|
||||
Log.w("DatabaseUpgradeActivity", "Running background upgrade..");
|
||||
DatabaseFactory.getInstance(DatabaseUpgradeActivity.this)
|
||||
.onApplicationLevelUpgrade(context, masterSecret, params[0], this);
|
||||
|
||||
if (params[0] < CURVE25519_VERSION) {
|
||||
IdentityKeyUtil.migrateIdentityKeys(context, masterSecret);
|
||||
}
|
||||
|
||||
if (params[0] < NO_V1_VERSION) {
|
||||
File v1sessions = new File(context.getFilesDir(), "sessions");
|
||||
|
||||
if (v1sessions.exists() && v1sessions.isDirectory()) {
|
||||
File[] contents = v1sessions.listFiles();
|
||||
|
||||
if (contents != null) {
|
||||
for (File session : contents) {
|
||||
session.delete();
|
||||
}
|
||||
}
|
||||
|
||||
v1sessions.delete();
|
||||
}
|
||||
}
|
||||
|
||||
if (params[0] < SIGNED_PREKEY_VERSION) {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new CreateSignedPreKeyJob(context));
|
||||
}
|
||||
|
||||
if (params[0] < NO_DECRYPT_QUEUE_VERSION) {
|
||||
scheduleMessagesInPushDatabase(context);
|
||||
}
|
||||
|
||||
if (params[0] < PUSH_DECRYPT_SERIAL_ID_VERSION) {
|
||||
scheduleMessagesInPushDatabase(context);
|
||||
}
|
||||
|
||||
if (params[0] < MIGRATE_SESSION_PLAINTEXT) {
|
||||
// new TextSecureSessionStore(context, masterSecret).migrateSessions();
|
||||
// new TextSecurePreKeyStore(context, masterSecret).migrateRecords();
|
||||
|
||||
IdentityKeyUtil.migrateIdentityKeys(context, masterSecret);
|
||||
scheduleMessagesInPushDatabase(context);;
|
||||
}
|
||||
|
||||
if (params[0] < CONTACTS_ACCOUNT_VERSION) {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(getApplicationContext(), false));
|
||||
}
|
||||
|
||||
if (params[0] < MEDIA_DOWNLOAD_CONTROLS_VERSION) {
|
||||
schedulePendingIncomingParts(context);
|
||||
}
|
||||
|
||||
if (params[0] < REDPHONE_SUPPORT_VERSION) {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new RefreshAttributesJob(getApplicationContext()));
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(getApplicationContext(), false));
|
||||
}
|
||||
|
||||
if (params[0] < PROFILES) {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(getApplicationContext(), false));
|
||||
}
|
||||
|
||||
if (params[0] < SCREENSHOTS) {
|
||||
boolean screenSecurity = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(TextSecurePreferences.SCREEN_SECURITY_PREF, true);
|
||||
TextSecurePreferences.setScreenSecurityEnabled(getApplicationContext(), screenSecurity);
|
||||
}
|
||||
|
||||
if (params[0] < PERSISTENT_BLOBS) {
|
||||
File externalDir = context.getExternalFilesDir(null);
|
||||
|
||||
if (externalDir != null && externalDir.isDirectory() && externalDir.exists()) {
|
||||
for (File blob : externalDir.listFiles()) {
|
||||
if (blob.exists() && blob.isFile()) blob.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params[0] < INTERNALIZE_CONTACTS) {
|
||||
if (TextSecurePreferences.isPushRegistered(getApplicationContext())) {
|
||||
TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(getApplicationContext(), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (params[0] < SQLCIPHER) {
|
||||
scheduleMessagesInPushDatabase(context);
|
||||
}
|
||||
|
||||
if (params[0] < SQLCIPHER_COMPLETE) {
|
||||
File file = context.getDatabasePath("messages.db");
|
||||
if (file != null && file.exists()) file.delete();
|
||||
}
|
||||
|
||||
if (params[0] < REMOVE_JOURNAL) {
|
||||
File file = context.getDatabasePath("messages.db-journal");
|
||||
if (file != null && file.exists()) file.delete();
|
||||
}
|
||||
|
||||
if (params[0] < REMOVE_CACHE) {
|
||||
try {
|
||||
FileUtils.deleteDirectoryContents(context.getCacheDir());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void schedulePendingIncomingParts(Context context) {
|
||||
final AttachmentDatabase attachmentDb = DatabaseFactory.getAttachmentDatabase(context);
|
||||
final MmsDatabase mmsDb = DatabaseFactory.getMmsDatabase(context);
|
||||
final List<DatabaseAttachment> pendingAttachments = DatabaseFactory.getAttachmentDatabase(context).getPendingAttachments();
|
||||
|
||||
Log.w(TAG, pendingAttachments.size() + " pending parts.");
|
||||
for (DatabaseAttachment attachment : pendingAttachments) {
|
||||
final Reader reader = mmsDb.readerFor(mmsDb.getMessage(attachment.getMmsId()));
|
||||
final MessageRecord record = reader.getNext();
|
||||
|
||||
if (attachment.hasData()) {
|
||||
Log.w(TAG, "corrected a pending media part " + attachment.getAttachmentId() + "that already had data.");
|
||||
attachmentDb.setTransferState(attachment.getMmsId(), attachment.getAttachmentId(), AttachmentDatabase.TRANSFER_PROGRESS_DONE);
|
||||
} else if (record != null && !record.isOutgoing() && record.isPush()) {
|
||||
Log.w(TAG, "queuing new attachment download job for incoming push part " + attachment.getAttachmentId() + ".");
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new AttachmentDownloadJob(context, attachment.getMmsId(), attachment.getAttachmentId(), false));
|
||||
}
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleMessagesInPushDatabase(Context context) {
|
||||
PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(context);
|
||||
Cursor pushReader = null;
|
||||
|
||||
try {
|
||||
pushReader = pushDatabase.getPending();
|
||||
|
||||
while (pushReader != null && pushReader.moveToNext()) {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new PushDecryptJob(getApplicationContext(),
|
||||
pushReader.getLong(pushReader.getColumnIndexOrThrow(PushDatabase.ID))));
|
||||
}
|
||||
} finally {
|
||||
if (pushReader != null)
|
||||
pushReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Double... update) {
|
||||
indeterminateProgress.setVisibility(View.GONE);
|
||||
determinateProgress.setVisibility(View.VISIBLE);
|
||||
|
||||
double scaler = update[0];
|
||||
determinateProgress.setProgress((int)Math.floor(determinateProgress.getMax() * scaler));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
VersionTracker.updateLastSeenVersion(DatabaseUpgradeActivity.this);
|
||||
updateNotifications(DatabaseUpgradeActivity.this);
|
||||
|
||||
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(int progress, int total) {
|
||||
publishProgress(((double)progress / (double)total));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,16 +25,20 @@ import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEventCenter;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.request.target.SimpleTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
@@ -47,8 +51,8 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
@@ -69,7 +73,7 @@ import java.util.Set;
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
implements OnRecipientDeletedListener
|
||||
implements OnRecipientDeletedListener, DcEventCenter.DcEventDelegate
|
||||
{
|
||||
|
||||
private final static String TAG = GroupCreateActivity.class.getSimpleName();
|
||||
@@ -86,14 +90,14 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
private ApplicationDcContext dcContext;
|
||||
|
||||
private boolean createVerified;
|
||||
private boolean verified;
|
||||
private EditText groupName;
|
||||
private ListView lv;
|
||||
private ImageView avatar;
|
||||
private TextView creatingText;
|
||||
private Bitmap avatarBmp;
|
||||
private CircularProgressButton verifyButton;
|
||||
private boolean editGroup;
|
||||
private Integer editGroupChatId = null;
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
@@ -106,11 +110,14 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
dcContext = DcHelper.getContext(this);
|
||||
setContentView(R.layout.group_create_activity);
|
||||
//noinspection ConstantConditions
|
||||
createVerified = getIntent().getBooleanExtra(GROUP_CREATE_VERIFIED_EXTRA, false);
|
||||
verified = getIntent().getBooleanExtra(GROUP_CREATE_VERIFIED_EXTRA, false);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
|
||||
initializeResources();
|
||||
initializeExistingGroup();
|
||||
initializeResources();
|
||||
|
||||
dcContext.eventCenter.addObserver(this, DcContext.DC_EVENT_CHAT_MODIFIED);
|
||||
dcContext.eventCenter.addObserver(this, DcContext.DC_EVENT_CONTACTS_CHANGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,16 +128,22 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
updateViewState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
dcContext.eventCenter.removeObservers(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void updateViewState() {
|
||||
avatar.setEnabled(true);
|
||||
groupName.setEnabled(true);
|
||||
|
||||
String title;
|
||||
if(editGroup) {
|
||||
if(isEdit()) {
|
||||
title = getString(R.string.GroupCreateActivity_actionbar_edit_title);
|
||||
}
|
||||
else if(createVerified) {
|
||||
else if(verified) {
|
||||
title = getString(R.string.GroupCreateActivity_actionbar_verified_title);
|
||||
}
|
||||
else {
|
||||
@@ -143,6 +156,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
new AddMembersTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipients);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(int eventId, Object data1, Object data2) {
|
||||
if (eventId== DcContext.DC_EVENT_CHAT_MODIFIED || eventId==DcContext.DC_EVENT_CONTACTS_CHANGED) {
|
||||
initializeExistingGroup();
|
||||
}
|
||||
}
|
||||
|
||||
private static class AddMembersTask extends AsyncTask<Recipient,Void,List<AddMembersTask.Result>> {
|
||||
static class Result {
|
||||
Optional<Recipient> recipient;
|
||||
@@ -173,8 +193,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
protected void onPostExecute(List<Result> results) {
|
||||
if (activity.isFinishing()) return;
|
||||
ApplicationDcContext dcContext = DcHelper.getContext(activity);
|
||||
|
||||
for (Result result : results) {
|
||||
Recipient recipient = result.recipient.get();
|
||||
Address address = recipient.getAddress();
|
||||
@@ -197,20 +215,38 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
lv.setAdapter(adapter);
|
||||
|
||||
findViewById(R.id.add_member_button).setOnClickListener(new AddRecipientButtonListener());
|
||||
if (createVerified) {
|
||||
if (verified) {
|
||||
verifyButton.setOnClickListener(new ShowQrButtonListener());
|
||||
verifyButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
|
||||
initializeAvatarView();
|
||||
}
|
||||
|
||||
private void initializeAvatarView() {
|
||||
boolean imageLoaded = false;
|
||||
if (editGroupChatId != null) {
|
||||
String avatarPath = dcContext.getChat(editGroupChatId).getProfileImage();
|
||||
File avatarFile = new File(avatarPath);
|
||||
if (avatarFile.exists()) {
|
||||
imageLoaded = true;
|
||||
GlideApp.with(this)
|
||||
.load(avatarFile)
|
||||
.circleCrop()
|
||||
.into(avatar);
|
||||
}
|
||||
}
|
||||
if (!imageLoaded) {
|
||||
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
|
||||
}
|
||||
avatar.setOnClickListener(view -> Crop.pickImage(GroupCreateActivity.this));
|
||||
}
|
||||
|
||||
private void initializeExistingGroup() {
|
||||
editGroup = true;
|
||||
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
|
||||
if (groupAddress != null) {
|
||||
int chatId = groupAddress.getDcChatId();
|
||||
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, chatId);
|
||||
editGroupChatId = groupAddress.getDcChatId();
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, editGroupChatId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +268,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
finish();
|
||||
return true;
|
||||
case R.id.menu_create_group:
|
||||
if (editGroup) handleGroupUpdate();
|
||||
if (isEdit()) handleGroupUpdate();
|
||||
else handleGroupCreate();
|
||||
return true;
|
||||
}
|
||||
@@ -248,47 +284,77 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
private void handleGroupCreate() {
|
||||
String groupName = getGroupName();
|
||||
if(groupName==null) {
|
||||
Toast.makeText(this, getString(R.string.GroupCreateActivity_please_enter_group_name), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
if (showGroupNameEmptyToast(groupName)) return;
|
||||
|
||||
int chatId = dcContext.createGroupChat(createVerified, groupName);
|
||||
int chatId = dcContext.createGroupChat(verified, groupName);
|
||||
|
||||
Set<Recipient> members = getAdapter().getRecipients();
|
||||
for(Recipient member : members) {
|
||||
Address address = member.getAddress();
|
||||
Set<Recipient> recipients = getAdapter().getRecipients();
|
||||
for(Recipient recipient : recipients) {
|
||||
Address address = recipient.getAddress();
|
||||
if(address.isDcContact()) {
|
||||
int contactId = address.getDcContactId();
|
||||
dcContext.addContactToChat(chatId, contactId);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle avatarBmp
|
||||
AvatarHelper.setGroupAvatar(this, chatId, avatarBmp);
|
||||
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)chatId);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, Address.fromChat(chatId));
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, chatId);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleGroupUpdate() {
|
||||
int chatId = 0;// TODO: get correct id from groupToUpdate.get().id; or so
|
||||
private boolean showGroupNameEmptyToast(String groupName) {
|
||||
if(groupName == null) {
|
||||
Toast.makeText(this, getString(R.string.GroupCreateActivity_please_enter_group_name), Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleGroupUpdate() {
|
||||
if(editGroupChatId == null) {
|
||||
return;
|
||||
}
|
||||
String groupName = getGroupName();
|
||||
if(groupName!=null) {
|
||||
dcContext.setChatName(chatId, groupName);
|
||||
if (showGroupNameEmptyToast(groupName)) {
|
||||
return;
|
||||
}
|
||||
dcContext.setChatName(editGroupChatId, groupName);
|
||||
updateGroupParticipants();
|
||||
|
||||
AvatarHelper.setGroupAvatar(this, editGroupChatId, avatarBmp);
|
||||
|
||||
Intent intent = new Intent();
|
||||
Recipient recipient = dcContext.getRecipient(ApplicationDcContext.RECIPIENT_TYPE_CHAT, editGroupChatId);
|
||||
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipient.getAddress());
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void updateGroupParticipants() {
|
||||
SparseBooleanArray currentChatContactIds = new SparseBooleanArray();
|
||||
for(int chatContactId : dcContext.getChatContacts(editGroupChatId)) {
|
||||
currentChatContactIds.put(chatContactId, false);
|
||||
}
|
||||
|
||||
// TODO: compare dcContext.getChatContacts(chatId); against getAdapter().getRecipients();
|
||||
// and add/remove contacts
|
||||
|
||||
|
||||
// TODO: handle avatarBmp
|
||||
|
||||
finish();
|
||||
Set<Recipient> recipients = getAdapter().getRecipients();
|
||||
for(Recipient recipient : recipients) {
|
||||
Address address = recipient.getAddress();
|
||||
if(address.isDcContact()) {
|
||||
int contactId = address.getDcContactId();
|
||||
if(currentChatContactIds.indexOfKey(contactId) < 0) {
|
||||
dcContext.addContactToChat(editGroupChatId, contactId);
|
||||
} else {
|
||||
currentChatContactIds.put(contactId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int index = 0; index < currentChatContactIds.size(); index++) {
|
||||
if (!currentChatContactIds.valueAt(index)) {
|
||||
dcContext.removeContactFromChat(editGroupChatId, currentChatContactIds.keyAt(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SelectedRecipientsAdapter getAdapter() {
|
||||
@@ -330,28 +396,33 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
new Crop(data.getData()).output(outputFile).asSquare().start(this);
|
||||
break;
|
||||
case Crop.REQUEST_CROP:
|
||||
GlideApp.with(this)
|
||||
.asBitmap()
|
||||
.load(Crop.getOutput(data))
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.centerCrop()
|
||||
.override(AVATAR_SIZE, AVATAR_SIZE)
|
||||
.into(new SimpleTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
|
||||
setAvatar(Crop.getOutput(data), resource);
|
||||
}
|
||||
});
|
||||
setAvatarView(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void setAvatarView(Intent data) {
|
||||
final Uri output = Crop.getOutput(data);
|
||||
GlideApp.with(this)
|
||||
.asBitmap()
|
||||
.load(output)
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.centerCrop()
|
||||
.override(AVATAR_SIZE, AVATAR_SIZE)
|
||||
.into(new SimpleTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
|
||||
setAvatar(output, resource);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class AddRecipientButtonListener implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(GroupCreateActivity.this, ContactMultiSelectionActivity.class);
|
||||
intent.putExtra(ContactSelectionListFragment.SELECT_VERIFIED_EXTRA, createVerified);
|
||||
if (editGroup) {
|
||||
intent.putExtra(ContactSelectionListFragment.SELECT_VERIFIED_EXTRA, verified);
|
||||
if (isEdit()) {
|
||||
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH);
|
||||
} else {
|
||||
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH | DisplayMode.FLAG_SMS);
|
||||
@@ -365,6 +436,9 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent qrIntent = new Intent(GroupCreateActivity.this, QrShowActivity.class);
|
||||
if (isEdit()) {
|
||||
qrIntent.putExtra(QrShowActivity.CHAT_ID, editGroupChatId);
|
||||
}
|
||||
startActivity(qrIntent);
|
||||
}
|
||||
}
|
||||
@@ -383,20 +457,22 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
protected Recipient doInBackground(Integer... recipientIds) {
|
||||
Integer recipientsId = recipientIds[0];
|
||||
Recipient recipient = activity.dcContext.getRecipient(ApplicationDcContext.RECIPIENT_TYPE_CHAT, recipientsId);
|
||||
return recipient;
|
||||
return activity.dcContext.getRecipient(ApplicationDcContext.RECIPIENT_TYPE_CHAT, recipientsId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Recipient recipient) {
|
||||
super.onPostExecute(recipient);
|
||||
activity.fllExistingGroup(recipient);
|
||||
activity.fillExistingGroup(recipient);
|
||||
}
|
||||
}
|
||||
|
||||
private void fllExistingGroup(Recipient recipient) {
|
||||
private void fillExistingGroup(Recipient recipient) {
|
||||
List<Recipient> participants = recipient.getParticipants();
|
||||
Recipient[] participantsArray = new Recipient[participants.size()];
|
||||
participantsArray = participants.toArray(participantsArray);
|
||||
if (!isFinishing()) {
|
||||
addSelectedContacts(participantsArray);
|
||||
groupName.setText(recipient.getName());
|
||||
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this, participants);
|
||||
adapter.setOnRecipientDeletedListener(this);
|
||||
@@ -415,19 +491,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
.into(avatar);
|
||||
}
|
||||
|
||||
private static class GroupData {
|
||||
String id;
|
||||
Set<Recipient> recipients;
|
||||
Bitmap avatarBmp;
|
||||
byte[] avatarBytes;
|
||||
String name;
|
||||
|
||||
public GroupData(String id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) {
|
||||
this.id = id;
|
||||
this.recipients = recipients;
|
||||
this.avatarBmp = avatarBmp;
|
||||
this.avatarBytes = avatarBytes;
|
||||
this.name = name;
|
||||
}
|
||||
private boolean isEdit() {
|
||||
return editGroupChatId != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||
|
||||
private static final String TAG = GroupMembersDialog.class.getSimpleName();
|
||||
|
||||
private final Recipient recipient;
|
||||
private final Context context;
|
||||
|
||||
public GroupMembersDialog(Context context, Recipient recipient) {
|
||||
this.recipient = recipient;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreExecute() {}
|
||||
|
||||
@Override
|
||||
protected List<Recipient> doInBackground(Void... params) {
|
||||
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostExecute(List<Recipient> members) {
|
||||
GroupMembers groupMembers = new GroupMembers(members);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(R.string.ConversationActivity_group_members);
|
||||
builder.setIconAttribute(R.attr.group_members_dialog_icon);
|
||||
builder.setCancelable(true);
|
||||
builder.setItems(groupMembers.getRecipientStrings(), new GroupMembersOnClickListener(context, groupMembers));
|
||||
builder.setPositiveButton(android.R.string.ok, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
public void display() {
|
||||
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private static class GroupMembersOnClickListener implements DialogInterface.OnClickListener {
|
||||
private final GroupMembers groupMembers;
|
||||
private final Context context;
|
||||
|
||||
public GroupMembersOnClickListener(Context context, GroupMembers members) {
|
||||
this.context = context;
|
||||
this.groupMembers = members;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int item) {
|
||||
Recipient recipient = groupMembers.get(item);
|
||||
|
||||
if (recipient.getContactUri() != null) {
|
||||
Intent intent = new Intent(context, RecipientPreferenceActivity.class);
|
||||
intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
|
||||
if (recipient.getAddress().isEmail()) {
|
||||
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString());
|
||||
} else {
|
||||
intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString());
|
||||
}
|
||||
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a List of Recipient (just like @class Recipients),
|
||||
* but with focus on the order of the Recipients.
|
||||
* So that the order of the RecipientStrings[] matches
|
||||
* the internal order.
|
||||
*
|
||||
* @author Christoph Haefner
|
||||
*/
|
||||
private class GroupMembers {
|
||||
private final String TAG = GroupMembers.class.getSimpleName();
|
||||
|
||||
private final LinkedList<Recipient> members = new LinkedList<>();
|
||||
|
||||
public GroupMembers(List<Recipient> recipients) {
|
||||
for (Recipient recipient : recipients) {
|
||||
if (isLocalNumber(recipient)) {
|
||||
members.push(recipient);
|
||||
} else {
|
||||
members.add(recipient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getRecipientStrings() {
|
||||
List<String> recipientStrings = new LinkedList<>();
|
||||
|
||||
for (Recipient recipient : members) {
|
||||
if (isLocalNumber(recipient)) {
|
||||
recipientStrings.add(context.getString(R.string.GroupMembersDialog_me));
|
||||
} else {
|
||||
String name = recipient.toShortString();
|
||||
|
||||
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
|
||||
name += " ~" + recipient.getProfileName();
|
||||
}
|
||||
|
||||
recipientStrings.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return recipientStrings.toArray(new String[members.size()]);
|
||||
}
|
||||
|
||||
public Recipient get(int index) {
|
||||
return members.get(index);
|
||||
}
|
||||
|
||||
private boolean isLocalNumber(Recipient recipient) {
|
||||
return Util.isOwnNumber(context, recipient.getAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,25 +19,13 @@ import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class InviteActivity extends PassphraseRequiredActionBarActivity {
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
|
||||
|
||||
import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
@@ -46,7 +46,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
private final GlideRequests glideRequests;
|
||||
private final Locale locale;
|
||||
private final ItemClickListener itemClickListener;
|
||||
private final Set<MediaRecord> selected;
|
||||
private final Set<DcMsg> selected;
|
||||
|
||||
private BucketedThreadMedia media;
|
||||
|
||||
@@ -105,10 +105,10 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
|
||||
@Override
|
||||
public void onBindItemViewHolder(ItemViewHolder viewHolder, int section, int offset) {
|
||||
MediaRecord mediaRecord = media.get(section, offset);
|
||||
DcMsg mediaRecord = media.get(section, offset);
|
||||
ThumbnailView thumbnailView = ((ViewHolder)viewHolder).imageView;
|
||||
View selectedIndicator = ((ViewHolder)viewHolder).selectedIndicator;
|
||||
Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment());
|
||||
Slide slide = MediaUtil.getSlideForMsg(context, mediaRecord);
|
||||
|
||||
if (slide != null) {
|
||||
thumbnailView.setImageResource(glideRequests, slide, false, false);
|
||||
@@ -133,7 +133,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
return media.getSectionItemCount(section);
|
||||
}
|
||||
|
||||
public void toggleSelection(@NonNull MediaRecord mediaRecord) {
|
||||
public void toggleSelection(@NonNull DcMsg mediaRecord) {
|
||||
if (!selected.remove(mediaRecord)) {
|
||||
selected.add(mediaRecord);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Collection<MediaRecord> getSelectedMedia() {
|
||||
public Collection<DcMsg> getSelectedMedia() {
|
||||
return new HashSet<>(selected);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
}
|
||||
|
||||
interface ItemClickListener {
|
||||
void onMediaClicked(@NonNull MediaRecord mediaRecord);
|
||||
void onMediaLongClicked(MediaRecord mediaRecord);
|
||||
void onMediaClicked(@NonNull DcMsg mediaRecord);
|
||||
void onMediaLongClicked(DcMsg mediaRecord);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,17 +47,18 @@ import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader;
|
||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
|
||||
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
@@ -257,7 +258,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaClicked(@NonNull MediaDatabase.MediaRecord mediaRecord) {
|
||||
public void onMediaClicked(@NonNull DcMsg mediaRecord) {
|
||||
if (actionMode != null) {
|
||||
handleMediaMultiSelectClick(mediaRecord);
|
||||
} else {
|
||||
@@ -265,7 +266,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMediaMultiSelectClick(@NonNull MediaDatabase.MediaRecord mediaRecord) {
|
||||
private void handleMediaMultiSelectClick(@NonNull DcMsg mediaRecord) {
|
||||
MediaGalleryAdapter adapter = getListAdapter();
|
||||
|
||||
adapter.toggleSelection(mediaRecord);
|
||||
@@ -277,8 +278,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMediaPreviewClick(@NonNull MediaDatabase.MediaRecord mediaRecord) {
|
||||
if (mediaRecord.getAttachment().getDataUri() == null) {
|
||||
private void handleMediaPreviewClick(@NonNull DcMsg mediaRecord) {
|
||||
if (mediaRecord.getFile() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -288,18 +289,15 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, MediaPreviewActivity.class);
|
||||
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
|
||||
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
|
||||
intent.putExtra(MediaPreviewActivity.DC_MSG_ID, mediaRecord.getId());
|
||||
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing());
|
||||
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, true);
|
||||
|
||||
intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType());
|
||||
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaLongClicked(MediaDatabase.MediaRecord mediaRecord) {
|
||||
public void onMediaLongClicked(DcMsg mediaRecord) {
|
||||
if (actionMode == null) {
|
||||
((MediaGalleryAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord);
|
||||
recyclerView.getAdapter().notifyDataSetChanged();
|
||||
@@ -309,7 +307,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void handleDeleteMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) {
|
||||
private void handleDeleteMedia(@NonNull Collection<DcMsg> mediaRecords) {
|
||||
int recordCount = mediaRecords.size();
|
||||
Resources res = getContext().getResources();
|
||||
String confirmTitle = res.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_title,
|
||||
@@ -324,25 +322,26 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
|
||||
builder.setTitle(confirmTitle);
|
||||
builder.setMessage(confirmMessage);
|
||||
builder.setCancelable(true);
|
||||
final DcContext dcContext = DcHelper.getContext(getContext());
|
||||
|
||||
builder.setPositiveButton(R.string.delete, (dialogInterface, i) -> {
|
||||
new ProgressDialogAsyncTask<MediaDatabase.MediaRecord, Void, Void>(getContext(),
|
||||
new ProgressDialogAsyncTask<DcMsg, Void, Void>(getContext(),
|
||||
R.string.MediaOverviewActivity_Media_delete_progress_title,
|
||||
R.string.MediaOverviewActivity_Media_delete_progress_message)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground(MediaDatabase.MediaRecord... records) {
|
||||
protected Void doInBackground(DcMsg... records) {
|
||||
if (records == null || records.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (MediaDatabase.MediaRecord record : records) {
|
||||
AttachmentUtil.deleteAttachment(getContext(), record.getAttachment());
|
||||
for (DcMsg record : records) {
|
||||
dcContext.deleteMsgs(new int[]{record.getId()});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}.execute(mediaRecords.toArray(new MediaDatabase.MediaRecord[mediaRecords.size()]));
|
||||
}.execute(mediaRecords.toArray(new DcMsg[mediaRecords.size()]));
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.show();
|
||||
|
||||
@@ -20,9 +20,7 @@ import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build.VERSION;
|
||||
@@ -32,7 +30,6 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
@@ -48,21 +45,20 @@ import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcMediaGalleryElement;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
|
||||
import org.thoughtcrime.securesms.components.MediaView;
|
||||
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
||||
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
@@ -75,22 +71,29 @@ import java.util.WeakHashMap;
|
||||
/**
|
||||
* Activity for displaying media attachments in-app
|
||||
*/
|
||||
public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, LoaderManager.LoaderCallbacks<Pair<Cursor, Integer>> {
|
||||
public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
|
||||
implements RecipientModifiedListener, LoaderManager.LoaderCallbacks<DcMediaGalleryElement> {
|
||||
|
||||
private final static String TAG = MediaPreviewActivity.class.getSimpleName();
|
||||
|
||||
public static final String ADDRESS_EXTRA = "address";
|
||||
public static final String DATE_EXTRA = "date";
|
||||
public static final String SIZE_EXTRA = "size";
|
||||
public static final String OUTGOING_EXTRA = "outgoing";
|
||||
public static final String LEFT_IS_RECENT_EXTRA = "left_is_recent";
|
||||
public static final String DC_MSG_ID = "dc_msg_id";
|
||||
|
||||
/** USE ONLY IF YOU HAVE NO MESSAGE ID! */
|
||||
public static final String DATE_EXTRA = "date";
|
||||
|
||||
/** USE ONLY IF YOU HAVE NO MESSAGE ID! */
|
||||
public static final String SIZE_EXTRA = "size";
|
||||
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
@Nullable
|
||||
private DcMsg messageRecord;
|
||||
private DcContext dcContext;
|
||||
private MediaItem initialMedia;
|
||||
private ViewPager mediaPager;
|
||||
private Uri initialMediaUri;
|
||||
private String initialMediaType;
|
||||
private long initialMediaSize;
|
||||
private Recipient conversationRecipient;
|
||||
private boolean leftIsRecent;
|
||||
|
||||
@@ -181,32 +184,51 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
private void initializeResources() {
|
||||
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
|
||||
|
||||
initialMediaUri = getIntent().getData();
|
||||
initialMediaType = getIntent().getType();
|
||||
initialMediaSize = getIntent().getLongExtra(SIZE_EXTRA, 0);
|
||||
final Context context = getApplicationContext();
|
||||
this.dcContext = DcHelper.getContext(context);
|
||||
final int msgId = getIntent().getIntExtra(DC_MSG_ID, DcMsg.DC_MSG_NO_ID);
|
||||
|
||||
if(msgId == DcMsg.DC_MSG_NO_ID) {
|
||||
messageRecord = null;
|
||||
long date = getIntent().getLongExtra(DATE_EXTRA, 0);
|
||||
long size = getIntent().getLongExtra(SIZE_EXTRA, 0);
|
||||
initialMedia = new MediaItem(null, getIntent().getData(), getIntent().getType(),
|
||||
DcMsg.DC_MSG_NO_ID, date, size, false);
|
||||
|
||||
if (address != null) {
|
||||
conversationRecipient = Recipient.from(context, address, false);
|
||||
} else {
|
||||
conversationRecipient = null;
|
||||
}
|
||||
} else {
|
||||
messageRecord = dcContext.getMsg(msgId);
|
||||
initialMedia = new MediaItem(Recipient.fromChat(context, msgId), Uri.fromFile(messageRecord.getFileAsFile()),
|
||||
messageRecord.getFilemime(), messageRecord.getId(), messageRecord.getDateReceived(),
|
||||
messageRecord.getFilebytes(), messageRecord.isOutgoing());
|
||||
conversationRecipient = Recipient.fromChat(context, msgId);
|
||||
}
|
||||
leftIsRecent = getIntent().getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
|
||||
restartItem = -1;
|
||||
|
||||
if (address != null) {
|
||||
conversationRecipient = Recipient.from(this, address, true);
|
||||
} else {
|
||||
conversationRecipient = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeMedia() {
|
||||
if (!isContentTypeSupported(initialMediaType)) {
|
||||
|
||||
// if you search for the place where the media are loaded, go to 'onCreateLoader'.
|
||||
|
||||
if (!isContentTypeSupported(initialMedia.type)) {
|
||||
Log.w(TAG, "Unsupported media type sent to MediaPreviewActivity, finishing.");
|
||||
Toast.makeText(getApplicationContext(), R.string.MediaPreviewActivity_unssuported_media_type, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
Log.w(TAG, "Loading Part URI: " + initialMediaUri);
|
||||
Log.w(TAG, "Loading Part URI: " + initialMedia);
|
||||
|
||||
if (conversationRecipient != null) {
|
||||
if (messageRecord != null) {
|
||||
getSupportLoaderManager().restartLoader(0, null, this);
|
||||
} else {
|
||||
mediaPager.setAdapter(new SingleItemPagerAdapter(this, GlideApp.with(this), getWindow(), initialMediaUri, initialMediaType, initialMediaSize));
|
||||
mediaPager.setAdapter(new SingleItemPagerAdapter(this, GlideApp.with(this),
|
||||
getWindow(), initialMedia.uri, initialMedia.type, initialMedia.size));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +283,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void deleteMedia() {
|
||||
MediaItem mediaItem = getCurrentMediaItem();
|
||||
if (mediaItem == null || mediaItem.attachment == null) {
|
||||
if (mediaItem == null || mediaItem.msgId == DcMsg.DC_MSG_NO_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,11 +297,10 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
if (mediaItem.attachment == null) {
|
||||
if (mediaItem.msgId == DcMsg.DC_MSG_NO_ID) {
|
||||
return null;
|
||||
}
|
||||
AttachmentUtil.deleteAttachment(MediaPreviewActivity.this.getApplicationContext(),
|
||||
mediaItem.attachment);
|
||||
dcContext.deleteMsgs(new int[]{mediaItem.msgId});
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
@@ -340,25 +361,26 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Pair<Cursor, Integer>> onCreateLoader(int id, Bundle args) {
|
||||
return new PagingMediaLoader(this, conversationRecipient, initialMediaUri, leftIsRecent);
|
||||
public Loader<DcMediaGalleryElement> onCreateLoader(int id, Bundle args) {
|
||||
return new PagingMediaLoader(this, messageRecord, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Pair<Cursor, Integer>> loader, @Nullable Pair<Cursor, Integer> data) {
|
||||
public void onLoadFinished(Loader<DcMediaGalleryElement> loader, @Nullable DcMediaGalleryElement data) {
|
||||
if (data != null) {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
CursorPagerAdapter adapter = new CursorPagerAdapter(this, GlideApp.with(this), getWindow(), data.first, data.second, leftIsRecent);
|
||||
DcMediaPagerAdapter adapter = new DcMediaPagerAdapter(this, GlideApp.with(this),
|
||||
getWindow(), data, leftIsRecent);
|
||||
mediaPager.setAdapter(adapter);
|
||||
adapter.setActive(true);
|
||||
|
||||
if (restartItem < 0) mediaPager.setCurrentItem(data.second);
|
||||
if (restartItem < 0) mediaPager.setCurrentItem(data.getPosition());
|
||||
else mediaPager.setCurrentItem(restartItem);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Pair<Cursor, Integer>> loader) {
|
||||
public void onLoaderReset(Loader<DcMediaGalleryElement> loader) {
|
||||
|
||||
}
|
||||
|
||||
@@ -450,7 +472,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
|
||||
@Override
|
||||
public MediaItem getMediaItemFor(int position) {
|
||||
return new MediaItem(null, null, uri, mediaType, -1, true);
|
||||
return new MediaItem(null, uri, mediaType, DcMsg.DC_MSG_NO_ID, -1, -1, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -459,29 +481,29 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
}
|
||||
}
|
||||
|
||||
private static class CursorPagerAdapter extends PagerAdapter implements MediaItemAdapter {
|
||||
private static class DcMediaPagerAdapter extends PagerAdapter implements MediaItemAdapter {
|
||||
|
||||
private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>();
|
||||
|
||||
private final Context context;
|
||||
private final GlideRequests glideRequests;
|
||||
private final Window window;
|
||||
private final Cursor cursor;
|
||||
private final DcMediaGalleryElement gallery;
|
||||
private final boolean leftIsRecent;
|
||||
|
||||
private boolean active;
|
||||
private int autoPlayPosition;
|
||||
|
||||
CursorPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
|
||||
@NonNull Window window, @NonNull Cursor cursor, int autoPlayPosition,
|
||||
DcMediaPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
|
||||
@NonNull Window window, @NonNull DcMediaGalleryElement gallery,
|
||||
boolean leftIsRecent)
|
||||
{
|
||||
this.context = context.getApplicationContext();
|
||||
this.glideRequests = glideRequests;
|
||||
this.window = window;
|
||||
this.cursor = cursor;
|
||||
this.autoPlayPosition = autoPlayPosition;
|
||||
this.gallery = gallery;
|
||||
this.leftIsRecent = leftIsRecent;
|
||||
this.autoPlayPosition = gallery.getPosition();
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
@@ -492,7 +514,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (!active) return 0;
|
||||
else return cursor.getCount();
|
||||
else return gallery.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -509,13 +531,14 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
|
||||
autoPlayPosition = -1;
|
||||
|
||||
cursor.moveToPosition(cursorPosition);
|
||||
gallery.moveToPosition(cursorPosition);
|
||||
|
||||
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
|
||||
DcMsg msg = gallery.getMessage();
|
||||
|
||||
try {
|
||||
//noinspection ConstantConditions
|
||||
mediaView.set(glideRequests, window, mediaRecord.getAttachment().getDataUri(), mediaRecord.getAttachment().getContentType(), mediaRecord.getAttachment().getSize(), autoplay);
|
||||
mediaView.set(glideRequests, window, Uri.fromFile(msg.getFileAsFile()),
|
||||
msg.getFilemime(), msg.getFilebytes(), autoplay);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
@@ -536,18 +559,18 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
}
|
||||
|
||||
public MediaItem getMediaItemFor(int position) {
|
||||
cursor.moveToPosition(getCursorPosition(position));
|
||||
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
|
||||
Address address = mediaRecord.getAddress();
|
||||
gallery.moveToPosition(getCursorPosition(position));
|
||||
DcMsg msg = gallery.getMessage();
|
||||
|
||||
if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError();
|
||||
if (msg.getFile() == null) throw new AssertionError();
|
||||
|
||||
return new MediaItem(address != null ? Recipient.from(context, address,true) : null,
|
||||
mediaRecord.getAttachment(),
|
||||
mediaRecord.getAttachment().getDataUri(),
|
||||
mediaRecord.getContentType(),
|
||||
mediaRecord.getDate(),
|
||||
mediaRecord.isOutgoing());
|
||||
return new MediaItem(Recipient.fromChat(context, msg.getId()),
|
||||
Uri.fromFile(msg.getFileAsFile()),
|
||||
msg.getFilemime(),
|
||||
msg.getId(),
|
||||
msg.getDateReceived(),
|
||||
msg.getFilebytes(),
|
||||
msg.isOutgoing());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -558,30 +581,33 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
|
||||
private int getCursorPosition(int position) {
|
||||
if (leftIsRecent) return position;
|
||||
else return cursor.getCount() - 1 - position;
|
||||
else return gallery.getCount() - 1 - position;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MediaItem {
|
||||
private final @Nullable Recipient recipient;
|
||||
private final @Nullable DatabaseAttachment attachment;
|
||||
private final @NonNull Uri uri;
|
||||
private final @NonNull String type;
|
||||
private final int msgId;
|
||||
private final long date;
|
||||
private final long size;
|
||||
private final boolean outgoing;
|
||||
|
||||
private MediaItem(@Nullable Recipient recipient,
|
||||
@Nullable DatabaseAttachment attachment,
|
||||
@NonNull Uri uri,
|
||||
@NonNull String type,
|
||||
int msgId,
|
||||
long date,
|
||||
long size,
|
||||
boolean outgoing)
|
||||
{
|
||||
this.recipient = recipient;
|
||||
this.attachment = attachment;
|
||||
this.uri = uri;
|
||||
this.type = type;
|
||||
this.msgId = msgId;
|
||||
this.date = date;
|
||||
this.size = size;
|
||||
this.outgoing = outgoing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,12 +98,10 @@ public class NewConversationActivity extends ContactSelectionActivity {
|
||||
final DcContext dcContext = DcHelper.getContext(this);
|
||||
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, Address.fromChat(chatId));
|
||||
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA));
|
||||
intent.setDataAndType(getIntent().getData(), getIntent().getType());
|
||||
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, (long)chatId);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, chatId);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@@ -12,10 +12,7 @@ import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -27,14 +24,12 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
private static final int STATE_NORMAL = 0;
|
||||
private static final int STATE_NEEDS_CONFIGURE = 1;
|
||||
|
||||
private SignalServiceNetworkAccess networkAccess;
|
||||
private BroadcastReceiver clearKeyReceiver;
|
||||
private boolean isVisible;
|
||||
|
||||
@Override
|
||||
protected final void onCreate(Bundle savedInstanceState) {
|
||||
Log.w(TAG, "onCreate(" + savedInstanceState + ")");
|
||||
this.networkAccess = new SignalServiceNetworkAccess(this);
|
||||
onPreCreate();
|
||||
|
||||
final boolean locked = KeyCachingService.isLocked(this);
|
||||
@@ -57,9 +52,6 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
super.onResume();
|
||||
KeyCachingService.registerPassphraseActivityStarted(this);
|
||||
|
||||
if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStarted(this);
|
||||
else ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this));
|
||||
|
||||
isVisible = true;
|
||||
}
|
||||
|
||||
@@ -69,8 +61,6 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
super.onPause();
|
||||
KeyCachingService.registerPassphraseActivityStopped(this);
|
||||
|
||||
if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStopped(this);
|
||||
|
||||
isVisible = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ public class QrShowActivity extends AppCompatActivity implements DcEventCenter.D
|
||||
public final static int BLACK = 0xFF000000;
|
||||
public final static int WIDTH = 400;
|
||||
public final static int HEIGHT = 400;
|
||||
public final static String CHAT_ID = "chat_id";
|
||||
|
||||
public int numJoiners;
|
||||
|
||||
@@ -50,7 +51,7 @@ public class QrShowActivity extends AppCompatActivity implements DcEventCenter.D
|
||||
Bundle extras = getIntent().getExtras();
|
||||
int chatId = 0;
|
||||
if (extras != null) {
|
||||
chatId = extras.getInt("chat_id");
|
||||
chatId = extras.getInt(CHAT_ID);
|
||||
}
|
||||
|
||||
ActionBar supportActionBar = getSupportActionBar();
|
||||
@@ -91,13 +92,13 @@ public class QrShowActivity extends AppCompatActivity implements DcEventCenter.D
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
dcEventCenter.addObserver(this, DcContext.DC_EVENT_SECUREJOIN_INVITER_PROGRESS);
|
||||
dcEventCenter.addObserver(DcContext.DC_EVENT_SECUREJOIN_INVITER_PROGRESS, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
dcEventCenter.removeObserver(this, DcContext.DC_EVENT_SECUREJOIN_INVITER_PROGRESS);
|
||||
dcEventCenter.removeObservers(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,16 +41,12 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||
import org.thoughtcrime.securesms.color.MaterialColors;
|
||||
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
@@ -64,7 +60,6 @@ import org.thoughtcrime.securesms.util.Dialogs;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
@@ -167,8 +162,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
Intent intent = new Intent(RecipientPreferenceActivity.this, MediaPreviewActivity.class);
|
||||
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, address);
|
||||
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing());
|
||||
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
|
||||
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
|
||||
Log.e(TAG, "MEDIA PREVIEW NOT IMPLEMENTED HERE must have dcMsgId in Media Record first!");
|
||||
if(true)
|
||||
throw new IllegalStateException("Media Preview not implemented");
|
||||
/* intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
|
||||
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());*/
|
||||
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, ViewCompat.getLayoutDirection(threadPhotoRailView) == ViewCompat.LAYOUT_DIRECTION_LTR);
|
||||
intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType());
|
||||
startActivity(intent);
|
||||
@@ -192,13 +190,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
}
|
||||
|
||||
private void setHeader(@NonNull Recipient recipient) {
|
||||
glideRequests.load(recipient.getContactPhoto())
|
||||
glideRequests.load(recipient.getContactPhoto(this))
|
||||
.fallback(recipient.getFallbackContactPhoto().asCallCard(this))
|
||||
.error(recipient.getFallbackContactPhoto().asCallCard(this))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.into(this.avatar);
|
||||
|
||||
if (recipient.getContactPhoto() == null) this.avatar.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
if (recipient.getContactPhoto(this) == null) this.avatar.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
else this.avatar.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
|
||||
this.avatar.setBackgroundColor(recipient.getColor().toActionBarColor(this));
|
||||
@@ -366,25 +364,26 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
if (recipient.isBlocked()) blockPreference.setTitle(R.string.RecipientPreferenceActivity_unblock);
|
||||
else blockPreference.setTitle(R.string.RecipientPreferenceActivity_block);
|
||||
|
||||
IdentityUtil.getRemoteIdentityKey(getActivity(), recipient).addListener(new ListenableFuture.Listener<Optional<IdentityRecord>>() {
|
||||
@Override
|
||||
public void onSuccess(Optional<IdentityRecord> result) {
|
||||
if (result.isPresent()) {
|
||||
if (identityPreference != null) identityPreference.setOnPreferenceClickListener(new IdentityClickedListener(result.get()));
|
||||
if (identityPreference != null) identityPreference.setEnabled(true);
|
||||
} else if (canHaveSafetyNumber) {
|
||||
if (identityPreference != null) identityPreference.setSummary(R.string.RecipientPreferenceActivity_available_once_a_message_has_been_sent_or_received);
|
||||
if (identityPreference != null) identityPreference.setEnabled(false);
|
||||
} else {
|
||||
if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ExecutionException e) {
|
||||
if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference);
|
||||
}
|
||||
});
|
||||
// IdentityUtil.getRemoteIdentityKey(getActivity(), recipient).addListener(new ListenableFuture.Listener<Optional<IdentityRecord>>() {
|
||||
// @Override
|
||||
// public void onSuccess(Optional<IdentityRecord> result) {
|
||||
// if (result.isPresent()) {
|
||||
// if (identityPreference != null) identityPreference.setOnPreferenceClickListener(new IdentityClickedListener(result.get()));
|
||||
// if (identityPreference != null) identityPreference.setEnabled(true);
|
||||
// } else if (canHaveSafetyNumber) {
|
||||
// if (identityPreference != null) identityPreference.setSummary(R.string.RecipientPreferenceActivity_available_once_a_message_has_been_sent_or_received);
|
||||
// if (identityPreference != null) identityPreference.setEnabled(false);
|
||||
// } else {
|
||||
// if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFailure(ExecutionException e) {
|
||||
// if (identityPreference != null) getPreferenceScreen().removePreference(identityPreference);
|
||||
// }
|
||||
// });
|
||||
getPreferenceScreen().removePreference(identityPreference);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,11 +528,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setColor(recipient, selectedColor);
|
||||
|
||||
if (recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceContactUpdateJob(context, recipient.getAddress()));
|
||||
}
|
||||
// if (recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
|
||||
// ApplicationContext.getInstance(context)
|
||||
// .getJobManager()
|
||||
// .add(new MultiDeviceContactUpdateJob(context, recipient.getAddress()));
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
@@ -586,11 +585,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent verifyIdentityIntent = new Intent(getActivity(), VerifyIdentityActivity.class);
|
||||
verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey()));
|
||||
verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);
|
||||
startActivity(verifyIdentityIntent);
|
||||
// Intent verifyIdentityIntent = new Intent(getActivity(), VerifyIdentityActivity.class);
|
||||
// verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
// verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey()));
|
||||
// verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);
|
||||
// startActivity(verifyIdentityIntent);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -642,9 +641,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
DatabaseFactory.getRecipientDatabase(context)
|
||||
.setBlocked(recipient, blocked);
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceBlockedUpdateJob(context));
|
||||
// ApplicationContext.getInstance(context)
|
||||
// .getJobManager()
|
||||
// .add(new MultiDeviceBlockedUpdateJob(context));
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
|
||||
@@ -38,6 +38,8 @@ import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.thoughtcrime.securesms.components.SearchToolbar;
|
||||
import org.thoughtcrime.securesms.connect.ApplicationDcContext;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
@@ -46,6 +48,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Dialogs;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
@@ -57,6 +60,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An activity to quickly share content with contacts
|
||||
@@ -71,6 +75,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
public static final String EXTRA_THREAD_ID = "thread_id";
|
||||
public static final String EXTRA_ADDRESS_MARSHALLED = "address_marshalled";
|
||||
public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
|
||||
public static final String EXTRA_MSG_IDS = "message_ids";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
@@ -82,6 +87,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
private Uri resolvedExtra;
|
||||
private String mimeType;
|
||||
private boolean isPassingAlongMedia;
|
||||
private ApplicationDcContext dcContext;
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
@@ -100,6 +106,12 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
|
||||
getIntent().putExtra(ContactSelectionListFragment.RECENTS, true);
|
||||
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, false);
|
||||
getIntent().putExtra(ContactSelectionListFragment.FROM_SHARE_ACTIVITY_EXTRA, true);
|
||||
|
||||
|
||||
|
||||
dcContext = DcHelper.getContext(this);
|
||||
|
||||
setContentView(R.layout.share_activity);
|
||||
|
||||
@@ -218,7 +230,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
private void handleResolvedMedia(Intent intent, boolean animate) {
|
||||
long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
|
||||
int threadId = intent.getIntExtra(EXTRA_THREAD_ID, -1);
|
||||
int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1);
|
||||
Address address = null;
|
||||
|
||||
@@ -240,16 +252,13 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
contactsFragment.getView().setVisibility(View.VISIBLE);
|
||||
progressWheel.setVisibility(View.GONE);
|
||||
} else {
|
||||
createConversation(threadId, address, distributionType);
|
||||
createConversation(threadId);
|
||||
}
|
||||
}
|
||||
|
||||
private void createConversation(long threadId, Address address, int distributionType) {
|
||||
private void createConversation(int threadId) {
|
||||
final Intent intent = getBaseShareIntent(ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, address);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
|
||||
|
||||
isPassingAlongMedia = true;
|
||||
startActivity(intent);
|
||||
}
|
||||
@@ -273,9 +282,16 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void onContactSelected(int specialId, String number) {
|
||||
Recipient recipient = Recipient.from(this, Address.fromExternal(this, number), true);
|
||||
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
|
||||
createConversation(existingThread, recipient.getAddress(), ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
Recipient recipient = dcContext.getRecipient(ApplicationDcContext.RECIPIENT_TYPE_CONTACT, specialId);
|
||||
Dialogs.showResponseDialog(this, "", getString(R.string.ShareActivity_forward_message_to_user_info, dcContext.getContact(specialId).getDisplayName()), (dialogInterface, i) -> {
|
||||
int chatId = dcContext.getChatIdByContactId(specialId);
|
||||
if(chatId == 0){
|
||||
chatId = dcContext.createChatByContactId(specialId);
|
||||
}
|
||||
int[] value = getIntent().getIntArrayExtra(EXTRA_MSG_IDS);
|
||||
dcContext.forwardMsgs(value, chatId);
|
||||
createConversation(chatId);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,671 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2017 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.Manifest;
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v7.widget.SwitchCompat;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnticipateInterpolator;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||
import org.thoughtcrime.securesms.components.camera.CameraView;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.qr.QrCode;
|
||||
import org.thoughtcrime.securesms.qr.ScanListener;
|
||||
import org.thoughtcrime.securesms.qr.ScanningThread;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.fingerprint.Fingerprint;
|
||||
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
|
||||
import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
|
||||
import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
||||
|
||||
/**
|
||||
* Activity for verifying identity keys.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, ScanListener, View.OnClickListener {
|
||||
|
||||
private static final String TAG = VerifyIdentityActivity.class.getSimpleName();
|
||||
|
||||
public static final String ADDRESS_EXTRA = "address";
|
||||
public static final String IDENTITY_EXTRA = "recipient_identity";
|
||||
public static final String VERIFIED_EXTRA = "verified_state";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
private VerifyDisplayFragment displayFragment = new VerifyDisplayFragment();
|
||||
private VerifyScanFragment scanFragment = new VerifyScanFragment();
|
||||
|
||||
@Override
|
||||
public void onPreCreate() {
|
||||
dynamicTheme.onCreate(this);
|
||||
dynamicLanguage.onCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle state, boolean ready) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number);
|
||||
|
||||
Recipient recipient = Recipient.from(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
|
||||
recipient.addListener(this);
|
||||
|
||||
setActionBarNotificationBarColor(recipient.getColor());
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(VerifyDisplayFragment.REMOTE_ADDRESS, getIntent().getParcelableExtra(ADDRESS_EXTRA));
|
||||
extras.putParcelable(VerifyDisplayFragment.REMOTE_IDENTITY, getIntent().getParcelableExtra(IDENTITY_EXTRA));
|
||||
extras.putString(VerifyDisplayFragment.REMOTE_NUMBER, recipient.getAddress().toPhoneString());
|
||||
extras.putParcelable(VerifyDisplayFragment.LOCAL_IDENTITY, new IdentityKeyParcelable(IdentityKeyUtil.getIdentityKey(this)));
|
||||
extras.putString(VerifyDisplayFragment.LOCAL_NUMBER, TextSecurePreferences.getLocalNumber(this));
|
||||
extras.putBoolean(VerifyDisplayFragment.VERIFIED_STATE, getIntent().getBooleanExtra(VERIFIED_EXTRA, false));
|
||||
|
||||
scanFragment.setScanListener(this);
|
||||
displayFragment.setClickListener(this);
|
||||
|
||||
initFragment(android.R.id.content, displayFragment, dynamicLanguage.getCurrentLocale(), extras);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: finish(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient recipient) {
|
||||
Util.runOnMain(() -> setActionBarNotificationBarColor(recipient.getColor()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQrDataFound(final String data) {
|
||||
Util.runOnMain(() -> {
|
||||
((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
|
||||
|
||||
getSupportFragmentManager().popBackStack();
|
||||
displayFragment.setScannedFingerprint(data);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(getString(R.string.VerifyIdentityActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code_but_it_has_been_permanently_denied))
|
||||
.onAllGranted(() -> {
|
||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||
transaction.setCustomAnimations(R.anim.slide_from_top, R.anim.slide_to_bottom,
|
||||
R.anim.slide_from_bottom, R.anim.slide_to_top);
|
||||
|
||||
transaction.replace(android.R.id.content, scanFragment)
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss();
|
||||
})
|
||||
.onAnyDenied(() -> Toast.makeText(this, R.string.VerifyIdentityActivity_unable_to_scan_qr_code_without_camera_permission, Toast.LENGTH_LONG).show())
|
||||
.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
private void setActionBarNotificationBarColor(MaterialColor color) {
|
||||
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setStatusBarColor(color.toStatusBarColor(this));
|
||||
}
|
||||
}
|
||||
|
||||
public static class VerifyDisplayFragment extends Fragment implements RecipientModifiedListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
public static final String REMOTE_ADDRESS = "remote_address";
|
||||
public static final String REMOTE_NUMBER = "remote_number";
|
||||
public static final String REMOTE_IDENTITY = "remote_identity";
|
||||
public static final String LOCAL_IDENTITY = "local_identity";
|
||||
public static final String LOCAL_NUMBER = "local_number";
|
||||
public static final String VERIFIED_STATE = "verified_state";
|
||||
|
||||
private Recipient recipient;
|
||||
private String localNumber;
|
||||
private String remoteNumber;
|
||||
|
||||
private IdentityKey localIdentity;
|
||||
private IdentityKey remoteIdentity;
|
||||
|
||||
private Fingerprint fingerprint;
|
||||
|
||||
private View container;
|
||||
private View numbersContainer;
|
||||
private ImageView qrCode;
|
||||
private ImageView qrVerified;
|
||||
private TextView tapLabel;
|
||||
private TextView description;
|
||||
private View.OnClickListener clickListener;
|
||||
private SwitchCompat verified;
|
||||
|
||||
private TextView[] codes = new TextView[12];
|
||||
private boolean animateSuccessOnDraw = false;
|
||||
private boolean animateFailureOnDraw = false;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
|
||||
this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_display_fragment);
|
||||
this.numbersContainer = ViewUtil.findById(container, R.id.number_table);
|
||||
this.qrCode = ViewUtil.findById(container, R.id.qr_code);
|
||||
this.verified = ViewUtil.findById(container, R.id.verified_switch);
|
||||
this.qrVerified = ViewUtil.findById(container, R.id.qr_verified);
|
||||
this.description = ViewUtil.findById(container, R.id.description);
|
||||
this.tapLabel = ViewUtil.findById(container, R.id.tap_label);
|
||||
this.codes[0] = ViewUtil.findById(container, R.id.code_first);
|
||||
this.codes[1] = ViewUtil.findById(container, R.id.code_second);
|
||||
this.codes[2] = ViewUtil.findById(container, R.id.code_third);
|
||||
this.codes[3] = ViewUtil.findById(container, R.id.code_fourth);
|
||||
this.codes[4] = ViewUtil.findById(container, R.id.code_fifth);
|
||||
this.codes[5] = ViewUtil.findById(container, R.id.code_sixth);
|
||||
this.codes[6] = ViewUtil.findById(container, R.id.code_seventh);
|
||||
this.codes[7] = ViewUtil.findById(container, R.id.code_eighth);
|
||||
this.codes[8] = ViewUtil.findById(container, R.id.code_ninth);
|
||||
this.codes[9] = ViewUtil.findById(container, R.id.code_tenth);
|
||||
this.codes[10] = ViewUtil.findById(container, R.id.code_eleventh);
|
||||
this.codes[11] = ViewUtil.findById(container, R.id.code_twelth);
|
||||
|
||||
this.qrCode.setOnClickListener(clickListener);
|
||||
this.registerForContextMenu(numbersContainer);
|
||||
|
||||
this.verified.setChecked(getArguments().getBoolean(VERIFIED_STATE, false));
|
||||
this.verified.setOnCheckedChangeListener(this);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
|
||||
Address address = getArguments().getParcelable(REMOTE_ADDRESS);
|
||||
IdentityKeyParcelable localIdentityParcelable = getArguments().getParcelable(LOCAL_IDENTITY);
|
||||
IdentityKeyParcelable remoteIdentityParcelable = getArguments().getParcelable(REMOTE_IDENTITY);
|
||||
|
||||
if (address == null) throw new AssertionError("Address required");
|
||||
if (localIdentityParcelable == null) throw new AssertionError("local identity required");
|
||||
if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required");
|
||||
|
||||
this.localNumber = getArguments().getString(LOCAL_NUMBER);
|
||||
this.localIdentity = localIdentityParcelable.get();
|
||||
this.remoteNumber = getArguments().getString(REMOTE_NUMBER);
|
||||
this.recipient = Recipient.from(getActivity(), address, true);
|
||||
this.remoteIdentity = remoteIdentityParcelable.get();
|
||||
|
||||
this.recipient.addListener(this);
|
||||
|
||||
new AsyncTask<Void, Void, Fingerprint>() {
|
||||
@Override
|
||||
protected Fingerprint doInBackground(Void... params) {
|
||||
return new NumericFingerprintGenerator(5200).createFor(localNumber, localIdentity,
|
||||
remoteNumber, remoteIdentity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Fingerprint fingerprint) {
|
||||
VerifyDisplayFragment.this.fingerprint = fingerprint;
|
||||
setFingerprintViews(fingerprint, true);
|
||||
getActivity().supportInvalidateOptionsMenu();
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient recipient) {
|
||||
Util.runOnMain(() -> setRecipientText(recipient));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
setRecipientText(recipient);
|
||||
|
||||
if (fingerprint != null) {
|
||||
setFingerprintViews(fingerprint, false);
|
||||
}
|
||||
|
||||
if (animateSuccessOnDraw) {
|
||||
animateSuccessOnDraw = false;
|
||||
animateVerifiedSuccess();
|
||||
} else if (animateFailureOnDraw) {
|
||||
animateFailureOnDraw = false;
|
||||
animateVerifiedFailure();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
recipient.removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view,
|
||||
ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
|
||||
if (fingerprint != null) {
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.verify_display_fragment_context_menu, menu);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
if (fingerprint == null) return super.onContextItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_copy: handleCopyToClipboard(fingerprint, codes.length); return true;
|
||||
case R.id.menu_compare: handleCompareWithClipboard(fingerprint); return true;
|
||||
default: return super.onContextItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
if (fingerprint != null) {
|
||||
inflater.inflate(R.menu.verify_identity, menu);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.verify_identity__share: handleShare(fingerprint, codes.length); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setScannedFingerprint(String scanned) {
|
||||
try {
|
||||
if (fingerprint.getScannableFingerprint().compareTo(scanned.getBytes("ISO-8859-1"))) {
|
||||
this.animateSuccessOnDraw = true;
|
||||
} else {
|
||||
this.animateFailureOnDraw = true;
|
||||
}
|
||||
} catch (FingerprintVersionMismatchException e) {
|
||||
Log.w(TAG, e);
|
||||
if (e.getOurVersion() < e.getTheirVersion()) {
|
||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_a_newer_version_of_Signal, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_an_old_version_of_signal, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} catch (FingerprintParsingException e) {
|
||||
Log.w(TAG, e);
|
||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_the_scanned_qr_code_is_not_a_correctly_formatted_safety_number, Toast.LENGTH_LONG).show();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setClickListener(View.OnClickListener listener) {
|
||||
this.clickListener = listener;
|
||||
}
|
||||
|
||||
private @NonNull String getFormattedSafetyNumbers(@NonNull Fingerprint fingerprint, int segmentCount) {
|
||||
String[] segments = getSegments(fingerprint, segmentCount);
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < segments.length; i++) {
|
||||
result.append(segments[i]);
|
||||
|
||||
if (i != segments.length - 1) {
|
||||
if (((i+1) % 4) == 0) result.append('\n');
|
||||
else result.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private void handleCopyToClipboard(Fingerprint fingerprint, int segmentCount) {
|
||||
Util.writeTextToClipboard(getActivity(), getFormattedSafetyNumbers(fingerprint, segmentCount));
|
||||
}
|
||||
|
||||
private void handleCompareWithClipboard(Fingerprint fingerprint) {
|
||||
String clipboardData = Util.readTextFromClipboard(getActivity());
|
||||
|
||||
if (clipboardData == null) {
|
||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_safety_number_to_compare_was_found_in_the_clipboard, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String numericClipboardData = clipboardData.replaceAll("\\D", "");
|
||||
|
||||
if (TextUtils.isEmpty(numericClipboardData) || numericClipboardData.length() != 60) {
|
||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_safety_number_to_compare_was_found_in_the_clipboard, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fingerprint.getDisplayableFingerprint().getDisplayText().equals(numericClipboardData)) {
|
||||
animateVerifiedSuccess();
|
||||
} else {
|
||||
animateVerifiedFailure();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleShare(@NonNull Fingerprint fingerprint, int segmentCount) {
|
||||
String shareString =
|
||||
getString(R.string.VerifyIdentityActivity_our_signal_safety_number) + "\n" +
|
||||
getFormattedSafetyNumbers(fingerprint, segmentCount) + "\n";
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, shareString);
|
||||
intent.setType("text/plain");
|
||||
|
||||
try {
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.VerifyIdentityActivity_share_safety_number_via)));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_app_to_share_to, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void setRecipientText(Recipient recipient) {
|
||||
description.setText(Html.fromHtml(String.format(getActivity().getString(R.string.verify_display_fragment__if_you_wish_to_verify_the_security_of_your_end_to_end_encryption_with_s), recipient.toShortString())));
|
||||
description.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
private void setFingerprintViews(Fingerprint fingerprint, boolean animate) {
|
||||
String[] segments = getSegments(fingerprint, codes.length);
|
||||
|
||||
for (int i=0;i<codes.length;i++) {
|
||||
if (animate) setCodeSegment(codes[i], segments[i]);
|
||||
else codes[i].setText(segments[i]);
|
||||
}
|
||||
|
||||
byte[] qrCodeData = fingerprint.getScannableFingerprint().getSerialized();
|
||||
String qrCodeString = new String(qrCodeData, Charset.forName("ISO-8859-1"));
|
||||
Bitmap qrCodeBitmap = QrCode.create(qrCodeString);
|
||||
|
||||
qrCode.setImageBitmap(qrCodeBitmap);
|
||||
|
||||
if (animate) {
|
||||
ViewUtil.fadeIn(qrCode, 1000);
|
||||
ViewUtil.fadeIn(tapLabel, 1000);
|
||||
} else {
|
||||
qrCode.setVisibility(View.VISIBLE);
|
||||
tapLabel.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCodeSegment(final TextView codeView, String segment) {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
ValueAnimator valueAnimator = new ValueAnimator();
|
||||
valueAnimator.setObjectValues(0, Integer.parseInt(segment));
|
||||
|
||||
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
int value = (int) animation.getAnimatedValue();
|
||||
codeView.setText(String.format("%05d", value));
|
||||
}
|
||||
});
|
||||
|
||||
valueAnimator.setEvaluator(new TypeEvaluator<Integer>() {
|
||||
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
|
||||
return Math.round(startValue + (endValue - startValue) * fraction);
|
||||
}
|
||||
});
|
||||
|
||||
valueAnimator.setDuration(1000);
|
||||
valueAnimator.start();
|
||||
} else {
|
||||
codeView.setText(segment);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getSegments(Fingerprint fingerprint, int segmentCount) {
|
||||
String[] segments = new String[segmentCount];
|
||||
String digits = fingerprint.getDisplayableFingerprint().getDisplayText();
|
||||
int partSize = digits.length() / segmentCount;
|
||||
|
||||
for (int i=0;i<segmentCount;i++) {
|
||||
segments[i] = digits.substring(i * partSize, (i * partSize) + partSize);
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
private Bitmap createVerifiedBitmap(int width, int height, @DrawableRes int id) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Bitmap check = BitmapFactory.decodeResource(getResources(), id);
|
||||
float offset = (width - check.getWidth()) / 2;
|
||||
|
||||
canvas.drawBitmap(check, offset, offset, null);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private void animateVerifiedSuccess() {
|
||||
Bitmap qrBitmap = ((BitmapDrawable)qrCode.getDrawable()).getBitmap();
|
||||
Bitmap qrSuccess = createVerifiedBitmap(qrBitmap.getWidth(), qrBitmap.getHeight(), R.drawable.ic_check_white_48dp);
|
||||
|
||||
qrVerified.setImageBitmap(qrSuccess);
|
||||
qrVerified.getBackground().setColorFilter(getResources().getColor(R.color.green_500), PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
animateVerified();
|
||||
}
|
||||
|
||||
private void animateVerifiedFailure() {
|
||||
Bitmap qrBitmap = ((BitmapDrawable)qrCode.getDrawable()).getBitmap();
|
||||
Bitmap qrSuccess = createVerifiedBitmap(qrBitmap.getWidth(), qrBitmap.getHeight(), R.drawable.ic_close_white_48dp);
|
||||
|
||||
qrVerified.setImageBitmap(qrSuccess);
|
||||
qrVerified.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
animateVerified();
|
||||
}
|
||||
|
||||
private void animateVerified() {
|
||||
ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1, 0, 1,
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
|
||||
scaleAnimation.setInterpolator(new OvershootInterpolator());
|
||||
scaleAnimation.setDuration(800);
|
||||
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
qrVerified.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0, 1, 0,
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
|
||||
|
||||
scaleAnimation.setInterpolator(new AnticipateInterpolator());
|
||||
scaleAnimation.setDuration(500);
|
||||
ViewUtil.animateOut(qrVerified, scaleAnimation, View.GONE);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
});
|
||||
|
||||
ViewUtil.animateIn(qrVerified, scaleAnimation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
|
||||
new AsyncTask<Recipient, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Recipient... params) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
if (isChecked) {
|
||||
Log.w(TAG, "Saving identity: " + params[0].getAddress());
|
||||
DatabaseFactory.getIdentityDatabase(getActivity())
|
||||
.saveIdentity(params[0].getAddress(),
|
||||
remoteIdentity,
|
||||
VerifiedStatus.VERIFIED, false,
|
||||
System.currentTimeMillis(), true);
|
||||
} else {
|
||||
DatabaseFactory.getIdentityDatabase(getActivity())
|
||||
.setVerified(params[0].getAddress(),
|
||||
remoteIdentity,
|
||||
VerifiedStatus.DEFAULT);
|
||||
}
|
||||
|
||||
ApplicationContext.getInstance(getActivity())
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceVerifiedUpdateJob(getActivity(),
|
||||
recipient.getAddress(),
|
||||
remoteIdentity,
|
||||
isChecked ? VerifiedStatus.VERIFIED :
|
||||
VerifiedStatus.DEFAULT));
|
||||
|
||||
IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
|
||||
}
|
||||
}
|
||||
|
||||
public static class VerifyScanFragment extends Fragment {
|
||||
|
||||
private View container;
|
||||
private CameraView cameraView;
|
||||
private ScanningThread scanningThread;
|
||||
private ScanListener scanListener;
|
||||
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
|
||||
this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_scan_fragment);
|
||||
this.cameraView = ViewUtil.findById(container, R.id.scanner);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
this.scanningThread = new ScanningThread();
|
||||
this.scanningThread.setScanListener(scanListener);
|
||||
this.scanningThread.setCharacterSet("ISO-8859-1");
|
||||
this.cameraView.onResume();
|
||||
this.cameraView.setPreviewCallback(scanningThread);
|
||||
this.scanningThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
this.cameraView.onPause();
|
||||
this.scanningThread.stopScanning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfiguration) {
|
||||
super.onConfigurationChanged(newConfiguration);
|
||||
this.cameraView.onPause();
|
||||
this.cameraView.onResume();
|
||||
this.cameraView.setPreviewCallback(scanningThread);
|
||||
}
|
||||
|
||||
public void setScanListener(ScanListener listener) {
|
||||
if (this.scanningThread != null) scanningThread.setScanListener(listener);
|
||||
this.scanListener = listener;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.components.webrtc.WebRtcAnswerDeclineButton;
|
||||
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallControls;
|
||||
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallScreen;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||
import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
||||
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.webrtc.CameraState;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
|
||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
||||
|
||||
public class WebRtcCallActivity extends Activity {
|
||||
|
||||
private static final String TAG = WebRtcCallActivity.class.getSimpleName();
|
||||
|
||||
private static final int STANDARD_DELAY_FINISH = 1000;
|
||||
public static final int BUSY_SIGNAL_DELAY_FINISH = 5500;
|
||||
|
||||
public static final String ANSWER_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_ACTION";
|
||||
public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION";
|
||||
public static final String END_CALL_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".END_CALL_ACTION";
|
||||
|
||||
private WebRtcCallScreen callScreen;
|
||||
private SignalServiceNetworkAccess networkAccess;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
Log.w(TAG, "onCreate()");
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.webrtc_call_activity);
|
||||
|
||||
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
|
||||
|
||||
initializeResources();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
Log.w(TAG, "onResume()");
|
||||
super.onResume();
|
||||
if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStarted(this);
|
||||
initializeScreenshotSecurity();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent){
|
||||
Log.w(TAG, "onNewIntent");
|
||||
if (ANSWER_ACTION.equals(intent.getAction())) {
|
||||
handleAnswerCall();
|
||||
} else if (DENY_ACTION.equals(intent.getAction())) {
|
||||
handleDenyCall();
|
||||
} else if (END_CALL_ACTION.equals(intent.getAction())) {
|
||||
handleEndCall();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
Log.w(TAG, "onPause");
|
||||
super.onPause();
|
||||
if (!networkAccess.isCensored(this)) MessageRetrievalService.registerActivityStopped(this);
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfiguration) {
|
||||
super.onConfigurationChanged(newConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
private void initializeScreenshotSecurity() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
|
||||
TextSecurePreferences.isScreenSecurityEnabled(this))
|
||||
{
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
} else {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
callScreen = ViewUtil.findById(this, R.id.callScreen);
|
||||
callScreen.setHangupButtonListener(new HangupButtonListener());
|
||||
callScreen.setIncomingCallActionListener(new IncomingCallActionListener());
|
||||
callScreen.setAudioMuteButtonListener(new AudioMuteButtonListener());
|
||||
callScreen.setVideoMuteButtonListener(new VideoMuteButtonListener());
|
||||
callScreen.setCameraFlipButtonListener(new CameraFlipButtonListener());
|
||||
callScreen.setSpeakerButtonListener(new SpeakerButtonListener());
|
||||
callScreen.setBluetoothButtonListener(new BluetoothButtonListener());
|
||||
|
||||
networkAccess = new SignalServiceNetworkAccess(this);
|
||||
}
|
||||
|
||||
private void handleSetMuteAudio(boolean enabled) {
|
||||
Intent intent = new Intent(this, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_SET_MUTE_AUDIO);
|
||||
intent.putExtra(WebRtcCallService.EXTRA_MUTE, enabled);
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
private void handleSetMuteVideo(boolean muted) {
|
||||
Intent intent = new Intent(this, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_SET_MUTE_VIDEO);
|
||||
intent.putExtra(WebRtcCallService.EXTRA_MUTE, muted);
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
private void handleFlipCamera() {
|
||||
Intent intent = new Intent(this, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_FLIP_CAMERA);
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
private void handleAnswerCall() {
|
||||
WebRtcViewModel event = EventBus.getDefault().getStickyEvent(WebRtcViewModel.class);
|
||||
|
||||
if (event != null) {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.WebRtcCallActivity_to_answer_the_call_from_s_give_signal_access_to_your_microphone, event.getRecipient().toShortString()),
|
||||
R.drawable.ic_mic_white_48dp, R.drawable.ic_videocam_white_48dp)
|
||||
.withPermanentDenialDialog(getString(R.string.WebRtcCallActivity_signal_requires_microphone_and_camera_permissions_in_order_to_make_or_receive_calls))
|
||||
.onAllGranted(() -> {
|
||||
callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_answering));
|
||||
|
||||
Intent intent = new Intent(this, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_ANSWER_CALL);
|
||||
startService(intent);
|
||||
})
|
||||
.onAnyDenied(this::handleDenyCall)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDenyCall() {
|
||||
WebRtcViewModel event = EventBus.getDefault().getStickyEvent(WebRtcViewModel.class);
|
||||
|
||||
if (event != null) {
|
||||
Intent intent = new Intent(this, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_DENY_CALL);
|
||||
startService(intent);
|
||||
|
||||
callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ending_call));
|
||||
delayedFinish();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEndCall() {
|
||||
Log.w(TAG, "Hangup pressed, handling termination now...");
|
||||
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_LOCAL_HANGUP);
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
private void handleIncomingCall(@NonNull WebRtcViewModel event) {
|
||||
callScreen.setIncomingCall(event.getRecipient());
|
||||
}
|
||||
|
||||
private void handleOutgoingCall(@NonNull WebRtcViewModel event) {
|
||||
callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_dialing));
|
||||
}
|
||||
|
||||
private void handleTerminate(@NonNull Recipient recipient /*, int terminationType */) {
|
||||
Log.w(TAG, "handleTerminate called");
|
||||
|
||||
callScreen.setActiveCall(recipient, getString(R.string.RedPhone_ending_call));
|
||||
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
|
||||
|
||||
delayedFinish();
|
||||
}
|
||||
|
||||
private void handleCallRinging(@NonNull WebRtcViewModel event) {
|
||||
callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ringing));
|
||||
}
|
||||
|
||||
private void handleCallBusy(@NonNull WebRtcViewModel event) {
|
||||
callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_busy));
|
||||
|
||||
delayedFinish(BUSY_SIGNAL_DELAY_FINISH);
|
||||
}
|
||||
|
||||
private void handleCallConnected(@NonNull WebRtcViewModel event) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
|
||||
callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_connected), "");
|
||||
}
|
||||
|
||||
private void handleRecipientUnavailable(@NonNull WebRtcViewModel event) {
|
||||
callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_recipient_unavailable));
|
||||
delayedFinish();
|
||||
}
|
||||
|
||||
private void handleServerFailure(@NonNull WebRtcViewModel event) {
|
||||
callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_network_failed));
|
||||
delayedFinish();
|
||||
}
|
||||
|
||||
private void handleNoSuchUser(final @NonNull WebRtcViewModel event) {
|
||||
if (isFinishing()) return; // XXX Stuart added this check above, not sure why, so I'm repeating in ignorance. - moxie
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
|
||||
dialog.setTitle(R.string.RedPhone_number_not_registered);
|
||||
dialog.setIconAttribute(R.attr.dialog_alert_icon);
|
||||
dialog.setMessage(R.string.RedPhone_the_number_you_dialed_does_not_support_secure_voice);
|
||||
dialog.setCancelable(true);
|
||||
dialog.setPositiveButton(R.string.RedPhone_got_it, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
WebRtcCallActivity.this.handleTerminate(event.getRecipient());
|
||||
}
|
||||
});
|
||||
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
WebRtcCallActivity.this.handleTerminate(event.getRecipient());
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void handleUntrustedIdentity(@NonNull WebRtcViewModel event) {
|
||||
final IdentityKey theirIdentity = event.getIdentityKey();
|
||||
final Recipient recipient = event.getRecipient();
|
||||
|
||||
callScreen.setUntrustedIdentity(recipient, theirIdentity);
|
||||
callScreen.setAcceptIdentityListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(WebRtcCallActivity.this);
|
||||
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.getAddress().serialize(), 1), theirIdentity, true);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, recipient.getAddress());
|
||||
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL);
|
||||
startService(intent);
|
||||
}
|
||||
});
|
||||
|
||||
callScreen.setCancelIdentityButton(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleTerminate(recipient);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void delayedFinish() {
|
||||
delayedFinish(STANDARD_DELAY_FINISH);
|
||||
}
|
||||
|
||||
private void delayedFinish(int delayMillis) {
|
||||
callScreen.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
WebRtcCallActivity.this.finish();
|
||||
}
|
||||
}, delayMillis);
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(final WebRtcViewModel event) {
|
||||
Log.w(TAG, "Got message from service: " + event);
|
||||
|
||||
switch (event.getState()) {
|
||||
case CALL_CONNECTED: handleCallConnected(event); break;
|
||||
case NETWORK_FAILURE: handleServerFailure(event); break;
|
||||
case CALL_RINGING: handleCallRinging(event); break;
|
||||
case CALL_DISCONNECTED: handleTerminate(event.getRecipient()); break;
|
||||
case NO_SUCH_USER: handleNoSuchUser(event); break;
|
||||
case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break;
|
||||
case CALL_INCOMING: handleIncomingCall(event); break;
|
||||
case CALL_OUTGOING: handleOutgoingCall(event); break;
|
||||
case CALL_BUSY: handleCallBusy(event); break;
|
||||
case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break;
|
||||
}
|
||||
|
||||
callScreen.setRemoteVideoEnabled(event.isRemoteVideoEnabled());
|
||||
callScreen.updateAudioState(event.isBluetoothAvailable(), event.isMicrophoneEnabled());
|
||||
callScreen.setControlsEnabled(event.getState() != WebRtcViewModel.State.CALL_INCOMING);
|
||||
callScreen.setLocalVideoState(event.getLocalCameraState());
|
||||
}
|
||||
|
||||
private class HangupButtonListener implements WebRtcCallScreen.HangupButtonListener {
|
||||
public void onClick() {
|
||||
handleEndCall();
|
||||
}
|
||||
}
|
||||
|
||||
private class AudioMuteButtonListener implements WebRtcCallControls.MuteButtonListener {
|
||||
@Override
|
||||
public void onToggle(boolean isMuted) {
|
||||
WebRtcCallActivity.this.handleSetMuteAudio(isMuted);
|
||||
}
|
||||
}
|
||||
|
||||
private class VideoMuteButtonListener implements WebRtcCallControls.MuteButtonListener {
|
||||
@Override
|
||||
public void onToggle(boolean isMuted) {
|
||||
WebRtcCallActivity.this.handleSetMuteVideo(isMuted);
|
||||
}
|
||||
}
|
||||
|
||||
private class CameraFlipButtonListener implements WebRtcCallControls.CameraFlipButtonListener {
|
||||
@Override
|
||||
public void onToggle() {
|
||||
WebRtcCallActivity.this.handleFlipCamera();
|
||||
}
|
||||
}
|
||||
|
||||
private class SpeakerButtonListener implements WebRtcCallControls.SpeakerButtonListener {
|
||||
@Override
|
||||
public void onSpeakerChange(boolean isSpeaker) {
|
||||
AudioManager audioManager = ServiceUtil.getAudioManager(WebRtcCallActivity.this);
|
||||
audioManager.setSpeakerphoneOn(isSpeaker);
|
||||
|
||||
if (isSpeaker && audioManager.isBluetoothScoOn()) {
|
||||
audioManager.stopBluetoothSco();
|
||||
audioManager.setBluetoothScoOn(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BluetoothButtonListener implements WebRtcCallControls.BluetoothButtonListener {
|
||||
@Override
|
||||
public void onBluetoothChange(boolean isBluetooth) {
|
||||
AudioManager audioManager = ServiceUtil.getAudioManager(WebRtcCallActivity.this);
|
||||
|
||||
if (isBluetooth) {
|
||||
audioManager.startBluetoothSco();
|
||||
audioManager.setBluetoothScoOn(true);
|
||||
} else {
|
||||
audioManager.stopBluetoothSco();
|
||||
audioManager.setBluetoothScoOn(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class IncomingCallActionListener implements WebRtcAnswerDeclineButton.AnswerDeclineListener {
|
||||
@Override
|
||||
public void onAnswered() {
|
||||
WebRtcCallActivity.this.handleAnswerCall();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeclined() {
|
||||
WebRtcCallActivity.this.handleDenyCall();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +1,17 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@@ -49,7 +47,8 @@ public class AvatarImageView extends AppCompatImageView {
|
||||
|
||||
public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) {
|
||||
if (recipient != null) {
|
||||
requestManager.load(recipient.getContactPhoto())
|
||||
ContactPhoto contactPhoto = recipient.getContactPhoto(getContext());
|
||||
requestManager.load(contactPhoto)
|
||||
.fallback(recipient.getFallbackContactPhotoDrawable(getContext(), inverted))
|
||||
.error(recipient.getFallbackContactPhotoDrawable(getContext(), inverted))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
|
||||
@@ -24,10 +24,6 @@ import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.CustomDefaultPreference.CustomDefaultPreferenceDialogFragmentCompat.CustomPreferenceValidator;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
|
||||
public class CustomDefaultPreference extends DialogPreference {
|
||||
|
||||
private static final String TAG = CustomDefaultPreference.class.getSimpleName();
|
||||
@@ -56,11 +52,6 @@ public class CustomDefaultPreference extends DialogPreference {
|
||||
setDialogLayoutResource(R.layout.custom_default_preference_dialog);
|
||||
}
|
||||
|
||||
public CustomDefaultPreference setValidator(CustomPreferenceValidator validator) {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomDefaultPreference setDefaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
this.setSummary(getSummary());
|
||||
@@ -193,46 +184,6 @@ public class CustomDefaultPreference extends DialogPreference {
|
||||
}
|
||||
}
|
||||
|
||||
public static class UriValidator implements CustomPreferenceValidator {
|
||||
@Override
|
||||
public boolean isValid(String value) {
|
||||
if (TextUtils.isEmpty(value)) return true;
|
||||
|
||||
try {
|
||||
new URI(value);
|
||||
return true;
|
||||
} catch (URISyntaxException mue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class HostnameValidator implements CustomPreferenceValidator {
|
||||
@Override
|
||||
public boolean isValid(String value) {
|
||||
if (TextUtils.isEmpty(value)) return true;
|
||||
|
||||
try {
|
||||
URI uri = new URI(null, value, null, null);
|
||||
return true;
|
||||
} catch (URISyntaxException mue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PortValidator implements CustomPreferenceValidator {
|
||||
@Override
|
||||
public boolean isValid(String value) {
|
||||
try {
|
||||
Integer.parseInt(value);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SelectionLister implements AdapterView.OnItemSelectedListener {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.webrtc;
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.tomergoldst.tooltips.ToolTip;
|
||||
import com.tomergoldst.tooltips.ToolTipsManager;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.AccessibleToggleButton;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.webrtc.CameraState;
|
||||
|
||||
public class WebRtcCallControls extends LinearLayout {
|
||||
|
||||
private static final String TAG = WebRtcCallControls.class.getSimpleName();
|
||||
|
||||
private AccessibleToggleButton audioMuteButton;
|
||||
private AccessibleToggleButton videoMuteButton;
|
||||
private AccessibleToggleButton speakerButton;
|
||||
private AccessibleToggleButton bluetoothButton;
|
||||
private AccessibleToggleButton cameraFlipButton;
|
||||
private boolean cameraFlipAvailable;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public WebRtcCallControls(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public WebRtcCallControls(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public WebRtcCallControls(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public WebRtcCallControls(Context context) {
|
||||
super(context);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.webrtc_call_controls, this, true);
|
||||
|
||||
this.speakerButton = ViewUtil.findById(this, R.id.speakerButton);
|
||||
this.bluetoothButton = ViewUtil.findById(this, R.id.bluetoothButton);
|
||||
this.audioMuteButton = ViewUtil.findById(this, R.id.muteButton);
|
||||
this.videoMuteButton = ViewUtil.findById(this, R.id.video_mute_button);
|
||||
this.cameraFlipButton = ViewUtil.findById(this, R.id.camera_flip_button);
|
||||
}
|
||||
|
||||
public void setAudioMuteButtonListener(final MuteButtonListener listener) {
|
||||
audioMuteButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
|
||||
listener.onToggle(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setVideoMuteButtonListener(final MuteButtonListener listener) {
|
||||
videoMuteButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
boolean videoMuted = !isChecked;
|
||||
listener.onToggle(videoMuted);
|
||||
cameraFlipButton.setVisibility(!videoMuted && cameraFlipAvailable ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setCameraFlipButtonListener(final CameraFlipButtonListener listener) {
|
||||
cameraFlipButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
listener.onToggle();
|
||||
cameraFlipButton.setBackgroundResource(isChecked ? R.drawable.webrtc_camera_front_button
|
||||
: R.drawable.webrtc_camera_rear_button);
|
||||
cameraFlipButton.setEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setSpeakerButtonListener(final SpeakerButtonListener listener) {
|
||||
speakerButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
listener.onSpeakerChange(isChecked);
|
||||
updateAudioState(bluetoothButton.getVisibility() == View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setBluetoothButtonListener(final BluetoothButtonListener listener) {
|
||||
bluetoothButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
listener.onBluetoothChange(isChecked);
|
||||
updateAudioState(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updateAudioState(boolean isBluetoothAvailable) {
|
||||
AudioManager audioManager = ServiceUtil.getAudioManager(getContext());
|
||||
|
||||
if (!isBluetoothAvailable) {
|
||||
bluetoothButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
bluetoothButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (audioManager.isBluetoothScoOn()) {
|
||||
bluetoothButton.setChecked(true, false);
|
||||
speakerButton.setChecked(false, false);
|
||||
} else if (audioManager.isSpeakerphoneOn()) {
|
||||
speakerButton.setChecked(true, false);
|
||||
bluetoothButton.setChecked(false, false);
|
||||
} else {
|
||||
speakerButton.setChecked(false, false);
|
||||
bluetoothButton.setChecked(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isVideoEnabled() {
|
||||
return videoMuteButton.isChecked();
|
||||
}
|
||||
|
||||
public void setVideoEnabled(boolean enabled) {
|
||||
videoMuteButton.setChecked(enabled, false);
|
||||
}
|
||||
|
||||
public void setVideoAvailable(boolean available) {
|
||||
videoMuteButton.setVisibility(available ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
public void setCameraFlipButtonEnabled(boolean enabled) {
|
||||
cameraFlipButton.setChecked(enabled, false);
|
||||
}
|
||||
|
||||
public void setCameraFlipAvailable(boolean available) {
|
||||
cameraFlipAvailable = available;
|
||||
}
|
||||
|
||||
public void setCameraFlipClickable(boolean clickable) {
|
||||
setControlEnabled(cameraFlipButton, clickable);
|
||||
}
|
||||
|
||||
public void setMicrophoneEnabled(boolean enabled) {
|
||||
audioMuteButton.setChecked(!enabled, false);
|
||||
}
|
||||
|
||||
public void setControlsEnabled(boolean enabled) {
|
||||
setControlEnabled(speakerButton, enabled);
|
||||
setControlEnabled(bluetoothButton, enabled);
|
||||
setControlEnabled(videoMuteButton, enabled);
|
||||
setControlEnabled(cameraFlipButton, enabled);
|
||||
setControlEnabled(audioMuteButton, enabled);
|
||||
}
|
||||
|
||||
private void setControlEnabled(@NonNull View view, boolean enabled) {
|
||||
if (enabled) {
|
||||
view.setAlpha(1.0f);
|
||||
view.setEnabled(true);
|
||||
} else {
|
||||
view.setAlpha(0.3f);
|
||||
view.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void displayVideoTooltip(ViewGroup viewGroup) {
|
||||
if (Build.VERSION.SDK_INT > 15 && videoMuteButton.getVisibility() == VISIBLE) {
|
||||
final ToolTipsManager toolTipsManager = new ToolTipsManager();
|
||||
|
||||
ToolTip toolTip = new ToolTip.Builder(getContext(), videoMuteButton, viewGroup,
|
||||
getContext().getString(R.string.WebRtcCallControls_tap_to_enable_your_video),
|
||||
ToolTip.POSITION_BELOW).build();
|
||||
toolTipsManager.show(toolTip);
|
||||
|
||||
videoMuteButton.postDelayed(() -> toolTipsManager.findAndDismiss(videoMuteButton), 4000);
|
||||
}
|
||||
}
|
||||
|
||||
public static interface MuteButtonListener {
|
||||
public void onToggle(boolean isMuted);
|
||||
}
|
||||
|
||||
public static interface CameraFlipButtonListener {
|
||||
public void onToggle();
|
||||
}
|
||||
|
||||
public static interface SpeakerButtonListener {
|
||||
public void onSpeakerChange(boolean isSpeaker);
|
||||
}
|
||||
|
||||
public static interface BluetoothButtonListener {
|
||||
public void onBluetoothChange(boolean isBluetooth);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.components.webrtc;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.VerifySpan;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.webrtc.CameraState;
|
||||
import org.webrtc.SurfaceViewRenderer;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
|
||||
/**
|
||||
* A UI widget that encapsulates the entire in-call screen
|
||||
* for both initiators and responders.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedListener {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = WebRtcCallScreen.class.getSimpleName();
|
||||
|
||||
private ImageView photo;
|
||||
private SurfaceViewRenderer localRenderer;
|
||||
private PercentFrameLayout localRenderLayout;
|
||||
private PercentFrameLayout remoteRenderLayout;
|
||||
private TextView name;
|
||||
private TextView phoneNumber;
|
||||
private TextView label;
|
||||
private TextView elapsedTime;
|
||||
private View untrustedIdentityContainer;
|
||||
private TextView untrustedIdentityExplanation;
|
||||
private Button acceptIdentityButton;
|
||||
private Button cancelIdentityButton;
|
||||
private TextView status;
|
||||
private FloatingActionButton endCallButton;
|
||||
private WebRtcCallControls controls;
|
||||
private RelativeLayout expandedInfo;
|
||||
private ViewGroup callHeader;
|
||||
|
||||
private WebRtcAnswerDeclineButton incomingCallButton;
|
||||
|
||||
private Recipient recipient;
|
||||
private boolean minimized;
|
||||
|
||||
|
||||
public WebRtcCallScreen(Context context) {
|
||||
super(context);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public WebRtcCallScreen(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public WebRtcCallScreen(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message, @Nullable String sas) {
|
||||
setCard(personInfo, message);
|
||||
setConnected(WebRtcCallService.localRenderer, WebRtcCallService.remoteRenderer);
|
||||
incomingCallButton.stopRingingAnimation();
|
||||
incomingCallButton.setVisibility(View.GONE);
|
||||
endCallButton.show();
|
||||
}
|
||||
|
||||
public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message) {
|
||||
setCard(personInfo, message);
|
||||
incomingCallButton.stopRingingAnimation();
|
||||
incomingCallButton.setVisibility(View.GONE);
|
||||
endCallButton.show();
|
||||
}
|
||||
|
||||
public void setIncomingCall(Recipient personInfo) {
|
||||
setCard(personInfo, getContext().getString(R.string.CallScreen_Incoming_call));
|
||||
endCallButton.setVisibility(View.INVISIBLE);
|
||||
incomingCallButton.setVisibility(View.VISIBLE);
|
||||
incomingCallButton.startRingingAnimation();
|
||||
}
|
||||
|
||||
public void setUntrustedIdentity(Recipient personInfo, IdentityKey untrustedIdentity) {
|
||||
String name = recipient.toShortString();
|
||||
String introduction = String.format(getContext().getString(R.string.WebRtcCallScreen_new_safety_numbers), name, name);
|
||||
SpannableString spannableString = new SpannableString(introduction + " " + getContext().getString(R.string.WebRtcCallScreen_you_may_wish_to_verify_this_contact));
|
||||
|
||||
spannableString.setSpan(new VerifySpan(getContext(), personInfo.getAddress(), untrustedIdentity),
|
||||
introduction.length()+1, spannableString.length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
setPersonInfo(personInfo);
|
||||
|
||||
incomingCallButton.stopRingingAnimation();
|
||||
incomingCallButton.setVisibility(View.GONE);
|
||||
this.status.setText(R.string.WebRtcCallScreen_new_safety_number_title);
|
||||
this.untrustedIdentityContainer.setVisibility(View.VISIBLE);
|
||||
this.untrustedIdentityExplanation.setText(spannableString);
|
||||
this.untrustedIdentityExplanation.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
this.endCallButton.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
public void setIncomingCallActionListener(WebRtcAnswerDeclineButton.AnswerDeclineListener listener) {
|
||||
incomingCallButton.setAnswerDeclineListener(listener);
|
||||
}
|
||||
|
||||
public void setAudioMuteButtonListener(WebRtcCallControls.MuteButtonListener listener) {
|
||||
this.controls.setAudioMuteButtonListener(listener);
|
||||
}
|
||||
|
||||
public void setVideoMuteButtonListener(WebRtcCallControls.MuteButtonListener listener) {
|
||||
this.controls.setVideoMuteButtonListener(listener);
|
||||
}
|
||||
|
||||
public void setCameraFlipButtonListener(WebRtcCallControls.CameraFlipButtonListener listener) {
|
||||
this.controls.setCameraFlipButtonListener(listener);
|
||||
}
|
||||
|
||||
public void setSpeakerButtonListener(WebRtcCallControls.SpeakerButtonListener listener) {
|
||||
this.controls.setSpeakerButtonListener(listener);
|
||||
}
|
||||
|
||||
public void setBluetoothButtonListener(WebRtcCallControls.BluetoothButtonListener listener) {
|
||||
this.controls.setBluetoothButtonListener(listener);
|
||||
}
|
||||
|
||||
public void setHangupButtonListener(final HangupButtonListener listener) {
|
||||
endCallButton.setOnClickListener(v -> listener.onClick());
|
||||
}
|
||||
|
||||
public void setAcceptIdentityListener(OnClickListener listener) {
|
||||
this.acceptIdentityButton.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
public void setCancelIdentityButton(OnClickListener listener) {
|
||||
this.cancelIdentityButton.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
public void updateAudioState(boolean isBluetoothAvailable, boolean isMicrophoneEnabled) {
|
||||
this.controls.updateAudioState(isBluetoothAvailable);
|
||||
this.controls.setMicrophoneEnabled(isMicrophoneEnabled);
|
||||
}
|
||||
|
||||
public void setControlsEnabled(boolean enabled) {
|
||||
this.controls.setControlsEnabled(enabled);
|
||||
}
|
||||
|
||||
public void setLocalVideoState(@NonNull CameraState cameraState) {
|
||||
this.controls.setVideoAvailable(cameraState.getCameraCount() > 0);
|
||||
this.controls.setVideoEnabled(cameraState.isEnabled());
|
||||
this.controls.setCameraFlipAvailable(cameraState.getCameraCount() > 1);
|
||||
this.controls.setCameraFlipClickable(cameraState.getActiveDirection() != CameraState.Direction.PENDING);
|
||||
this.controls.setCameraFlipButtonEnabled(cameraState.getActiveDirection() == CameraState.Direction.BACK);
|
||||
|
||||
if (this.localRenderer != null) {
|
||||
this.localRenderer.setMirror(cameraState.getActiveDirection() == CameraState.Direction.FRONT);
|
||||
}
|
||||
|
||||
if (this.localRenderLayout.isHidden() == cameraState.isEnabled()) {
|
||||
this.localRenderLayout.setHidden(!cameraState.isEnabled());
|
||||
this.localRenderLayout.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void setRemoteVideoEnabled(boolean enabled) {
|
||||
if (enabled && this.remoteRenderLayout.isHidden()) {
|
||||
this.photo.setVisibility(View.INVISIBLE);
|
||||
setMinimized(true);
|
||||
|
||||
this.remoteRenderLayout.setHidden(false);
|
||||
this.remoteRenderLayout.requestLayout();
|
||||
|
||||
if (localRenderLayout.isHidden()) this.controls.displayVideoTooltip(callHeader);
|
||||
} else if (!enabled && !this.remoteRenderLayout.isHidden()){
|
||||
setMinimized(false);
|
||||
this.photo.setVisibility(View.VISIBLE);
|
||||
this.remoteRenderLayout.setHidden(true);
|
||||
this.remoteRenderLayout.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isVideoEnabled() {
|
||||
return controls.isVideoEnabled();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.webrtc_call_screen, this, true);
|
||||
|
||||
this.elapsedTime = findViewById(R.id.elapsedTime);
|
||||
this.photo = findViewById(R.id.photo);
|
||||
this.localRenderLayout = findViewById(R.id.local_render_layout);
|
||||
this.remoteRenderLayout = findViewById(R.id.remote_render_layout);
|
||||
this.phoneNumber = findViewById(R.id.phoneNumber);
|
||||
this.name = findViewById(R.id.name);
|
||||
this.label = findViewById(R.id.label);
|
||||
this.status = findViewById(R.id.callStateLabel);
|
||||
this.controls = findViewById(R.id.inCallControls);
|
||||
this.endCallButton = findViewById(R.id.hangup_fab);
|
||||
this.incomingCallButton = findViewById(R.id.answer_decline_button);
|
||||
this.untrustedIdentityContainer = findViewById(R.id.untrusted_layout);
|
||||
this.untrustedIdentityExplanation = findViewById(R.id.untrusted_explanation);
|
||||
this.acceptIdentityButton = findViewById(R.id.accept_safety_numbers);
|
||||
this.cancelIdentityButton = findViewById(R.id.cancel_safety_numbers);
|
||||
this.expandedInfo = findViewById(R.id.expanded_info);
|
||||
this.callHeader = findViewById(R.id.call_info_1);
|
||||
|
||||
this.localRenderLayout.setHidden(true);
|
||||
this.remoteRenderLayout.setHidden(true);
|
||||
this.minimized = false;
|
||||
|
||||
this.remoteRenderLayout.setOnClickListener(v -> setMinimized(!minimized));
|
||||
}
|
||||
|
||||
private void setConnected(SurfaceViewRenderer localRenderer,
|
||||
SurfaceViewRenderer remoteRenderer)
|
||||
{
|
||||
if (localRenderLayout.getChildCount() == 0 && remoteRenderLayout.getChildCount() == 0) {
|
||||
if (localRenderer.getParent() != null) {
|
||||
((ViewGroup)localRenderer.getParent()).removeView(localRenderer);
|
||||
}
|
||||
|
||||
if (remoteRenderer.getParent() != null) {
|
||||
((ViewGroup)remoteRenderer.getParent()).removeView(remoteRenderer);
|
||||
}
|
||||
|
||||
localRenderLayout.setPosition(7, 70, 25, 25);
|
||||
localRenderLayout.setSquare(true);
|
||||
remoteRenderLayout.setPosition(0, 0, 100, 100);
|
||||
|
||||
localRenderer.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
remoteRenderer.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
localRenderer.setMirror(true);
|
||||
localRenderer.setZOrderMediaOverlay(true);
|
||||
|
||||
localRenderLayout.addView(localRenderer);
|
||||
remoteRenderLayout.addView(remoteRenderer);
|
||||
|
||||
this.localRenderer = localRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
private void setPersonInfo(final @NonNull Recipient recipient) {
|
||||
this.recipient = recipient;
|
||||
this.recipient.addListener(this);
|
||||
|
||||
GlideApp.with(getContext().getApplicationContext())
|
||||
.load(recipient.getContactPhoto())
|
||||
.fallback(recipient.getFallbackContactPhoto().asCallCard(getContext()))
|
||||
.error(recipient.getFallbackContactPhoto().asCallCard(getContext()))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.into(this.photo);
|
||||
|
||||
this.name.setText(recipient.getName());
|
||||
|
||||
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
|
||||
this.phoneNumber.setText(recipient.getAddress().serialize() + " (~" + recipient.getProfileName() + ")");
|
||||
} else {
|
||||
this.phoneNumber.setText(recipient.getAddress().serialize());
|
||||
}
|
||||
}
|
||||
|
||||
private void setCard(Recipient recipient, String status) {
|
||||
setPersonInfo(recipient);
|
||||
this.status.setText(status);
|
||||
this.untrustedIdentityContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void setMinimized(boolean minimized) {
|
||||
if (minimized) {
|
||||
ViewCompat.animate(callHeader).translationY(-1 * expandedInfo.getHeight());
|
||||
ViewCompat.animate(status).alpha(0);
|
||||
ViewCompat.animate(endCallButton).translationY(endCallButton.getHeight() + ViewUtil.dpToPx(getContext(), 40));
|
||||
ViewCompat.animate(endCallButton).alpha(0);
|
||||
|
||||
this.minimized = true;
|
||||
} else {
|
||||
ViewCompat.animate(callHeader).translationY(0);
|
||||
ViewCompat.animate(status).alpha(1);
|
||||
ViewCompat.animate(endCallButton).translationY(0);
|
||||
ViewCompat.animate(endCallButton).alpha(1).withEndAction(() -> {
|
||||
// Note: This is to work around an Android bug, see #6225
|
||||
endCallButton.requestLayout();
|
||||
});
|
||||
|
||||
this.minimized = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(Recipient recipient) {
|
||||
Util.runOnMain(() -> {
|
||||
if (recipient == WebRtcCallScreen.this.recipient) {
|
||||
setPersonInfo(recipient);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface HangupButtonListener {
|
||||
void onClick();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,23 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Delta Chat contributors
|
||||
*
|
||||
* 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.connect;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
@@ -38,11 +26,14 @@ import com.b44t.messenger.DcMsg;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.SystemContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientProvider;
|
||||
import org.thoughtcrime.securesms.util.Hash;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -56,7 +47,6 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class ApplicationDcContext extends DcContext {
|
||||
|
||||
@IntDef({RECIPIENT_TYPE_CHAT, RECIPIENT_TYPE_CONTACT})
|
||||
@@ -66,6 +56,7 @@ public class ApplicationDcContext extends DcContext {
|
||||
public static final int RECIPIENT_TYPE_CONTACT = 1;
|
||||
|
||||
public Context context;
|
||||
public volatile boolean isScreenOn = false;
|
||||
|
||||
public ApplicationDcContext(Context context) {
|
||||
super("android-dev");
|
||||
@@ -92,7 +83,28 @@ public class ApplicationDcContext extends DcContext {
|
||||
}
|
||||
|
||||
new ForegroundDetector(ApplicationContext.getInstance(context));
|
||||
startThreads();
|
||||
startThreads(0);
|
||||
|
||||
TimerReceiver.scheduleNextAlarm(context);
|
||||
|
||||
BroadcastReceiver networkStateReceiver = new NetworkStateReceiver();
|
||||
context.registerReceiver(networkStateReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
BroadcastReceiver screenReceiver = new ScreenReceiver();
|
||||
context.registerReceiver(screenReceiver, filter);
|
||||
|
||||
try {
|
||||
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
||||
isScreenOn = pm.isScreenOn();
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
if( !isScreenOn ) {
|
||||
context.startService(new Intent(context, KeepAliveService.class));
|
||||
}
|
||||
}
|
||||
|
||||
public File getImexDir()
|
||||
@@ -176,13 +188,30 @@ public class ApplicationDcContext extends DcContext {
|
||||
participants.add(getRecipient(RECIPIENT_TYPE_CONTACT, contactId));
|
||||
}
|
||||
RecipientProvider.RecipientDetails recipientDetails = new RecipientProvider.RecipientDetails(chat.getName(), null, false, null, participants);
|
||||
return new Recipient(Address.fromChat(chat.getId()), recipientDetails);
|
||||
Recipient recipient = new Recipient(Address.fromChat(chat.getId()), recipientDetails);
|
||||
if (!chat.isGroup()) {
|
||||
String identifier = Hash.sha256(chat.getName() + chat.getSubtitle());
|
||||
Uri systemContactPhoto = TextSecurePreferences.getSystemContactPhoto(context, identifier);
|
||||
if (systemContactPhoto != null) {
|
||||
recipient.setSystemContactPhoto(systemContactPhoto);
|
||||
}
|
||||
}
|
||||
return recipient;
|
||||
}
|
||||
@NonNull
|
||||
|
||||
public Recipient getRecipient(DcContact contact) {
|
||||
RecipientProvider.RecipientDetails recipientDetails = new RecipientProvider.RecipientDetails(contact.getDisplayName(), null, false, null, null);
|
||||
return new Recipient(Address.fromContact(contact.getId()), recipientDetails);
|
||||
Recipient recipient = new Recipient(Address.fromContact(contact.getId()), recipientDetails);
|
||||
String identifier = Hash.sha256(contact.getName() + contact.getAddr());
|
||||
Uri systemContactPhoto = TextSecurePreferences.getSystemContactPhoto(context, identifier);
|
||||
if (systemContactPhoto != null) {
|
||||
recipient.setSystemContactPhoto(systemContactPhoto);
|
||||
}
|
||||
if (contact.getId() == DcContact.DC_CONTACT_ID_SELF) {
|
||||
recipient.setProfileAvatar("SELF");
|
||||
}
|
||||
return recipient;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -229,7 +258,8 @@ public class ApplicationDcContext extends DcContext {
|
||||
|
||||
public PowerManager.WakeLock afterForgroundWakeLock = null;
|
||||
|
||||
public void startThreads()
|
||||
public final static int INTERRUPT_IDLE = 0x01; // interrupt idle if the thread is already running
|
||||
public void startThreads(int flags)
|
||||
{
|
||||
synchronized(threadsCritical) {
|
||||
|
||||
@@ -239,33 +269,36 @@ public class ApplicationDcContext extends DcContext {
|
||||
imapThreadStartedVal = false;
|
||||
}
|
||||
|
||||
imapThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// raise the starting condition
|
||||
// after acquiring a wakelock so that the process is not terminated.
|
||||
// as imapWakeLock is not reference counted that would result in a wakelock-gap is not needed here.
|
||||
imapThread = new Thread(() -> {
|
||||
// raise the starting condition
|
||||
// after acquiring a wakelock so that the process is not terminated.
|
||||
// as imapWakeLock is not reference counted that would result in a wakelock-gap is not needed here.
|
||||
imapWakeLock.acquire();
|
||||
synchronized (imapThreadStartedCond) {
|
||||
imapThreadStartedVal = true;
|
||||
imapThreadStartedCond.notifyAll();
|
||||
}
|
||||
|
||||
Log.i("DeltaChat", "###################### IMAP-Thread started. ######################");
|
||||
|
||||
|
||||
while (true) {
|
||||
imapWakeLock.acquire();
|
||||
synchronized (imapThreadStartedCond) {
|
||||
imapThreadStartedVal = true;
|
||||
imapThreadStartedCond.notifyAll();
|
||||
}
|
||||
|
||||
Log.i("DeltaChat", "###################### IMAP-Thread started. ######################");
|
||||
|
||||
|
||||
while (true) {
|
||||
imapWakeLock.acquire();
|
||||
performJobs();
|
||||
fetch();
|
||||
imapWakeLock.release();
|
||||
idle();
|
||||
}
|
||||
performImapJobs();
|
||||
performImapFetch();
|
||||
imapWakeLock.release();
|
||||
performImapIdle();
|
||||
}
|
||||
}, "imapThread");
|
||||
imapThread.setPriority(Thread.NORM_PRIORITY);
|
||||
imapThread.start();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((flags&INTERRUPT_IDLE)!=0) {
|
||||
interruptImapIdle();
|
||||
}
|
||||
}
|
||||
|
||||
if (smtpThread == null || !smtpThread.isAlive()) {
|
||||
|
||||
@@ -273,24 +306,21 @@ public class ApplicationDcContext extends DcContext {
|
||||
smtpThreadStartedVal = false;
|
||||
}
|
||||
|
||||
smtpThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
smtpThread = new Thread(() -> {
|
||||
smtpWakeLock.acquire();
|
||||
synchronized (smtpThreadStartedCond) {
|
||||
smtpThreadStartedVal = true;
|
||||
smtpThreadStartedCond.notifyAll();
|
||||
}
|
||||
|
||||
Log.i("DeltaChat", "###################### SMTP-Thread started. ######################");
|
||||
|
||||
|
||||
while (true) {
|
||||
smtpWakeLock.acquire();
|
||||
synchronized (smtpThreadStartedCond) {
|
||||
smtpThreadStartedVal = true;
|
||||
smtpThreadStartedCond.notifyAll();
|
||||
}
|
||||
|
||||
Log.i("DeltaChat", "###################### SMTP-Thread started. ######################");
|
||||
|
||||
|
||||
while (true) {
|
||||
smtpWakeLock.acquire();
|
||||
performSmtpJobs();
|
||||
smtpWakeLock.release();
|
||||
performSmtpIdle();
|
||||
}
|
||||
performSmtpJobs();
|
||||
smtpWakeLock.release();
|
||||
performSmtpIdle();
|
||||
}
|
||||
}, "smtpThread");
|
||||
smtpThread.setPriority(Thread.MAX_PRIORITY);
|
||||
@@ -320,6 +350,23 @@ public class ApplicationDcContext extends DcContext {
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************************
|
||||
* Tools
|
||||
**********************************************************************************************/
|
||||
|
||||
public boolean isNetworkConnected() {
|
||||
try {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo netInfo = cm.getActiveNetworkInfo();
|
||||
if (netInfo != null && netInfo.isConnected()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/***********************************************************************************************
|
||||
* Event Handling
|
||||
**********************************************************************************************/
|
||||
@@ -355,6 +402,35 @@ public class ApplicationDcContext extends DcContext {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleError(int event, boolean popUp, String string)
|
||||
{
|
||||
// log error
|
||||
boolean showAsToast;
|
||||
Log.e("DeltaChat", string);
|
||||
synchronized (lastErrorLock) {
|
||||
lastErrorString = string;
|
||||
showAsToast = showNextErrorAsToast;
|
||||
showNextErrorAsToast = true;
|
||||
}
|
||||
|
||||
// show error to user
|
||||
Util.runOnMain(() -> {
|
||||
if (popUp && showAsToast) {
|
||||
String toastString = string;
|
||||
|
||||
if(event==DC_EVENT_ERROR_NETWORK) {
|
||||
if(!isNetworkConnected()) {
|
||||
toastString = context.getString(R.string.error_no_network);
|
||||
}
|
||||
}
|
||||
|
||||
if (ForegroundDetector.getInstance().isForeground()) {
|
||||
Toast.makeText(context, toastString, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public long handleEvent(final int event, long data1, long data2) {
|
||||
switch(event) {
|
||||
@@ -367,23 +443,11 @@ public class ApplicationDcContext extends DcContext {
|
||||
break;
|
||||
|
||||
case DC_EVENT_ERROR:
|
||||
Log.e("DeltaChat", dataToString(data2));
|
||||
synchronized (lastErrorLock) {
|
||||
lastErrorString = dataToString(data2);
|
||||
}
|
||||
Util.runOnMain(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (lastErrorLock) {
|
||||
if (showNextErrorAsToast) {
|
||||
if (ForegroundDetector.getInstance().isForeground()) {
|
||||
Toast.makeText(context, lastErrorString, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
showNextErrorAsToast = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
handleError(event, true, dataToString(data2));
|
||||
break;
|
||||
|
||||
case DC_EVENT_ERROR_NETWORK:
|
||||
handleError(event, data1!=0, dataToString(data2));
|
||||
break;
|
||||
|
||||
case DC_EVENT_HTTP_GET:
|
||||
@@ -415,6 +479,7 @@ public class ApplicationDcContext extends DcContext {
|
||||
case DC_EVENT_GET_STRING:
|
||||
String s;
|
||||
switch( (int)data1 ) { // the integers are defined in the core and used only here, an enum or sth. like that won't have a big benefit
|
||||
case 8: s = context.getString(R.string.menu_deaddrop); break;
|
||||
case 13: s = context.getString(R.string.default_status_text); break;
|
||||
case 42: s = context.getString(R.string.autocrypt__asm_subject); break;
|
||||
case 43: s = context.getString(R.string.autocrypt__asm_general_body); break;
|
||||
@@ -426,15 +491,9 @@ public class ApplicationDcContext extends DcContext {
|
||||
{
|
||||
final Object data1obj = data1IsString(event)? dataToString(data1) : data1;
|
||||
final Object data2obj = data2IsString(event)? dataToString(data2) : data2;
|
||||
Util.runOnMain(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(eventCenter!=null) {
|
||||
eventCenter.sendToObservers(event, data1obj, data2obj);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(eventCenter!=null) {
|
||||
eventCenter.sendToObservers(event, data1obj, data2obj);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||